Skip to content

Commit 5c8c916

Browse files
author
Rakeshwar Reddy Kambaiahgari
committed
Added tests
1 parent baa054f commit 5c8c916

6 files changed

Lines changed: 346 additions & 43 deletions

File tree

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
namespace VirtualClient
5+
{
6+
using System;
7+
using System.Diagnostics;
8+
using NUnit.Framework;
9+
using VirtualClient.Common;
10+
11+
[TestFixture]
12+
[Category("Unit")]
13+
internal class FixtureTrackingTests
14+
{
15+
private FixtureTracking tracking;
16+
17+
[SetUp]
18+
public void SetupTest()
19+
{
20+
this.tracking = new FixtureTracking();
21+
}
22+
23+
[Test]
24+
public void AssertCommandsExecutedThrowsWhenNoCommandsWereExecuted()
25+
{
26+
Assert.Throws<InvalidOperationException>(() => this.tracking.AssertCommandsExecuted("any-command"));
27+
}
28+
29+
[Test]
30+
public void AssertCommandsExecutedThrowsWhenExpectedCommandWasNotExecuted()
31+
{
32+
this.AddCommand("command1 --arg1");
33+
34+
Assert.Throws<InvalidOperationException>(() => this.tracking.AssertCommandsExecuted("command2"));
35+
}
36+
37+
[Test]
38+
public void AssertCommandsExecutedDoesNotThrowWhenExpectedCommandWasExecuted()
39+
{
40+
this.AddCommand("command1 --arg1");
41+
42+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecuted("command1 --arg1"));
43+
}
44+
45+
[Test]
46+
public void AssertCommandsExecutedMatchesUsingRegularExpressions()
47+
{
48+
this.AddCommand("/home/user/bin/workload --threads 4 --size 128");
49+
50+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecuted("workload.*--threads 4"));
51+
}
52+
53+
[Test]
54+
public void AssertCommandsExecutedMatchesRegardlessOfOrder()
55+
{
56+
this.AddCommand("command1");
57+
this.AddCommand("command2");
58+
this.AddCommand("command3");
59+
60+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecuted("command3", "command1"));
61+
}
62+
63+
[Test]
64+
public void AssertCommandsExecutedMatchesAreCaseInsensitive()
65+
{
66+
this.AddCommand("SomeCommand --Arg1");
67+
68+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecuted("somecommand --arg1"));
69+
}
70+
71+
[Test]
72+
public void AssertCommandsExecutedFallsBackToExactMatchWhenRegexIsInvalid()
73+
{
74+
// The '[' is an invalid regex pattern by itself.
75+
this.AddCommand("[invalid-regex");
76+
77+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecuted("[invalid-regex"));
78+
}
79+
80+
[Test]
81+
public void AssertCommandsExecutedDoesNotMatchTheSameCommandTwice()
82+
{
83+
this.AddCommand("command1");
84+
85+
Assert.Throws<InvalidOperationException>(() => this.tracking.AssertCommandsExecuted("command1", "command1"));
86+
}
87+
88+
[Test]
89+
public void AssertCommandsExecutedInOrderThrowsWhenNoCommandsWereExecuted()
90+
{
91+
Assert.Throws<InvalidOperationException>(() => this.tracking.AssertCommandsExecutedInOrder("any-command"));
92+
}
93+
94+
[Test]
95+
public void AssertCommandsExecutedInOrderThrowsWhenCommandsAreOutOfOrder()
96+
{
97+
this.AddCommand("command1");
98+
this.AddCommand("command2");
99+
100+
Assert.Throws<InvalidOperationException>(() => this.tracking.AssertCommandsExecutedInOrder("command2", "command1"));
101+
}
102+
103+
[Test]
104+
public void AssertCommandsExecutedInOrderDoesNotThrowWhenCommandsAreInOrder()
105+
{
106+
this.AddCommand("command1");
107+
this.AddCommand("command2");
108+
this.AddCommand("command3");
109+
110+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecutedInOrder("command1", "command3"));
111+
}
112+
113+
[Test]
114+
public void AssertCommandsExecutedInOrderMatchesUsingRegularExpressions()
115+
{
116+
this.AddCommand("sudo chmod +x /home/user/workload");
117+
this.AddCommand("sudo /home/user/workload --port 6379");
118+
119+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecutedInOrder(
120+
"chmod.*workload",
121+
"workload.*--port 6379"));
122+
}
123+
124+
[Test]
125+
public void AssertCommandsExecutedInOrderFallsBackToExactMatchWhenRegexIsInvalid()
126+
{
127+
this.AddCommand("[first");
128+
this.AddCommand("[second");
129+
130+
Assert.DoesNotThrow(() => this.tracking.AssertCommandsExecutedInOrder("[first", "[second"));
131+
}
132+
133+
[Test]
134+
public void AssertCommandExecutedTimesThrowsWhenCountDoesNotMatch()
135+
{
136+
this.AddCommand("command1");
137+
138+
Assert.Throws<InvalidOperationException>(() => this.tracking.AssertCommandExecutedTimes("command1", 2));
139+
}
140+
141+
[Test]
142+
public void AssertCommandExecutedTimesDoesNotThrowWhenCountMatches()
143+
{
144+
this.AddCommand("command1 --port 6379");
145+
this.AddCommand("command1 --port 6380");
146+
147+
Assert.DoesNotThrow(() => this.tracking.AssertCommandExecutedTimes("command1", 2));
148+
}
149+
150+
[Test]
151+
public void AssertCommandExecutedTimesHandlesZeroExpectedExecutions()
152+
{
153+
this.AddCommand("command1");
154+
155+
Assert.DoesNotThrow(() => this.tracking.AssertCommandExecutedTimes("command2", 0));
156+
}
157+
158+
[Test]
159+
public void AssertCommandExecutedTimesMatchesUsingRegularExpressions()
160+
{
161+
this.AddCommand("numactl -C 0 redis-server --port 6379");
162+
this.AddCommand("numactl -C 1 redis-server --port 6380");
163+
this.AddCommand("chmod +x redis-server");
164+
165+
Assert.DoesNotThrow(() => this.tracking.AssertCommandExecutedTimes("numactl.*redis-server", 2));
166+
}
167+
168+
[Test]
169+
public void AssertCommandExecutedTimesFallsBackToSubstringMatchWhenRegexIsInvalid()
170+
{
171+
this.AddCommand("run [test");
172+
this.AddCommand("run [test");
173+
174+
Assert.DoesNotThrow(() => this.tracking.AssertCommandExecutedTimes("[test", 2));
175+
}
176+
177+
[Test]
178+
public void ClearRemovesAllTrackedCommands()
179+
{
180+
this.AddCommand("command1");
181+
this.AddCommand("command2");
182+
183+
this.tracking.Clear();
184+
185+
Assert.AreEqual(0, this.tracking.Commands.Count);
186+
Assert.Throws<InvalidOperationException>(() => this.tracking.AssertCommandsExecuted("command1"));
187+
}
188+
189+
[Test]
190+
public void CommandsPropertyReturnsAllTrackedCommandsInOrder()
191+
{
192+
this.AddCommand("first");
193+
this.AddCommand("second");
194+
this.AddCommand("third");
195+
196+
Assert.AreEqual(3, this.tracking.Commands.Count);
197+
Assert.AreEqual("first", this.tracking.Commands[0].FullCommand);
198+
Assert.AreEqual("second", this.tracking.Commands[1].FullCommand);
199+
Assert.AreEqual("third", this.tracking.Commands[2].FullCommand);
200+
}
201+
202+
[Test]
203+
public void GetDetailedSummaryReturnsFormattedSummaryOfTrackedCommands()
204+
{
205+
this.AddCommand("command1 --arg1");
206+
207+
string summary = this.tracking.GetDetailedSummary();
208+
209+
Assert.IsNotNull(summary);
210+
StringAssert.Contains("Total Commands Executed: 1", summary);
211+
StringAssert.Contains("command1 --arg1", summary);
212+
}
213+
214+
[Test]
215+
public void GetDetailedSummaryHandlesNoCommands()
216+
{
217+
string summary = this.tracking.GetDetailedSummary();
218+
219+
Assert.IsNotNull(summary);
220+
StringAssert.Contains("Total Commands Executed: 0", summary);
221+
}
222+
223+
[Test]
224+
public void ErrorMessageIncludesActualCommandsExecuted()
225+
{
226+
this.AddCommand("actual-command --flag");
227+
228+
InvalidOperationException error = Assert.Throws<InvalidOperationException>(
229+
() => this.tracking.AssertCommandsExecuted("missing-command"));
230+
231+
StringAssert.Contains("actual-command --flag", error.Message);
232+
StringAssert.Contains("Missing Commands:", error.Message);
233+
}
234+
235+
[Test]
236+
public void ErrorMessageForOrderedAssertionIncludesExpectedAndActualOrder()
237+
{
238+
this.AddCommand("command2");
239+
this.AddCommand("command1");
240+
241+
InvalidOperationException error = Assert.Throws<InvalidOperationException>(
242+
() => this.tracking.AssertCommandsExecutedInOrder("command1", "command2"));
243+
244+
StringAssert.Contains("Expected Order:", error.Message);
245+
StringAssert.Contains("Actual Execution Order:", error.Message);
246+
}
247+
248+
[Test]
249+
public void ErrorMessageForCountMismatchIncludesExpectedAndActualCounts()
250+
{
251+
this.AddCommand("command1");
252+
253+
InvalidOperationException error = Assert.Throws<InvalidOperationException>(
254+
() => this.tracking.AssertCommandExecutedTimes("command1", 3));
255+
256+
StringAssert.Contains("Expected: 3 execution(s)", error.Message);
257+
StringAssert.Contains("Actual: 1 execution(s)", error.Message);
258+
}
259+
260+
private void AddCommand(string fullCommand)
261+
{
262+
// Split on first space to separate command from arguments, matching how
263+
// InMemoryProcessManager records executions.
264+
string command = fullCommand;
265+
string arguments = null;
266+
267+
int spaceIndex = fullCommand.IndexOf(' ');
268+
if (spaceIndex >= 0)
269+
{
270+
command = fullCommand.Substring(0, spaceIndex);
271+
arguments = fullCommand.Substring(spaceIndex + 1);
272+
}
273+
274+
InMemoryProcess process = new InMemoryProcess
275+
{
276+
StartInfo = new ProcessStartInfo
277+
{
278+
FileName = command,
279+
Arguments = arguments
280+
},
281+
OnHasExited = () => true,
282+
OnStart = () => true
283+
};
284+
285+
this.tracking.AddCommand(new CommandExecutionInfo(
286+
command,
287+
arguments,
288+
null,
289+
process,
290+
DateTime.UtcNow));
291+
}
292+
}
293+
}

