Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 26, 2022. It is now read-only.
/PSharpPublic archive

Commitdcd7bdb

Browse files
authored
Refactored various internal systematic testing and scheduling related APIs (#428)
1 parent8dd6e87 commitdcd7bdb

File tree

88 files changed

+1858
-2002
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+1858
-2002
lines changed

‎Docs/Testing/UnitTesting.md‎

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,24 @@ The `MachineTestKit` API provides the capability to _unit-test_ a machine of typ
44

55
We will now discuss how to use`MachineTestKit` by going through some simple examples.
66

7-
Say that you have the following machine`M`, whichhas a method`Add(int m, int k)` that takes two integers, adds them, and returnstheresult:
7+
Say that you have the following machine`M`, whichwaits to receive an event of type`E` (via the`Receive` statement) in the`InitOnEntry` handler ofthe`Start` state`Init`:
88
```C#
9+
privateclassE :Event {}
10+
911
privateclassM :Machine
1012
{
1113
[Start]
14+
[OnEntry(nameof(InitOnEntry))]
1215
privateclassInit :MachineState {}
1316

14-
internalintAdd(intm,intk)
17+
privateasyncTaskInitOnEntry()
1518
{
16-
returnm+k;
19+
awaitthis.Receive(typeof(E));
1720
}
1821
}
1922
```
2023

21-
To unit-test the above`Add` machine method, first import the`Microsoft.PSharp.TestingServices` library:
24+
To unit-test the abovelogic, first import the`Microsoft.PSharp.TestingServices` library:
2225
```C#
2326
usingMicrosoft.PSharp.TestingServices;
2427
```
@@ -33,7 +36,46 @@ public void Test()
3336

3437
When`MachineTestKit<M>` is instantiated, it creates an instance of the machine`M`, which executes in a special runtime that provides isolation. The internals of the machine (e.g. the queue) are properly initialized, as if the machine was executing in production. However, if the machine is trying to create other machines, it will get a_dummy_`MachineId`, and if the machine tries to send an event to a machine other than itself, that event will be dropped. Talking to external APIs (e.g. network or storage) might still require mocking (as is the case in regular unit-testing).
3538

36-
The`MachineTestKit<M>` instance gives you access to the machine reference through the`MachineTestKit.Machine` property, as seen below. You can use this referene to directly invoke methods of the machine, in a unit-testing fashion. We also provide`MachineTestKit.Assert`, which is a generic assertion that you can use for checking correctness, as well as several build-in assertions, e.g. for asserting state transitions, or if the inbox is empty (and more assertions can be added based on user feedback).
39+
The`MachineTestKit` provides two APIs that allow someone to asynchronously (but sequentially) interact with the machine via its inbox, and thus test how the machine transitions to different states and handles events. These two APIs are`StartMachineAsync(Event initialEvent = null)` and`SendEventAsync(Event e)`.
40+
41+
The`StartMachineAsync` method transitions the machine to its`Start` state, passes the optional specified event (`initialEvent`) and invokes its`OnEntry` handler, if there is one available. This method returns a task that completes when the machine reaches quiescence (typically when the event handler finishes executing because there are not more events to dequeue, or when the machine asynchronously waits to receive an event). This method should only be called in the beginning of the unit-test, since a machine only transitions to its`Start` state once.
42+
43+
The`SendEventAsync` method sends an event to the machine and starts its event handler. Similar to`StartMachineAsync`, this method returns a task that completes when the machine reaches quiescence (typically when the event handler finishes executing because there are not more events to dequeue, or when the machine asynchronously waits to receive an event).
44+
45+
The`MachineTestKit<M>` also provides`Assert`, which is a generic assertion that you can use for checking correctness, as well as several other specialized assertions, e.g. for asserting state transitions, or to check if the inbox is empty. We plan to add more assertions in the future.
46+
47+
The following code snippet shows how to use these APIs to test the machine`M` of the above example:
48+
```C#
49+
publicasyncTaskTest()
50+
{
51+
vartest=newMachineTestKit<M>();
52+
53+
awaittest.StartMachineAsync();
54+
test.AssertIsWaitingToReceiveEvent(true);
55+
56+
awaittest.SendEventAsync(newE());
57+
test.AssertIsWaitingToReceiveEvent(false);
58+
test.AssertInboxSize(0);
59+
}
60+
```
61+
62+
The above unit-test creates a new instance of the machine`M`, then transitions the machine to its`Start` state using`StartMachineAsync`, and then asserts that the machine is asynchronously waiting to receive an event of type`E` by invoking the`AssertIsWaitingToReceiveEvent(true)` assertion. Next, the test is sending the expected event`E` using`SendEventAsync`, and finally asserts that the machine is not waiting any event, by calling`AssertIsWaitingToReceiveEvent(false)`, and that its inbox is empty, by calling`AssertInboxSize(0)`.
63+
64+
Besides providing the capability to drive the execution of a machine via the`StartMachineAsync` and`SendEventAsync` APIs, the`MachineTestKit` also allows someone to directly call machine methods. Lets see how this can be done in a simple example. Say that you have the following machine`M`, which has a method`Add(int m, int k)` that takes two integers, adds them, and returns the result:
65+
```C#
66+
privateclassM :Machine
67+
{
68+
[Start]
69+
privateclassInit :MachineState {}
70+
71+
internalintAdd(intm,intk)
72+
{
73+
returnm+k;
74+
}
75+
}
76+
```
77+
78+
To unit-test the above`Add` machine method, the`MachineTestKit<M>` instance gives you access to the machine reference through the`MachineTestKit.Machine` property. You can then use this reference to directly invoke methods of the machine, as seen below.
3779
```C#
3880
publicvoidTest()
3981
{
@@ -43,7 +85,7 @@ public void Test()
4385
}
4486
```
4587

46-
Note that directly calling machine methods(as above)only works if these methods are declared as`public` or`internal`. This is typically not recommended for machines (and actors, in general), since the only way to interact with them should be by sending messages. However, it can be very useful to unit-test private machine methods, and for this reason the`MachineTestKit` provides the`Invoke` and`InvokeAsync` APIs, which accept the name of the method (and, optionally, parameter types for distinguishing overloaded methods), as well as the parameters to invoke the method, if any. The following example shows how to use these APIs to invoke private machine methods:
88+
Note that directly calling machine methods only works if these methods are declared as`public` or`internal`. This is typically not recommended for machines (and actors, in general), since the only way to interact with them should be by sending messages. However, it can be very useful to unit-test private machine methods, and for this reason the`MachineTestKit` provides the`Invoke` and`InvokeAsync` APIs, which accept the name of the method (and, optionally, parameter types for distinguishing overloaded methods), as well as the parameters to invoke the method, if any. The following example shows how to use these APIs to invoke private machine methods:
4789

4890
```C#
4991
privateclassM :Machine
@@ -85,41 +127,4 @@ public async Task TestAsync()
85127
}
86128
```
87129

88-
Besides allowing the user to directly call machine methods, the`MachineTestKit` also provides access to two APIs that allow the user to asynchronously (but sequentially) interact with the machine via its inbox, and thus test how the machine transitions to different states and handles events. These two APIs are`StartMachineAsync(Event initialEvent = null)` and`SendEventAsync(Event e)`.
89-
90-
The`StartMachineAsync` method transitions the machine to its`Start` state, passes the optional specified event (`initialEvent`) and invokes its`OnEntry` handler, if there is one available. This method returns a task that completes when the machine reaches quiescence (typically when the event handler finishes executing because there are not more events to dequeue, or when the machine asynchronously waits to receive an event). This method should only be called in the beginning of the unit-test, since a machine only transitions to its`Start` state once.
91-
92-
The`SendEventAsync` method sends an event to the machine and starts its event handler. Similar to`StartMachineAsync`, this method returns a task that completes when the machine reaches quiescence (typically when the event handler finishes executing because there are not more events to dequeue, or when the machine asynchronously waits to receive an event).
93-
94-
An example of using these two methods is the following:
95-
```C#
96-
privateclassE :Event {}
97-
98-
privateclassM :Machine
99-
{
100-
[Start]
101-
[OnEntry(nameof(InitOnEntry))]
102-
privateclassInit :MachineState {}
103-
104-
privateasyncTaskInitOnEntry()
105-
{
106-
awaitthis.Receive(typeof(E));
107-
}
108-
}
109-
110-
publicasyncTaskTest()
111-
{
112-
vartest=newMachineTestKit<M>();
113-
114-
awaittest.StartMachineAsync();
115-
test.AssertIsWaitingToReceiveEvent(true);
116-
117-
awaittest.SendEventAsync(newE());
118-
test.AssertIsWaitingToReceiveEvent(false);
119-
test.AssertInboxSize(0);
120-
}
121-
```
122-
123-
The above unit-test, creates a new instance of the machine`M`, then transitions the machine to its`Start` state using`StartMachineAsync`, and then asserts that the machine is asynchronously waiting to receive an event of type`E` (via the`Receive` statement) by invoking the`AssertIsWaitingToReceiveEvent(true)` assertion. Next, the test is sending the expected event`E` using`SendEventAsync`, and finally asserts that the machine is not waiting any event, by calling`AssertIsWaitingToReceiveEvent(false)`, and that its inbox is empty, by calling`AssertInboxSize(0)`.
124-
125-
Note that its possible to use both the`StartMachineAsync` and`SendEventAsync`, as well as invoke directly methods by accessing the`MachineTestKit.Machine` property, based on the testing scenario.
130+
Note that its possible to use both the`StartMachineAsync` and`SendEventAsync`, as well as invoke directly machine methods by accessing the`MachineTestKit.Machine` property, based on the testing scenario.

‎PSharp.sln‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedObjects", "Source\Sha
5353
EndProject
5454
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") ="SharedObjects.Tests","Tests\SharedObjects.Tests\SharedObjects.Tests.csproj","{4240DE34-A775-4B3E-A67A-37E50AC9850B}"
5555
EndProject
56-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") ="SchedulingStrategies","Source\SchedulingStrategies\SchedulingStrategies.csproj","{2A3D4399-787C-4F97-8591-6A8021775AE0}"
57-
EndProject
5856
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") ="Tests.Common","Tests\Tests.Common\Tests.Common.csproj","{61FC86A6-AF87-4007-B184-AF860A57AB9E}"
5957
EndProject
6058
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") ="Benchmarking","Benchmarking","{80164985-D9AE-495B-9D01-BA86AE137739}"
@@ -135,10 +133,6 @@ Global
135133
{4240DE34-A775-4B3E-A67A-37E50AC9850B}.Debug|Any CPU.Build.0=Debug|Any CPU
136134
{4240DE34-A775-4B3E-A67A-37E50AC9850B}.Release|Any CPU.ActiveCfg=Release|Any CPU
137135
{4240DE34-A775-4B3E-A67A-37E50AC9850B}.Release|Any CPU.Build.0=Release|Any CPU
138-
{2A3D4399-787C-4F97-8591-6A8021775AE0}.Debug|Any CPU.ActiveCfg=Debug|Any CPU
139-
{2A3D4399-787C-4F97-8591-6A8021775AE0}.Debug|Any CPU.Build.0=Debug|Any CPU
140-
{2A3D4399-787C-4F97-8591-6A8021775AE0}.Release|Any CPU.ActiveCfg=Release|Any CPU
141-
{2A3D4399-787C-4F97-8591-6A8021775AE0}.Release|Any CPU.Build.0=Release|Any CPU
142136
{61FC86A6-AF87-4007-B184-AF860A57AB9E}.Debug|Any CPU.ActiveCfg=Debug|Any CPU
143137
{61FC86A6-AF87-4007-B184-AF860A57AB9E}.Debug|Any CPU.Build.0=Debug|Any CPU
144138
{61FC86A6-AF87-4007-B184-AF860A57AB9E}.Release|Any CPU.ActiveCfg=Release|Any CPU
@@ -178,7 +172,6 @@ Global
178172
{DABC68C1-79D3-4324-A750-7CF72E0A0ACF} ={2012300C-6E5D-47A0-9D57-B3F0A73AA1D4}
179173
{B1A04084-448F-4765-9068-C8C7D5F0C6C6} ={83369B7E-5C21-4D49-A14C-E8A6A4892807}
180174
{4240DE34-A775-4B3E-A67A-37E50AC9850B} ={2012300C-6E5D-47A0-9D57-B3F0A73AA1D4}
181-
{2A3D4399-787C-4F97-8591-6A8021775AE0} ={83369B7E-5C21-4D49-A14C-E8A6A4892807}
182175
{61FC86A6-AF87-4007-B184-AF860A57AB9E} ={2012300C-6E5D-47A0-9D57-B3F0A73AA1D4}
183176
{80164985-D9AE-495B-9D01-BA86AE137739} ={9BC0914F-3068-4148-B778-4CA27D828D39}
184177
{B27EFAA7-9166-4CC6-94AF-214A69DC5794} ={80164985-D9AE-495B-9D01-BA86AE137739}

‎Source/Core/IO/Debugging/Debug.cs‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,54 @@ public static class Debug
1818
/// </summary>
1919
internalstaticboolIsEnabled=false;
2020

21+
/// <summary>
22+
/// Writes the debugging information, followed by the current line terminator,
23+
/// to the output stream. The print occurs only if debugging is enabled.
24+
/// </summary>
25+
publicstaticvoidWrite(stringformat,objectarg0)
26+
{
27+
if(IsEnabled)
28+
{
29+
Console.Write(string.Format(CultureInfo.InvariantCulture,format,arg0));
30+
}
31+
}
32+
33+
/// <summary>
34+
/// Writes the debugging information, followed by the current line terminator,
35+
/// to the output stream. The print occurs only if debugging is enabled.
36+
/// </summary>
37+
publicstaticvoidWrite(stringformat,objectarg0,objectarg1)
38+
{
39+
if(IsEnabled)
40+
{
41+
Console.Write(string.Format(CultureInfo.InvariantCulture,format,arg0,arg1));
42+
}
43+
}
44+
45+
/// <summary>
46+
/// Writes the debugging information, followed by the current line terminator,
47+
/// to the output stream. The print occurs only if debugging is enabled.
48+
/// </summary>
49+
publicstaticvoidWrite(stringformat,objectarg0,objectarg1,objectarg2)
50+
{
51+
if(IsEnabled)
52+
{
53+
Console.Write(string.Format(CultureInfo.InvariantCulture,format,arg0,arg1,arg2));
54+
}
55+
}
56+
57+
/// <summary>
58+
/// Writes the debugging information, followed by the current line terminator,
59+
/// to the output stream. The print occurs only if debugging is enabled.
60+
/// </summary>
61+
publicstaticvoidWrite(stringformat,paramsobject[]args)
62+
{
63+
if(IsEnabled)
64+
{
65+
Console.Write(string.Format(CultureInfo.InvariantCulture,format,args));
66+
}
67+
}
68+
2169
/// <summary>
2270
/// Writes the debugging information, followed by the current line terminator,
2371
/// to the output stream. The print occurs only if debugging is enabled.

‎Source/Core/Properties/codeanalysis.ruleset‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
<Rule Id="SA1004" Action="Error" />
6868
<Rule Id="SA1005" Action="Error" />
6969
<Rule Id="SA1008" Action="None" />
70-
<Rule Id="SA1009" Action="Error" />
70+
<Rule Id="SA1009" Action="None" />
7171
<Rule Id="SA1013" Action="Error" />
7272
<Rule Id="SA1024" Action="Error" />
7373
<Rule Id="SA1027" Action="Error" />

‎Source/Core/Runtime/BaseRuntime.cs‎

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ internal abstract class BaseRuntime : IMachineRuntime
5656
/// </summary>
5757
publiceventOnEventDroppedHandlerOnEventDropped;
5858

59-
/// <summary>
60-
/// Checks if an <see cref="OnEventDropped"/> handler has been installed by the user.
61-
/// </summary>
62-
internalboolIsOnEventDroppedHandlerInstalled=>this.OnEventDropped!=null;
63-
6459
/// <summary>
6560
/// Initializes a new instance of the <see cref="BaseRuntime"/> class.
6661
/// </summary>
@@ -397,6 +392,42 @@ internal virtual void NotifyCompletedAction(Machine machine, MethodInfo action,
397392
// Override to implement the notification.
398393
}
399394

395+
/// <summary>
396+
/// Notifies that a machine invoked an action.
397+
/// </summary>
398+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
399+
internalvirtualvoidNotifyInvokedOnEntryAction(Machinemachine,MethodInfoaction,EventreceivedEvent)
400+
{
401+
// Override to implement the notification.
402+
}
403+
404+
/// <summary>
405+
/// Notifies that a machine completed invoking an action.
406+
/// </summary>
407+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
408+
internalvirtualvoidNotifyCompletedOnEntryAction(Machinemachine,MethodInfoaction,EventreceivedEvent)
409+
{
410+
// Override to implement the notification.
411+
}
412+
413+
/// <summary>
414+
/// Notifies that a machine invoked an action.
415+
/// </summary>
416+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
417+
internalvirtualvoidNotifyInvokedOnExitAction(Machinemachine,MethodInfoaction,EventreceivedEvent)
418+
{
419+
// Override to implement the notification.
420+
}
421+
422+
/// <summary>
423+
/// Notifies that a machine completed invoking an action.
424+
/// </summary>
425+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
426+
internalvirtualvoidNotifyCompletedOnExitAction(Machinemachine,MethodInfoaction,EventreceivedEvent)
427+
{
428+
// Override to implement the notification.
429+
}
430+
400431
/// <summary>
401432
/// Notifies that a monitor invoked an action.
402433
/// </summary>
@@ -463,13 +494,14 @@ internal virtual void NotifyHandleRaisedEvent(Machine machine, Event e)
463494
/// <summary>
464495
/// Notifies that a machine is waiting to receive an event of one of the specified types.
465496
/// </summary>
497+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
466498
internalvirtualvoidNotifyWaitEvent(Machinemachine,IEnumerable<Type>eventTypes)
467499
{
468500
// Override to implement the notification.
469501
}
470502

471503
/// <summary>
472-
/// Notifies that a machinereceived an<see cref="Event"/>that it was waitingfor.
504+
/// Notifies that a machineenqueued aneventthat it was waitingto receive.
473505
/// </summary>
474506
[MethodImpl(MethodImplOptions.AggressiveInlining)]
475507
internalvirtualvoidNotifyReceivedEvent(Machinemachine,Evente,EventInfoeventInfo)
@@ -538,15 +570,15 @@ internal static void SetOperationGroupIdForMachine(Machine created, BaseMachine
538570
{
539571
if(operationGroupId.HasValue)
540572
{
541-
created.Info.OperationGroupId=operationGroupId.Value;
573+
created.OperationGroupId=operationGroupId.Value;
542574
}
543575
elseif(sender!=null)
544576
{
545-
created.Info.OperationGroupId=sender.Info.OperationGroupId;
577+
created.OperationGroupId=sender.OperationGroupId;
546578
}
547579
else
548580
{
549-
created.Info.OperationGroupId=Guid.Empty;
581+
created.OperationGroupId=Guid.Empty;
550582
}
551583
}
552584

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp