Skip to content

Commit 29792f9

Browse files
committed
reverting changes back to commit that worked
1 parent 0b1429b commit 29792f9

32 files changed

+966
-16
lines changed

Azure.Functions.Cli.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Functions.Cli.Abstrac
2121
EndProject
2222
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreToolsHost", "src\CoreToolsHost\CoreToolsHost.csproj", "{0333D5B6-B628-4605-A51E-D0AEE4C3F1FC}"
2323
EndProject
24-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Functions.Cli.Test.Framework", "test\Cli\Test.Framework\Azure.Functions.Cli.Test.Framework.csproj", "{50E27986-E943-4A5D-8771-AE97C531B077}"
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Functions.Cli.TestFramework", "test\Cli\Test.Framework\Azure.Functions.Cli.TestFramework.csproj", "{50E27986-E943-4A5D-8771-AE97C531B077}"
2525
EndProject
2626
Global
2727
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
// Based off of: https://github.com/dotnet/sdk/blob/e793aa4709d28cd783712df40413448250e26fea/test/Microsoft.NET.TestFramework/Assertions/CommandResultAssertions.cs
5+
using FluentAssertions;
6+
using FluentAssertions.Execution;
7+
8+
namespace Func.TestFramework.Assertions
9+
{
10+
public class CommandResultAssertions
11+
{
12+
private CommandResult _commandResult;
13+
14+
public CommandResultAssertions(CommandResult commandResult)
15+
{
16+
_commandResult = commandResult;
17+
}
18+
19+
public CommandResultAssertions ExitWith(int expectedExitCode)
20+
{
21+
Execute.Assertion.ForCondition(_commandResult.ExitCode == expectedExitCode)
22+
.FailWith($"Expected command to exit with {expectedExitCode} but it did not. Error message: {_commandResult.StdErr}");
23+
return this;
24+
}
25+
26+
public AndConstraint<CommandResultAssertions> HaveStdOutContaining(string pattern)
27+
{
28+
Execute.Assertion.ForCondition(_commandResult.StdOut is not null && _commandResult.StdOut.Contains(pattern))
29+
.FailWith($"The command output did not contain expected result: {pattern}{Environment.NewLine}");
30+
return new AndConstraint<CommandResultAssertions>(this);
31+
}
32+
33+
public AndConstraint<CommandResultAssertions> HaveStdErrContaining(string pattern)
34+
{
35+
Execute.Assertion.ForCondition(_commandResult.StdErr is not null && _commandResult.StdErr.Contains(pattern))
36+
.FailWith($"The command output did not contain expected result: {pattern}{Environment.NewLine}");
37+
return new AndConstraint<CommandResultAssertions>(this);
38+
}
39+
40+
public AndConstraint<CommandResultAssertions> NotHaveStdOutContaining(string pattern)
41+
{
42+
Execute.Assertion.ForCondition(_commandResult.StdOut is not null && !_commandResult.StdOut.Contains(pattern))
43+
.FailWith($"The command output did contain expected result: {pattern}{Environment.NewLine}");
44+
return new AndConstraint<CommandResultAssertions>(this);
45+
}
46+
}
47+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
// Copied from: https://github.com/dotnet/sdk/blob/e793aa4709d28cd783712df40413448250e26fea/test/Microsoft.NET.TestFramework/Assertions/CommandResultExtensions.cs
5+
namespace Func.TestFramework.Assertions
6+
{
7+
public static class CommandResultExtensions
8+
{
9+
public static CommandResultAssertions Should(this CommandResult commandResult) => new CommandResultAssertions(commandResult);
10+
}
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectCapability Remove="TestContainer" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Azure.Storage.Queues" />
14+
<PackageReference Include="xunit" />
15+
<PackageReference Include="FluentAssertions"/>
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<ProjectReference Include="$(RepoSrcRoot)Cli\Abstractions\Azure.Functions.Cli.Abstractions.csproj" />
20+
</ItemGroup>
21+
22+
</Project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
// Based off of: https://github.com/dotnet/sdk/blob/e793aa4709d28cd783712df40413448250e26fea/test/Microsoft.NET.TestFramework/Commands/SdkCommandSpec.cs
5+
using System.Diagnostics;
6+
using Azure.Functions.Cli.Abstractions;
7+
8+
namespace Func.TestFramework
9+
{
10+
public class CommandInfo
11+
{
12+
public required string FileName { get; set; }
13+
14+
public List<string> Arguments { get; set; } = new List<string>();
15+
16+
public Dictionary<string, string> Environment { get; set; } = new Dictionary<string, string>();
17+
18+
public List<string> EnvironmentToRemove { get; } = new List<string>();
19+
20+
public required string WorkingDirectory { get; set; }
21+
22+
public string? TestName { get; set; }
23+
24+
public Command ToCommand(bool doNotEscapeArguments = false)
25+
{
26+
var process = new Process()
27+
{
28+
StartInfo = ToProcessStartInfo()
29+
};
30+
31+
return new Command(process, trimTrailingNewlines: true);
32+
}
33+
34+
public ProcessStartInfo ToProcessStartInfo()
35+
{
36+
var psi = new ProcessStartInfo
37+
{
38+
FileName = FileName,
39+
Arguments = string.Join(" ", Arguments),
40+
UseShellExecute = false
41+
};
42+
43+
foreach (var kvp in Environment)
44+
{
45+
psi.Environment[kvp.Key] = kvp.Value;
46+
}
47+
48+
foreach (var envToRemove in EnvironmentToRemove)
49+
{
50+
psi.Environment.Remove(envToRemove);
51+
}
52+
53+
if (WorkingDirectory is not null)
54+
{
55+
psi.WorkingDirectory = WorkingDirectory;
56+
}
57+
58+
return psi;
59+
}
60+
}
61+
}
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
// Based off of: https://github.com/dotnet/sdk/blob/e793aa4709d28cd783712df40413448250e26fea/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs
5+
6+
using Azure.Functions.Cli.Abstractions;
7+
using System.Diagnostics;
8+
using Xunit.Abstractions;
9+
10+
namespace Func.TestFramework.Commands
11+
{
12+
public abstract class FuncCommand
13+
{
14+
private Dictionary<string, string> _environment = new Dictionary<string, string>();
15+
16+
public ITestOutputHelper Log { get; }
17+
18+
public string WorkingDirectory { get; set; }
19+
20+
public List<string> Arguments { get; set; } = new List<string>();
21+
22+
public List<string> EnvironmentToRemove { get; } = new List<string>();
23+
24+
// These only work via Execute(), not when using GetProcessStartInfo()
25+
public Action<string>? CommandOutputHandler { get; set; }
26+
27+
public Func<Process, Task>? ProcessStartedHandler { get; set; }
28+
29+
public StreamWriter? FileWriter { get; private set; } = null;
30+
31+
public string LogFilePath { get; private set; }
32+
33+
protected FuncCommand(ITestOutputHelper log)
34+
{
35+
Log = log;
36+
}
37+
38+
protected abstract CommandInfo CreateCommand(IEnumerable<string> args);
39+
40+
public FuncCommand WithEnvironmentVariable(string name, string value)
41+
{
42+
_environment[name] = value;
43+
return this;
44+
}
45+
46+
public FuncCommand WithWorkingDirectory(string workingDirectory)
47+
{
48+
WorkingDirectory = workingDirectory;
49+
return this;
50+
}
51+
52+
private CommandInfo CreateCommandInfo(IEnumerable<string> args)
53+
{
54+
var commandInfo = CreateCommand(args);
55+
foreach (var kvp in _environment)
56+
{
57+
commandInfo.Environment[kvp.Key] = kvp.Value;
58+
}
59+
60+
foreach (var envToRemove in EnvironmentToRemove)
61+
{
62+
commandInfo.EnvironmentToRemove.Add(envToRemove);
63+
}
64+
65+
if (WorkingDirectory is not null)
66+
{
67+
commandInfo.WorkingDirectory = WorkingDirectory;
68+
}
69+
70+
if (Arguments.Any())
71+
{
72+
commandInfo.Arguments = Arguments.Concat(commandInfo.Arguments).ToList();
73+
}
74+
75+
return commandInfo;
76+
}
77+
78+
public ProcessStartInfo GetProcessStartInfo(params string[] args)
79+
{
80+
var commandSpec = CreateCommandInfo(args);
81+
return commandSpec.ToProcessStartInfo();
82+
}
83+
84+
public virtual CommandResult Execute(IEnumerable<string> args)
85+
{
86+
var spec = CreateCommandInfo(args);
87+
var command = spec
88+
.ToCommand()
89+
.CaptureStdOut()
90+
.CaptureStdErr();
91+
92+
var funcExeDirectory = Path.GetDirectoryName(spec.FileName);
93+
94+
Directory.SetCurrentDirectory(funcExeDirectory);
95+
96+
var directoryToLogTo = Environment.GetEnvironmentVariable("DirectoryToLogTo");
97+
if (string.IsNullOrEmpty(directoryToLogTo))
98+
{
99+
directoryToLogTo = Directory.GetCurrentDirectory();
100+
}
101+
102+
// Ensure directory exists
103+
Directory.CreateDirectory(directoryToLogTo);
104+
105+
// Create a more unique filename to avoid conflicts
106+
string uniqueId = Guid.NewGuid().ToString("N").Substring(0, 8);
107+
LogFilePath = Path.Combine(directoryToLogTo,
108+
$"func_{spec.Arguments.First()}_{spec.TestName}_{DateTime.Now:yyyyMMdd_HHmmss}_{uniqueId}.log");
109+
110+
// Make sure we're only opening the file once
111+
try
112+
{
113+
// Open with FileShare.Read to allow others to read but not write
114+
var fileStream = new FileStream(LogFilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
115+
FileWriter = new StreamWriter(fileStream)
116+
{
117+
AutoFlush = true
118+
};
119+
120+
// Write initial information
121+
FileWriter.WriteLine($"=== Test started at {DateTime.Now} ===");
122+
FileWriter.WriteLine($"Test Name: {spec.TestName}");
123+
var display = $"func {string.Join(" ", spec.Arguments)}";
124+
FileWriter.WriteLine($"Command: {display}");
125+
FileWriter.WriteLine($"Working Directory: {spec.WorkingDirectory ?? "not specified"}");
126+
FileWriter.WriteLine("====================================");
127+
128+
command.OnOutputLine(line =>
129+
{
130+
try
131+
{
132+
// Write to the file if it's still open
133+
if (FileWriter is not null && FileWriter.BaseStream is not null)
134+
{
135+
FileWriter.WriteLine($"[STDOUT] {line}");
136+
FileWriter.Flush();
137+
}
138+
139+
Log.WriteLine($"》 {line}");
140+
CommandOutputHandler?.Invoke(line);
141+
}
142+
catch (Exception ex)
143+
{
144+
Log.WriteLine($"Error writing to log file: {ex.Message}");
145+
}
146+
});
147+
148+
command.OnErrorLine(line =>
149+
{
150+
try
151+
{
152+
// Write to the file if it's still open
153+
if (FileWriter is not null && FileWriter.BaseStream is not null)
154+
{
155+
FileWriter.WriteLine($"[STDERR] {line}");
156+
FileWriter.Flush();
157+
}
158+
159+
if (!string.IsNullOrEmpty(line))
160+
{
161+
Log.WriteLine($"❌ {line}");
162+
}
163+
}
164+
catch (Exception ex)
165+
{
166+
Log.WriteLine($"Error writing to log file: {ex.Message}");
167+
}
168+
});
169+
170+
Log.WriteLine($"Executing '{display}':");
171+
Log.WriteLine($"Output being captured to: {LogFilePath}");
172+
173+
var result = ((Command)command).Execute(ProcessStartedHandler, FileWriter);
174+
175+
FileWriter.WriteLine("====================================");
176+
FileWriter.WriteLine($"Command exited with code: {result.ExitCode}");
177+
FileWriter.WriteLine($"=== Test ended at {DateTime.Now} ===");
178+
179+
Log.WriteLine($"Command '{display}' exited with exit code {result.ExitCode}.");
180+
181+
return result;
182+
}
183+
finally
184+
{
185+
// Make sure to close and dispose the writer
186+
if (FileWriter is not null)
187+
{
188+
try
189+
{
190+
FileWriter.Close();
191+
FileWriter.Dispose();
192+
}
193+
catch (Exception ex)
194+
{
195+
Log.WriteLine($"Error closing log file: {ex.Message}");
196+
}
197+
}
198+
}
199+
}
200+
201+
public static void LogCommandResult(ITestOutputHelper log, CommandResult result)
202+
{
203+
log.WriteLine($"> {result.StartInfo.FileName} {result.StartInfo.Arguments}");
204+
log.WriteLine(result.StdOut);
205+
206+
if (!string.IsNullOrEmpty(result.StdErr))
207+
{
208+
log.WriteLine("");
209+
log.WriteLine("StdErr:");
210+
log.WriteLine(result.StdErr);
211+
}
212+
213+
if (result.ExitCode != 0)
214+
{
215+
log.WriteLine($"Exit Code: {result.ExitCode}");
216+
}
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)