src/VirtualClient/VirtualClient.TestFramework/CommandExecutionInfo.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace VirtualClient
77
using VirtualClient.Common;
88

99
/// <summary>
10-
/// Represents information about a command execution captured during testing.
10+
/// Represents information about a command/process execution captured during testing.
1111
/// </summary>
1212
public class CommandExecutionInfo
1313
{
@@ -16,9 +16,9 @@ public class CommandExecutionInfo
1616
/// </summary>
1717
/// <param name="command">The command/executable that was run.</param>
1818
/// <param name="arguments">The command-line arguments.</param>
19-
/// <param name="workingDirectory">The working directory where the command was executed.</param>
20-
/// <param name="process">Reference to the process proxy.</param>
21-
/// <param name="executionTime">When the command was executed.</param>
19+
/// <param name="workingDirectory">The working directory for the command execution.</param>
20+
/// <param name="process">The process proxy associated with the execution.</param>
21+
/// <param name="executionTime">The time at which the command was executed.</param>
2222
public CommandExecutionInfo(
2323
string command,
2424
string arguments,
@@ -44,7 +44,7 @@ public CommandExecutionInfo(
4444
public string Arguments { get; }
4545

4646
/// <summary>
47-
/// The working directory where the command was executed.
47+
/// The working directory for the command execution.
4848
/// </summary>
4949
public string WorkingDirectory { get; }
5050

@@ -56,12 +56,12 @@ public CommandExecutionInfo(
5656
: $"{this.Command} {this.Arguments}";
5757

5858
/// <summary>
59-
/// Reference to the process proxy.
59+
/// The process proxy associated with the execution.
6060
/// </summary>
6161
public IProcessProxy Process { get; }
6262

6363
/// <summary>
64-
/// When the command was executed.
64+
/// The time at which the command was executed.
6565
/// </summary>
6666
public DateTime ExecutionTime { get; }
6767

src/VirtualClient/VirtualClient.TestFramework/DependencyFixture.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public string PlatformArchitectureName
203203
public ProfileTiming Timing { get; set; }
204204

205205
/// <summary>
206-
/// Gets the process tracking instance for assertions. Populated after <see cref="TrackProcesses"/> is called.
206+
/// Gets the process tracking instance. This is populated after the <see cref="TrackProcesses"/> method is called.
207207
/// </summary>
208208
public FixtureTracking Tracking => this.ProcessManager.Tracking;
209209

@@ -413,7 +413,7 @@ public DependencyPath ToPlatformSpecificPath(DependencyPath dependency, Platform
413413
/// <summary>
414414
/// Enables automatic tracking of all process executions.
415415
/// </summary>
416-
/// <param name="reset">If true, clears any previously tracked commands.</param>
416+
/// <param name="reset">True to clear any previously tracked commands.</param>
417417
/// <returns>The fixture instance for method chaining.</returns>
418418
public DependencyFixture TrackProcesses(bool reset = true)
419419
{
@@ -422,12 +422,13 @@ public DependencyFixture TrackProcesses(bool reset = true)
422422
}
423423

424424
/// <summary>
425-
/// Sets up automatic output for processes matching a command pattern.
425+
/// Sets up automatic output for processes whose full command line matches
426+
/// the pattern provided.
426427
/// </summary>
427-
/// <param name="commandPattern">Regex pattern matching the command.</param>
428-
/// <param name="standardOutput">Output to return for matching commands.</param>
429-
/// <param name="standardError">Error output (optional).</param>
430-
/// <param name="exitCode">Exit code for the process (default: 0).</param>
428+
/// <param name="commandPattern">A regex pattern matching the command.</param>
429+
/// <param name="standardOutput">The standard output to return for matching commands.</param>
430+
/// <param name="standardError">The standard error output (optional).</param>
431+
/// <param name="exitCode">The exit code for the process (default: 0).</param>
431432
/// <returns>The fixture instance for method chaining.</returns>
432433
public DependencyFixture SetupProcessOutput(
433434
string commandPattern,

0 commit comments

Comments
 (0)