GitHub Actions Runner

This commit is contained in:
Tingluo Huang
2019-10-10 00:52:42 -04:00
commit c8afc84840
1255 changed files with 198670 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker
{
public class LoggingCommandL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "LoggingCommand")]
public void CommandParserTest()
{
//Arrange
using (var hc = new TestHostContext(this))
{
string message;
ActionCommand test;
ActionCommand verify;
HashSet<string> commands = new HashSet<string>() { "do-something" };
//##[do-something k1=v1;]msg
message = "##[do-something k1=v1;]msg";
test = new ActionCommand("do-something")
{
Data = "msg",
};
test.Properties.Add("k1", "v1");
Assert.True(ActionCommand.TryParse(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//##[do-something]
message = "##[do-something]";
test = new ActionCommand("do-something");
Assert.True(ActionCommand.TryParse(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//##[do-something k1=%3B=%0D=%0A=%5D;]%3B-%0D-%0A-%5D
message = "##[do-something k1=%3B=%0D=%0A=%5D;]%3B-%0D-%0A-%5D";
test = new ActionCommand("do-something")
{
Data = ";-\r-\n-]",
};
test.Properties.Add("k1", ";=\r=\n=]");
Assert.True(ActionCommand.TryParse(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//##[do-something k1=;k2=;]
message = "##[do-something k1=;k2=;]";
test = new ActionCommand("do-something");
Assert.True(ActionCommand.TryParse(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//>>> ##[do-something k1=;k2=;]
message = ">>> ##[do-something k1=v1;]msg";
test = new ActionCommand("do-something")
{
Data = "msg",
};
test.Properties.Add("k1", "v1");
Assert.True(ActionCommand.TryParse(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "LoggingCommand")]
public void CommandParserV2Test()
{
//Arrange
using (var hc = new TestHostContext(this))
{
string message;
ActionCommand test;
ActionCommand verify;
HashSet<string> commands = new HashSet<string>() { "do-something" };
//::do-something k1=v1;]msg
message = "::do-something k1=v1,::msg";
test = new ActionCommand("do-something")
{
Data = "msg",
};
test.Properties.Add("k1", "v1");
Assert.True(ActionCommand.TryParseV2(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//::do-something::
message = "::do-something::";
test = new ActionCommand("do-something");
Assert.True(ActionCommand.TryParseV2(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//::do-something k1=%3B=%0D=%0A=%5D;::%3B-%0D-%0A-%5D
message = "::do-something k1=;=%2C=%0D=%0A=]=%3A,::;-%0D-%0A-]-:-,";
test = new ActionCommand("do-something")
{
Data = ";-\r-\n-]-:-,",
};
test.Properties.Add("k1", ";=,=\r=\n=]=:");
Assert.True(ActionCommand.TryParseV2(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//::do-something k1=,k2=,::
message = "::do-something k1=,k2=,::";
test = new ActionCommand("do-something");
Assert.True(ActionCommand.TryParseV2(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
//::do-something k1=v1::
message = "::do-something k1=v1::";
test = new ActionCommand("do-something");
test.Properties.Add("k1", "v1");
Assert.True(ActionCommand.TryParseV2(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
test = null;
verify = null;
// ::do-something k1=v1,::
message = " ::do-something k1=v1,::msg";
test = new ActionCommand("do-something")
{
Data = "msg",
};
test.Properties.Add("k1", "v1");
Assert.True(ActionCommand.TryParseV2(message, commands, out verify));
Assert.True(IsEqualCommand(hc, test, verify));
message = "";
verify = null;
// >>> ::do-something k1=v1,::
message = " >>> ::do-something k1=v1,::msg";
Assert.False(ActionCommand.TryParseV2(message, commands, out verify));
}
}
private bool IsEqualCommand(IHostContext hc, ActionCommand e1, ActionCommand e2)
{
try
{
if (!string.Equals(e1.Command, e2.Command, StringComparison.OrdinalIgnoreCase))
{
hc.GetTrace("CommandEqual").Info("Command 1={0}, Command 2={1}", e1.Command, e2.Command);
return false;
}
if (!string.Equals(e1.Data, e2.Data, StringComparison.OrdinalIgnoreCase) && (!string.IsNullOrEmpty(e1.Data) && !string.IsNullOrEmpty(e2.Data)))
{
hc.GetTrace("CommandEqual").Info("Data 1={0}, Data 2={1}", e1.Data, e2.Data);
return false;
}
if (e1.Properties.Count != e2.Properties.Count)
{
hc.GetTrace("CommandEqual").Info("Logging events contain different numbers of Properties,{0} to {1}", e1.Properties.Count, e2.Properties.Count);
return false;
}
if (!e1.Properties.SequenceEqual(e2.Properties))
{
hc.GetTrace("CommandEqual").Info("Logging events contain different Properties");
hc.GetTrace("CommandEqual").Info("Properties for event 1:");
foreach (var data in e1.Properties)
{
hc.GetTrace("CommandEqual").Info("Key={0}, Value={1}", data.Key, data.Value);
}
hc.GetTrace("CommandEqual").Info("Properties for event 2:");
foreach (var data in e2.Properties)
{
hc.GetTrace("CommandEqual").Info("Key={0}, Value={1}", data.Key, data.Value);
}
return false;
}
}
catch (Exception ex)
{
hc.GetTrace("CommandEqual").Info("Catch Exception during compare:{0}", ex.ToString());
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker;
using Moq;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class ActionCommandManagerL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EnablePluginInternalCommand()
{
using (TestHostContext _hc = new TestHostContext(this))
{
var extensionManger = new Mock<IExtensionManager>();
var directoryManager = new Mock<IPipelineDirectoryManager>();
var pluginCommand = new InternalPluginSetRepoPathCommandExtension();
pluginCommand.Initialize(_hc);
var envCommand = new SetEnvCommandExtension();
envCommand.Initialize(_hc);
extensionManger.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { pluginCommand, envCommand });
_hc.SetSingleton<IExtensionManager>(extensionManger.Object);
_hc.SetSingleton<IPipelineDirectoryManager>(directoryManager.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) =>
{
_hc.GetTrace().Info($"{tag} {line}");
return 1;
});
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) =>
{
_hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
});
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
commandManager.EnablePluginInternalCommand();
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath"));
directoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void DisablePluginInternalCommand()
{
using (TestHostContext _hc = new TestHostContext(this))
{
var extensionManger = new Mock<IExtensionManager>();
var directoryManager = new Mock<IPipelineDirectoryManager>();
var pluginCommand = new InternalPluginSetRepoPathCommandExtension();
pluginCommand.Initialize(_hc);
var envCommand = new SetEnvCommandExtension();
envCommand.Initialize(_hc);
extensionManger.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { pluginCommand, envCommand });
_hc.SetSingleton<IExtensionManager>(extensionManger.Object);
_hc.SetSingleton<IPipelineDirectoryManager>(directoryManager.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) =>
{
_hc.GetTrace().Info($"{tag} {line}");
return 1;
});
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) =>
{
_hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
});
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
commandManager.EnablePluginInternalCommand();
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath"));
commandManager.DisablePluginInternalCommand();
Assert.False(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath"));
directoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void StopProcessCommand()
{
using (TestHostContext _hc = new TestHostContext(this))
{
var extensionManger = new Mock<IExtensionManager>();
var pluginCommand = new InternalPluginSetRepoPathCommandExtension();
pluginCommand.Initialize(_hc);
var envCommand = new SetEnvCommandExtension();
envCommand.Initialize(_hc);
extensionManger.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { pluginCommand, envCommand });
_hc.SetSingleton<IExtensionManager>(extensionManger.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) =>
{
_hc.GetTrace().Info($"{tag} {line}");
return 1;
});
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) =>
{
_hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
});
_ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>());
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[stop-commands]stopToken"));
Assert.False(commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar"));
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[stopToken]"));
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar"));
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,497 @@
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker;
using Moq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class ActionManifestManagerL0
{
private CancellationTokenSource _ecTokenSource;
private Mock<IExecutionContext> _ec;
private TestHostContext _hc;
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_ContainerAction_Dockerfile()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.Container);
var containerAction = result.Execution as ContainerActionExecutionData;
Assert.Equal(containerAction.Image, "Dockerfile");
Assert.Equal(containerAction.EntryPoint, "main.sh");
Assert.Equal(containerAction.Arguments[0].ToString(), "bzz");
Assert.Equal(containerAction.Environment[0].Key.ToString(), "Token");
Assert.Equal(containerAction.Environment[0].Value.ToString(), "foo");
Assert.Equal(containerAction.Environment[1].Key.ToString(), "Url");
Assert.Equal(containerAction.Environment[1].Value.ToString(), "bar");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_ContainerAction_Dockerfile_Post()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_cleanup.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.Container);
var containerAction = result.Execution as ContainerActionExecutionData;
Assert.Equal(containerAction.Image, "Dockerfile");
Assert.Equal(containerAction.EntryPoint, "main.sh");
Assert.Equal(containerAction.Cleanup, "cleanup.sh");
Assert.Equal(containerAction.CleanupCondition, "failure()");
Assert.Equal(containerAction.Arguments[0].ToString(), "bzz");
Assert.Equal(containerAction.Environment[0].Key.ToString(), "Token");
Assert.Equal(containerAction.Environment[0].Value.ToString(), "foo");
Assert.Equal(containerAction.Environment[1].Key.ToString(), "Url");
Assert.Equal(containerAction.Environment[1].Value.ToString(), "bar");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_ContainerAction_NoArgsNoEnv()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_noargs_noenv_noentrypoint.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.Container);
var containerAction = result.Execution as ContainerActionExecutionData;
Assert.Equal(containerAction.Image, "Dockerfile");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_ContainerAction_Dockerfile_Expression()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_arg_env_expression.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.Container);
var containerAction = result.Execution as ContainerActionExecutionData;
Assert.Equal(containerAction.Image, "Dockerfile");
Assert.Equal(containerAction.EntryPoint, "main.sh");
Assert.Equal(containerAction.Arguments[0].ToString(), "${{ inputs.greeting }}");
Assert.Equal(containerAction.Environment[0].Key.ToString(), "Token");
Assert.Equal(containerAction.Environment[0].Value.ToString(), "foo");
Assert.Equal(containerAction.Environment[1].Key.ToString(), "Url");
Assert.Equal(containerAction.Environment[1].Value.ToString(), "${{ inputs.entryPoint }}");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_ContainerAction_DockerHub()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerhubaction.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.Container);
var containerAction = result.Execution as ContainerActionExecutionData;
Assert.Equal(containerAction.Image, "docker://ubuntu:18.04");
Assert.Equal(containerAction.EntryPoint, "main.sh");
Assert.Equal(containerAction.Arguments[0].ToString(), "bzz");
Assert.Equal(containerAction.Environment[0].Key.ToString(), "Token");
Assert.Equal(containerAction.Environment[0].Value.ToString(), "foo");
Assert.Equal(containerAction.Environment[1].Key.ToString(), "Url");
Assert.Equal(containerAction.Environment[1].Value.ToString(), "bar");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_NodeAction()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Deprecated.Count, 1);
Assert.True(result.Deprecated.ContainsKey("greeting"));
result.Deprecated.TryGetValue("greeting", out string value);
Assert.Equal(value, "This property has been deprecated");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.NodeJS);
var nodeAction = result.Execution as NodeJSActionExecutionData;
Assert.Equal(nodeAction.Script, "main.js");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_NodeAction_Cleanup()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction_cleanup.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Deprecated.Count, 1);
Assert.True(result.Deprecated.ContainsKey("greeting"));
result.Deprecated.TryGetValue("greeting", out string value);
Assert.Equal(value, "This property has been deprecated");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.NodeJS);
var nodeAction = result.Execution as NodeJSActionExecutionData;
Assert.Equal(nodeAction.Script, "main.js");
Assert.Equal(nodeAction.Cleanup, "cleanup.js");
Assert.Equal(nodeAction.CleanupCondition, "cancelled()");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_PluginAction()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "pluginaction.yml"));
//Assert
Assert.Equal(result.Name, "Hello World");
Assert.Equal(result.Description, "Greet the world and record the time");
Assert.Equal(result.Inputs.Count, 2);
Assert.Equal(result.Inputs[0].Key.AssertString("key").Value, "greeting");
Assert.Equal(result.Inputs[0].Value.AssertString("value").Value, "Hello");
Assert.Equal(result.Inputs[1].Key.AssertString("key").Value, "entryPoint");
Assert.Equal(result.Inputs[1].Value.AssertString("value").Value, "");
Assert.Equal(result.Execution.ExecutionType, ActionExecutionType.Plugin);
var pluginAction = result.Execution as PluginActionExecutionData;
Assert.Equal(pluginAction.Plugin, "someplugin");
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Evaluate_ContainerAction_Args()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
var arguments = new SequenceToken(null, null, null);
arguments.Add(new BasicExpressionToken(null, null, null, "inputs.greeting"));
arguments.Add(new StringToken(null, null, null, "test"));
var inputsContext = new DictionaryContextData();
inputsContext.Add("greeting", new StringContextData("hello"));
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
evaluateContext["inputs"] = inputsContext;
//Act
var result = actionManifest.EvaluateContainerArguments(_ec.Object, arguments, evaluateContext);
//Assert
Assert.Equal(result[0], "hello");
Assert.Equal(result[1], "test");
Assert.Equal(result.Count, 2);
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Evaluate_ContainerAction_Env()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
var environment = new MappingToken(null, null, null);
environment.Add(new StringToken(null, null, null, "hello"), new BasicExpressionToken(null, null, null, "inputs.greeting"));
environment.Add(new StringToken(null, null, null, "test"), new StringToken(null, null, null, "test"));
var inputsContext = new DictionaryContextData();
inputsContext.Add("greeting", new StringContextData("hello"));
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
evaluateContext["inputs"] = inputsContext;
//Act
var result = actionManifest.EvaluateContainerEnvironment(_ec.Object, environment, evaluateContext);
//Assert
Assert.Equal(result["hello"], "hello");
Assert.Equal(result["test"], "test");
Assert.Equal(result.Count, 2);
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Evaluate_Default_Input()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
var githubContext = new DictionaryContextData();
githubContext.Add("ref", new StringContextData("refs/heads/master"));
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
evaluateContext["github"] = githubContext;
evaluateContext["strategy"] = new DictionaryContextData();
evaluateContext["matrix"] = new DictionaryContextData();
evaluateContext["steps"] = new DictionaryContextData();
evaluateContext["job"] = new DictionaryContextData();
evaluateContext["runner"] = new DictionaryContextData();
evaluateContext["env"] = new DictionaryContextData();
//Act
var result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new StringToken(null, null, null, "defaultValue"), evaluateContext);
//Assert
Assert.Equal(result, "defaultValue");
//Act
result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new BasicExpressionToken(null, null, null, "github.ref"), evaluateContext);
//Assert
Assert.Equal(result, "refs/heads/master");
}
finally
{
Teardown();
}
}
private void Setup([CallerMemberName] string name = "")
{
_ecTokenSource?.Dispose();
_ecTokenSource = new CancellationTokenSource();
// Test host context.
_hc = new TestHostContext(this, name);
_ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.WriteDebug).Returns(true);
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary<string, VariableValue>()));
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
}
private void Teardown()
{
_hc?.Dispose();
}
}
}

View File

@@ -0,0 +1,359 @@
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container;
using GitHub.Runner.Worker.Handlers;
using Moq;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class ActionRunnerL0
{
private CancellationTokenSource _ecTokenSource;
private Mock<IHandlerFactory> _handlerFactory;
private Mock<IActionManager> _actionManager;
private Mock<IDefaultStepHost> _defaultStepHost;
private Mock<IExecutionContext> _ec;
private TestHostContext _hc;
private ActionRunner _actionRunner;
private IActionManifestManager _actionManifestManager;
private string _workFolder;
private DictionaryContextData _context = new DictionaryContextData();
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void MergeDefaultInputs()
{
//Arrange
Setup();
var actionId = Guid.NewGuid();
var actionInputs = new MappingToken(null, null, null);
actionInputs.Add(new StringToken(null, null, null, "input1"), new StringToken(null, null, null, "test1"));
actionInputs.Add(new StringToken(null, null, null, "input2"), new StringToken(null, null, null, "test2"));
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
Reference = new Pipelines.ContainerRegistryReference()
{
Image = "ubuntu:16.04"
},
Inputs = actionInputs
};
_actionRunner.Action = action;
Dictionary<string, string> finialInputs = new Dictionary<string, string>();
_handlerFactory.Setup(x => x.Create(It.IsAny<IExecutionContext>(), It.IsAny<ActionStepDefinitionReference>(), It.IsAny<IStepHost>(), It.IsAny<ActionExecutionData>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<Variables>(), It.IsAny<string>()))
.Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary<string, string> inputs, Dictionary<string, string> environment, Variables runtimeVariables, string taskDirectory) =>
{
finialInputs = inputs;
})
.Returns(new Mock<IHandler>().Object);
//Act
await _actionRunner.RunAsync();
foreach (var input in finialInputs)
{
_hc.GetTrace().Info($"Input: {input.Key}={input.Value}");
}
//Assert
Assert.Equal(finialInputs["input1"], "test1");
Assert.Equal(finialInputs["input2"], "test2");
Assert.Equal(finialInputs["input3"], "github");
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void WriteEventPayload()
{
//Arrange
Setup();
var actionId = Guid.NewGuid();
var actionInputs = new MappingToken(null, null, null);
actionInputs.Add(new StringToken(null, null, null, "input1"), new StringToken(null, null, null, "test1"));
actionInputs.Add(new StringToken(null, null, null, "input2"), new StringToken(null, null, null, "test2"));
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
Reference = new Pipelines.ContainerRegistryReference()
{
Image = "ubuntu:16.04"
},
Inputs = actionInputs
};
_actionRunner.Action = action;
Dictionary<string, string> finialInputs = new Dictionary<string, string>();
_handlerFactory.Setup(x => x.Create(It.IsAny<IExecutionContext>(), It.IsAny<ActionStepDefinitionReference>(), It.IsAny<IStepHost>(), It.IsAny<ActionExecutionData>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<Variables>(), It.IsAny<string>()))
.Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary<string, string> inputs, Dictionary<string, string> environment, Variables runtimeVariables, string taskDirectory) =>
{
finialInputs = inputs;
})
.Returns(new Mock<IHandler>().Object);
//Act
await _actionRunner.RunAsync();
//Assert
_ec.Verify(x => x.SetGitHubContext("event_path", Path.Combine(_hc.GetDirectory(WellKnownDirectory.Temp), "_github_workflow", "event.json")), Times.Once);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateLegacyDisplayName()
{
// Arrange
Setup();
var actionInputs = new MappingToken(null, null, null);
actionInputs.Add(new StringToken(null, null, null, "script"), new StringToken(null, null, null, "echo hello world"));
var actionId = Guid.NewGuid();
var actionDisplayName = "Run echo hello world";
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
DisplayName = actionDisplayName,
Inputs = actionInputs,
};
_actionRunner.Action = action;
var matrixData = new DictionaryContextData
{
["node"] = new NumberContextData(8)
};
_context.Add("matrix", matrixData);
// Act
// Should not do anything if we don't have a displayNameToken to expand
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
// Assert
Assert.False(didUpdateDisplayName);
Assert.Equal(actionDisplayName, _actionRunner.DisplayName);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateExpansionOfDisplayNameToken()
{
// Arrange
Setup();
var actionId = Guid.NewGuid();
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
DisplayNameToken = new BasicExpressionToken(null, null, null, "matrix.node"),
};
_actionRunner.Action = action;
var expectedString = "8";
var matrixData = new DictionaryContextData
{
["node"] = new StringContextData(expectedString)
};
_context.Add("matrix", matrixData);
// Act
// Should expand the displaynameToken and set the display name to that
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
// Assert
Assert.True(didUpdateDisplayName);
Assert.Equal(expectedString, _actionRunner.DisplayName);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateExpansionOfScriptDisplayName()
{
// Arrange
Setup();
var actionInputs = new MappingToken(null, null, null);
actionInputs.Add(new StringToken(null, null, null, "script"), new BasicExpressionToken(null, null, null, "matrix.node"));
var actionId = Guid.NewGuid();
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
Inputs = actionInputs,
Reference = new Pipelines.ScriptReference()
};
_actionRunner.Action = action;
var matrixData = new DictionaryContextData
{
["node"] = new StringContextData("8")
};
_context.Add("matrix", matrixData);
// Act
// Should expand the displaynameToken and set the display name to that
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
// Assert
Assert.True(didUpdateDisplayName);
Assert.Equal("Run 8", _actionRunner.DisplayName);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateExpansionOfContainerDisplayName()
{
// Arrange
Setup();
var actionId = Guid.NewGuid();
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
Reference = new Pipelines.ContainerRegistryReference()
{
Image = "TestImageName:latest"
}
};
_actionRunner.Action = action;
// Act
// Should expand the displaynameToken and set the display name to that
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
// Assert
Assert.True(didUpdateDisplayName);
Assert.Equal("Run TestImageName:latest", _actionRunner.DisplayName);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateDisplayNameWithoutContext()
{
// Arrange
Setup();
var actionId = Guid.NewGuid();
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
DisplayNameToken = new BasicExpressionToken(null, null, null, "matrix.node"),
};
_actionRunner.Action = action;
// Act
// Should not do anything if we don't have context on the display name
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
// Assert
Assert.False(didUpdateDisplayName);
// Should use the pretty display name until we can eval
Assert.Equal("${{ matrix.node }}", _actionRunner.DisplayName);
}
private void CreateAction(string yamlContent, out Pipelines.ActionStep instance, out string directory)
{
directory = Path.Combine(_workFolder, Constants.Path.ActionsDirectory, "GitHub/actions".Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), "master");
string file = Path.Combine(directory, Constants.Path.ActionManifestFile);
Directory.CreateDirectory(Path.GetDirectoryName(file));
File.WriteAllText(file, yamlContent);
instance = new Pipelines.ActionStep()
{
Id = Guid.NewGuid(),
Reference = new Pipelines.RepositoryPathReference()
{
Name = "GitHub/actions",
Ref = "master",
RepositoryType = Pipelines.RepositoryTypes.GitHub
}
};
}
private void Setup([CallerMemberName] string name = "")
{
_ecTokenSource?.Dispose();
_ecTokenSource = new CancellationTokenSource();
// Test host context.
_hc = new TestHostContext(this, name);
var actionInputs = new MappingToken(null, null, null);
actionInputs.Add(new StringToken(null, null, null, "input1"), new StringToken(null, null, null, "input1"));
actionInputs.Add(new StringToken(null, null, null, "input2"), new StringToken(null, null, null, ""));
actionInputs.Add(new StringToken(null, null, null, "input3"), new StringToken(null, null, null, "github"));
var actionDefinition = new Definition()
{
Directory = _hc.GetDirectory(WellKnownDirectory.Work),
Data = new ActionDefinitionData()
{
Name = name,
Description = name,
Inputs = actionInputs,
Execution = new ScriptActionExecutionData()
}
};
// Mocks.
_actionManager = new Mock<IActionManager>();
_actionManager.Setup(x => x.LoadAction(It.IsAny<IExecutionContext>(), It.IsAny<ActionStep>())).Returns(actionDefinition);
_handlerFactory = new Mock<IHandlerFactory>();
_defaultStepHost = new Mock<IDefaultStepHost>();
_actionManifestManager = new ActionManifestManager();
_actionManifestManager.Initialize(_hc);
var githubContext = new GitHubContext();
githubContext.Add("event", JToken.Parse("{\"foo\":\"bar\"}").ToPipelineContextData());
_context.Add("github", githubContext);
_ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.ExpressionValues).Returns(_context);
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
_ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>());
_ec.Setup(x => x.SetGitHubContext(It.IsAny<string>(), It.IsAny<string>()));
_ec.Setup(x => x.GetGitHubContext(It.IsAny<string>())).Returns("{\"foo\":\"bar\"}");
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary<string, VariableValue>()));
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
_hc.SetSingleton<IActionManager>(_actionManager.Object);
_hc.SetSingleton<IHandlerFactory>(_handlerFactory.Object);
_hc.SetSingleton<IActionManifestManager>(_actionManifestManager);
_hc.EnqueueInstance<IDefaultStepHost>(_defaultStepHost.Object);
// Instance to test.
_actionRunner = new ActionRunner();
_actionRunner.Initialize(_hc);
_actionRunner.ExecutionContext = _ec.Object;
}
}
}

View File

@@ -0,0 +1,259 @@
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Xunit;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class ExecutionContextL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void AddIssue_CountWarningsErrors()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange: Create a job request message.
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
TimelineReference timeline = new TimelineReference();
JobEnvironment environment = new JobEnvironment();
environment.SystemConnection = new ServiceEndpoint();
List<TaskInstance> tasks = new List<TaskInstance>();
Guid JobId = Guid.NewGuid();
string jobName = "some job name";
var jobRequest = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, environment, tasks));
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Id = "github",
Version = "sha1"
});
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
// Arrange: Setup the paging logger.
var pagingLogger = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
hc.EnqueueInstance(pagingLogger.Object);
hc.SetSingleton(jobServerQueue.Object);
var ec = new Runner.Worker.ExecutionContext();
ec.Initialize(hc);
// Act.
ec.InitializeJob(jobRequest, CancellationToken.None);
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
ec.Complete();
// Assert.
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.ErrorCount == 15)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.WarningCount == 14)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Type == IssueType.Error).Count() == 10)), Times.AtLeastOnce);
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Type == IssueType.Warning).Count() == 10)), Times.AtLeastOnce);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Debug_Multilines()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange: Create a job request message.
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
TimelineReference timeline = new TimelineReference();
JobEnvironment environment = new JobEnvironment();
environment.SystemConnection = new ServiceEndpoint();
List<TaskInstance> tasks = new List<TaskInstance>();
Guid JobId = Guid.NewGuid();
string jobName = "some job name";
var jobRequest = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, environment, tasks));
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Id = "github",
Version = "sha1"
});
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
jobRequest.Variables["ACTIONS_STEP_DEBUG"] = "true";
// Arrange: Setup the paging logger.
var pagingLogger = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>())).Callback((Guid id, string msg) => { hc.GetTrace().Info(msg); });
hc.EnqueueInstance(pagingLogger.Object);
hc.SetSingleton(jobServerQueue.Object);
var ec = new Runner.Worker.ExecutionContext();
ec.Initialize(hc);
// Act.
ec.InitializeJob(jobRequest, CancellationToken.None);
ec.Debug(null);
ec.Debug("");
ec.Debug("\n");
ec.Debug("\r\n");
ec.Debug("test");
ec.Debug("te\nst");
ec.Debug("te\r\nst");
ec.Complete();
jobServerQueue.Verify(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>()), Times.Exactly(10));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void RegisterPostJobAction_ShareState()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange: Create a job request message.
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
TimelineReference timeline = new TimelineReference();
JobEnvironment environment = new JobEnvironment();
environment.SystemConnection = new ServiceEndpoint();
List<TaskInstance> tasks = new List<TaskInstance>();
Guid JobId = Guid.NewGuid();
string jobName = "some job name";
var jobRequest = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, environment, tasks));
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Id = "github",
Version = "sha1"
});
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
jobRequest.Variables["ACTIONS_STEP_DEBUG"] = "true";
// Arrange: Setup the paging logger.
var pagingLogger1 = new Mock<IPagingLogger>();
var pagingLogger2 = new Mock<IPagingLogger>();
var pagingLogger3 = new Mock<IPagingLogger>();
var pagingLogger4 = new Mock<IPagingLogger>();
var pagingLogger5 = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>())).Callback((Guid id, string msg) => { hc.GetTrace().Info(msg); });
var actionRunner1 = new ActionRunner();
actionRunner1.Initialize(hc);
var actionRunner2 = new ActionRunner();
actionRunner2.Initialize(hc);
hc.EnqueueInstance(pagingLogger1.Object);
hc.EnqueueInstance(pagingLogger2.Object);
hc.EnqueueInstance(pagingLogger3.Object);
hc.EnqueueInstance(pagingLogger4.Object);
hc.EnqueueInstance(pagingLogger5.Object);
hc.EnqueueInstance(actionRunner1 as IActionRunner);
hc.EnqueueInstance(actionRunner2 as IActionRunner);
hc.SetSingleton(jobServerQueue.Object);
var jobContext = new Runner.Worker.ExecutionContext();
jobContext.Initialize(hc);
// Act.
jobContext.InitializeJob(jobRequest, CancellationToken.None);
var action1 = jobContext.CreateChild(Guid.NewGuid(), "action_1", "action_1", null, null);
action1.IntraActionState["state"] = "1";
var action2 = jobContext.CreateChild(Guid.NewGuid(), "action_2", "action_2", null, null);
action2.IntraActionState["state"] = "2";
action1.RegisterPostJobAction("post1", "always()", new Pipelines.ActionStep() { Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } });
action2.RegisterPostJobAction("post2", "always()", new Pipelines.ActionStep() { Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } });
Assert.NotNull(jobContext.JobSteps);
Assert.NotNull(jobContext.PostJobSteps);
Assert.Null(action1.JobSteps);
Assert.Null(action2.JobSteps);
Assert.Null(action1.PostJobSteps);
Assert.Null(action2.PostJobSteps);
var post1 = jobContext.PostJobSteps.Pop();
var post2 = jobContext.PostJobSteps.Pop();
Assert.Equal("post2", (post1 as IActionRunner).Action.Name);
Assert.Equal("post1", (post2 as IActionRunner).Action.Name);
Assert.Equal(ActionRunStage.Post, (post1 as IActionRunner).Stage);
Assert.Equal(ActionRunStage.Post, (post2 as IActionRunner).Stage);
Assert.Equal("always()", (post1 as IActionRunner).Condition);
Assert.Equal("always()", (post2 as IActionRunner).Condition);
Assert.Equal("2", (post1 as IActionRunner).ExecutionContext.IntraActionState["state"]);
Assert.Equal("1", (post2 as IActionRunner).ExecutionContext.IntraActionState["state"]);
}
}
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
{
var hc = new TestHostContext(this, testName);
// Arrange: Setup the configation store.
var configurationStore = new Mock<IConfigurationStore>();
configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings());
hc.SetSingleton(configurationStore.Object);
// Arrange: Setup the proxy configation.
var proxy = new Mock<IRunnerWebProxy>();
hc.SetSingleton(proxy.Object);
// Arrange: Setup the cert configation.
var cert = new Mock<IRunnerCertificateManager>();
hc.SetSingleton(cert.Object);
// Arrange: Create the execution context.
hc.SetSingleton(new Mock<IJobServerQueue>().Object);
return hc;
}
}
}

View File

@@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker;
using Moq;
using Xunit;
using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.Pipelines.ContextData;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class ExpressionManagerL0
{
private Mock<IExecutionContext> _ec;
private ExpressionManager _expressionManager;
private DictionaryContextData _expressions;
private JobContext _jobContext;
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void AlwaysFunction()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var variableSets = new[]
{
new { JobStatus = (ActionResult?)null, Expected = true },
new { JobStatus = (ActionResult?)ActionResult.Cancelled, Expected = true },
new { JobStatus = (ActionResult?)ActionResult.Failure, Expected = true },
new { JobStatus = (ActionResult?)ActionResult.Success, Expected = true },
};
foreach (var variableSet in variableSets)
{
InitializeExecutionContext(hc);
_jobContext.Status = variableSet.JobStatus;
// Act.
bool actual = _expressionManager.Evaluate(_ec.Object, "always()").Value;
// Assert.
Assert.Equal(variableSet.Expected, actual);
}
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void CancelledFunction()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var variableSets = new[]
{
new { JobStatus = (ActionResult?)ActionResult.Cancelled, Expected = true },
new { JobStatus = (ActionResult?)null, Expected = false },
new { JobStatus = (ActionResult?)ActionResult.Failure, Expected = false },
new { JobStatus = (ActionResult?)ActionResult.Success, Expected = false },
};
foreach (var variableSet in variableSets)
{
InitializeExecutionContext(hc);
_jobContext.Status = variableSet.JobStatus;
// Act.
bool actual = _expressionManager.Evaluate(_ec.Object, "cancelled()").Value;
// Assert.
Assert.Equal(variableSet.Expected, actual);
}
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void FailureFunction()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var variableSets = new[]
{
new { JobStatus = (ActionResult?)ActionResult.Failure, Expected = true },
new { JobStatus = (ActionResult?)null, Expected = false },
new { JobStatus = (ActionResult?)ActionResult.Cancelled, Expected = false },
new { JobStatus = (ActionResult?)ActionResult.Success, Expected = false },
};
foreach (var variableSet in variableSets)
{
InitializeExecutionContext(hc);
_jobContext.Status = variableSet.JobStatus;
// Act.
bool actual = _expressionManager.Evaluate(_ec.Object, "failure()").Value;
// Assert.
Assert.Equal(variableSet.Expected, actual);
}
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void SuccessFunction()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var variableSets = new[]
{
new { JobStatus = (ActionResult?)null, Expected = true },
new { JobStatus = (ActionResult?)ActionResult.Success, Expected = true },
new { JobStatus = (ActionResult?)ActionResult.Cancelled, Expected = false },
new { JobStatus = (ActionResult?)ActionResult.Failure, Expected = false },
};
foreach (var variableSet in variableSets)
{
InitializeExecutionContext(hc);
_jobContext.Status = variableSet.JobStatus;
// Act.
bool actual = _expressionManager.Evaluate(_ec.Object, "success()").Value;
// Assert.
Assert.Equal(variableSet.Expected, actual);
}
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void ContextNamedValue()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var variableSets = new[]
{
new { Condition = "github.ref == 'refs/heads/master'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = true },
new { Condition = "github['ref'] == 'refs/heads/master'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = true },
new { Condition = "github.nosuch || '' == ''", VariableName = "ref", VariableValue = "refs/heads/master", Expected = true },
new { Condition = "github['ref'] == 'refs/heads/release'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = false },
new { Condition = "github.ref == 'refs/heads/release'", VariableName = "ref", VariableValue = "refs/heads/master", Expected = false },
};
foreach (var variableSet in variableSets)
{
InitializeExecutionContext(hc);
_ec.Object.ExpressionValues["github"] = new GitHubContext() { { variableSet.VariableName, new StringContextData(variableSet.VariableValue) } };
// Act.
bool actual = _expressionManager.Evaluate(_ec.Object, variableSet.Condition).Value;
// Assert.
Assert.Equal(variableSet.Expected, actual);
}
}
}
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
{
var hc = new TestHostContext(this, testName);
_expressionManager = new ExpressionManager();
_expressionManager.Initialize(hc);
return hc;
}
private void InitializeExecutionContext(TestHostContext hc)
{
_expressions = new DictionaryContextData();
_jobContext = new JobContext();
_ec = new Mock<IExecutionContext>();
_ec.SetupAllProperties();
_ec.Setup(x => x.ExpressionValues).Returns(_expressions);
_ec.Setup(x => x.JobContext).Returns(_jobContext);
}
}
}

View File

@@ -0,0 +1,795 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using GitHub.Runner.Worker;
using GitHub.Services.WebApi;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class IssueMatcherL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Loop_MayNotBeSetOnSinglePattern()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^error: (.+)$"",
""message"": 1,
""loop"": true
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "^file: (.+)$",
File = 1,
},
config.Matchers[0].Patterns[0],
};
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Loop_OnlyAllowedOnLastPattern()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(error)$"",
""severity"": 1
},
{
""regexp"": ""^file: (.+)$"",
""file"": 1,
""loop"": true
},
{
""regexp"": ""^error: (.+)$"",
""message"": 1
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Patterns[1].Loop = false;
config.Matchers[0].Patterns[2].Loop = true;
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Loop_MustSetMessage()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^file: (.+)$"",
""message"": 1
},
{
""regexp"": ""^file: (.+)$"",
""file"": 1,
""loop"": true
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
config.Matchers[0].Patterns[1].Loop = false;
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Message_AllowedInFirstPattern()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^file: (.+)$"",
""message"": 1
},
{
""regexp"": ""^error: (.+)$"",
""file"": 1
}
]
}
]
}
");
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Message_Required()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^error: (.+)$"",
""file"": 1
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Patterns[0].File = null;
config.Matchers[0].Patterns[0].Message = 1;
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Owner_Distinct()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^error: (.+)$"",
""message"": 1
}
]
},
{
""owner"": ""MYmatcher"",
""pattern"": [
{
""regexp"": ""^ERR: (.+)$"",
""message"": 1
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Owner = "asdf";
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Owner_Required()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": """",
""pattern"": [
{
""regexp"": ""^error: (.+)$"",
""message"": 1
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Owner = "asdf";
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_Pattern_Required()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "^error: (.+)$",
Message = 1,
}
};
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_PropertyMayNotBeSetTwice()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^severity: (.+)$"",
""file"": 1
},
{
""regexp"": ""^file: (.+)$"",
""file"": 1
},
{
""regexp"": ""^(.+)$"",
""message"": 1
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Patterns[0].File = null;
config.Matchers[0].Patterns[0].Severity = 1;
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_PropertyOutOfRange()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(.+)$"",
""message"": 2
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Patterns[0].Message = 1;
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Config_Validate_PropertyOutOfRange_LessThanZero()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(.+)$"",
""message"": -1
}
]
}
]
}
");
Assert.Throws<ArgumentException>(() => config.Validate());
// Sanity test
config.Matchers[0].Patterns[0].Message = 1;
config.Validate();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_MultiplePatterns_Loop_AccumulatesStatePerLine()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(.+)$"",
""file"": 1
},
{
""regexp"": ""^(.+)$"",
""code"": 1
},
{
""regexp"": ""^message:(.+)$"",
""message"": 1,
""loop"": true
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("file1");
Assert.Null(match);
match = matcher.Match("code1");
Assert.Null(match);
match = matcher.Match("message:message1");
Assert.Equal("file1", match.File);
Assert.Equal("code1", match.Code);
Assert.Equal("message1", match.Message);
match = matcher.Match("message:message1-2"); // sanity check loop
Assert.Equal("file1", match.File);
Assert.Equal("code1", match.Code);
Assert.Equal("message1-2", match.Message);
match = matcher.Match("abc"); // discarded
match = matcher.Match("file2");
Assert.Null(match);
match = matcher.Match("code2");
Assert.Null(match);
match = matcher.Match("message:message2");
Assert.Equal("file2", match.File);
Assert.Equal("code2", match.Code);
Assert.Equal("message2", match.Message);
match = matcher.Match("abc"); // discarded
match = matcher.Match("abc"); // discarded
match = matcher.Match("file3");
Assert.Null(match);
match = matcher.Match("code3");
Assert.Null(match);
match = matcher.Match("message:message3");
Assert.Equal("file3", match.File);
Assert.Equal("code3", match.Code);
Assert.Equal("message3", match.Message);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_MultiplePatterns_Loop_BrokenMatchClearsState()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(.+)$"",
""file"": 1
},
{
""regexp"": ""^(.+)$"",
""severity"": 1
},
{
""regexp"": ""^message:(.+)$"",
""message"": 1,
""loop"": true
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("my-file.cs"); // file
Assert.Null(match);
match = matcher.Match("real-bad"); // severity
Assert.Null(match);
match = matcher.Match("message:not-working"); // message
Assert.Equal("my-file.cs", match.File);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("not-working", match.Message);
match = matcher.Match("message:problem"); // message
Assert.Equal("my-file.cs", match.File);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("problem", match.Message);
match = matcher.Match("other-file.cs"); // file - breaks the loop
Assert.Null(match);
match = matcher.Match("message:not-good"); // severity - also matches the message pattern, therefore
Assert.Null(match); // guarantees sufficient previous state has been cleared
match = matcher.Match("message:broken"); // message
Assert.Equal("other-file.cs", match.File);
Assert.Equal("message:not-good", match.Severity);
Assert.Equal("broken", match.Message);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_MultiplePatterns_Loop_ExtractsProperties()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^file:(.+) fromPath:(.+)$"",
""file"": 1,
""fromPath"": 2
},
{
""regexp"": ""^severity:(.+)$"",
""severity"": 1
},
{
""regexp"": ""^line:(.+) column:(.+) code:(.+) message:(.+)$"",
""line"": 1,
""column"": 2,
""code"": 3,
""message"": 4,
""loop"": true
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("file:my-file.cs fromPath:my-project.proj");
Assert.Null(match);
match = matcher.Match("severity:real-bad");
Assert.Null(match);
match = matcher.Match("line:123 column:45 code:uh-oh message:not-working");
Assert.Equal("my-file.cs", match.File);
Assert.Equal("my-project.proj", match.FromPath);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("123", match.Line);
Assert.Equal("45", match.Column);
Assert.Equal("uh-oh", match.Code);
Assert.Equal("not-working", match.Message);
match = matcher.Match("line:234 column:56 code:yikes message:broken");
Assert.Equal("my-file.cs", match.File);
Assert.Equal("my-project.proj", match.FromPath);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("234", match.Line);
Assert.Equal("56", match.Column);
Assert.Equal("yikes", match.Code);
Assert.Equal("broken", match.Message);
match = matcher.Match("line:345 column:67 code:failed message:cant-do-that");
Assert.Equal("my-file.cs", match.File);
Assert.Equal("my-project.proj", match.FromPath);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("345", match.Line);
Assert.Equal("67", match.Column);
Assert.Equal("failed", match.Code);
Assert.Equal("cant-do-that", match.Message);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_MultiplePatterns_NonLoop_AccumulatesStatePerLine()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(.+)$"",
""file"": 1
},
{
""regexp"": ""^(.+)$"",
""code"": 1
},
{
""regexp"": ""^message:(.+)$"",
""message"": 1
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("file1");
Assert.Null(match);
match = matcher.Match("code1");
Assert.Null(match);
match = matcher.Match("message:message1");
Assert.Equal("file1", match.File);
Assert.Equal("code1", match.Code);
Assert.Equal("message1", match.Message);
match = matcher.Match("abc"); // discarded
match = matcher.Match("file2");
Assert.Null(match);
match = matcher.Match("code2");
Assert.Null(match);
match = matcher.Match("message:message2");
Assert.Equal("file2", match.File);
Assert.Equal("code2", match.Code);
Assert.Equal("message2", match.Message);
match = matcher.Match("abc"); // discarded
match = matcher.Match("abc"); // discarded
match = matcher.Match("file3");
Assert.Null(match);
match = matcher.Match("code3");
Assert.Null(match);
match = matcher.Match("message:message3");
Assert.Equal("file3", match.File);
Assert.Equal("code3", match.Code);
Assert.Equal("message3", match.Message);
match = matcher.Match("message:message3"); // sanity check not loop
Assert.Null(match);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_MultiplePatterns_NonLoop_DoesNotLoop()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^file:(.+)$"",
""file"": 1
},
{
""regexp"": ""^message:(.+)$"",
""message"": 1
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("file:my-file.cs");
Assert.Null(match);
match = matcher.Match("message:not-working");
Assert.Equal("my-file.cs", match.File);
Assert.Equal("not-working", match.Message);
match = matcher.Match("message:not-working");
Assert.Null(match);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_MultiplePatterns_NonLoop_ExtractsProperties()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^file:(.+) fromPath:(.+)$"",
""file"": 1,
""fromPath"": 2
},
{
""regexp"": ""^severity:(.+)$"",
""severity"": 1
},
{
""regexp"": ""^line:(.+) column:(.+) code:(.+) message:(.+)$"",
""line"": 1,
""column"": 2,
""code"": 3,
""message"": 4
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("file:my-file.cs fromPath:my-project.proj");
Assert.Null(match);
match = matcher.Match("severity:real-bad");
Assert.Null(match);
match = matcher.Match("line:123 column:45 code:uh-oh message:not-working");
Assert.Equal("my-file.cs", match.File);
Assert.Equal("my-project.proj", match.FromPath);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("123", match.Line);
Assert.Equal("45", match.Column);
Assert.Equal("uh-oh", match.Code);
Assert.Equal("not-working", match.Message);
match = matcher.Match("line:123 column:45 code:uh-oh message:not-working"); // sanity check not loop
Assert.Null(match);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_MultiplePatterns_NonLoop_MatchClearsState()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(.+)$"",
""file"": 1
},
{
""regexp"": ""^(.+)$"",
""severity"": 1
},
{
""regexp"": ""^(.+)$"",
""message"": 1
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("my-file.cs"); // file
Assert.Null(match);
match = matcher.Match("real-bad"); // severity
Assert.Null(match);
match = matcher.Match("not-working"); // message
Assert.Equal("my-file.cs", match.File);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("not-working", match.Message);
match = matcher.Match("other-file.cs"); // file
Assert.Null(match);
match = matcher.Match("not-good"); // severity
Assert.Null(match);
match = matcher.Match("broken"); // message
Assert.Equal("other-file.cs", match.File);
Assert.Equal("not-good", match.Severity);
Assert.Equal("broken", match.Message);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_SetsOwner()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^(.+)$"",
""message"": 1
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
Assert.Equal("myMatcher", matcher.Owner);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Matcher_SinglePattern_ExtractsProperties()
{
var config = JsonUtility.FromString<IssueMatchersConfig>(@"
{
""problemMatcher"": [
{
""owner"": ""myMatcher"",
""pattern"": [
{
""regexp"": ""^file:(.+) line:(.+) column:(.+) severity:(.+) code:(.+) message:(.+) fromPath:(.+)$"",
""file"": 1,
""line"": 2,
""column"": 3,
""severity"": 4,
""code"": 5,
""message"": 6,
""fromPath"": 7
}
]
}
]
}
");
config.Validate();
var matcher = new IssueMatcher(config.Matchers[0], TimeSpan.FromSeconds(1));
var match = matcher.Match("file:my-file.cs line:123 column:45 severity:real-bad code:uh-oh message:not-working fromPath:my-project.proj");
Assert.Equal("my-file.cs", match.File);
Assert.Equal("123", match.Line);
Assert.Equal("45", match.Column);
Assert.Equal("real-bad", match.Severity);
Assert.Equal("uh-oh", match.Code);
Assert.Equal("not-working", match.Message);
Assert.Equal("my-project.proj", match.FromPath);
}
}
}

View File

@@ -0,0 +1,386 @@
// using GitHub.DistributedTask.WebApi;
// using GitHub.Runner.Worker;
// using Moq;
// using System;
// using System.Collections.Generic;
// using System.Linq;
// using System.Runtime.CompilerServices;
// using System.Threading.Tasks;
// using Xunit;
// using System.Threading;
// using Pipelines = GitHub.DistributedTask.Pipelines;
// namespace GitHub.Runner.Common.Tests.Worker
// {
// public sealed class JobExtensionL0
// {
// private class TestJobExtension : JobExtension
// {
// public override HostTypes HostType => HostTypes.None;
// public override Type ExtensionType => typeof(IJobExtension);
// public override void ConvertLocalPath(IExecutionContext context, string localPath, out string repoName, out string sourcePath)
// {
// repoName = "";
// sourcePath = "";
// }
// public override IStep GetExtensionPostJobStep(IExecutionContext jobContext)
// {
// return null;
// }
// public override IStep GetExtensionPreJobStep(IExecutionContext jobContext)
// {
// return null;
// }
// public override string GetRootedPath(IExecutionContext context, string path)
// {
// return path;
// }
// public override void InitializeJobExtension(IExecutionContext context, IList<Pipelines.JobStep> steps, Pipelines.WorkspaceOptions workspace)
// {
// return;
// }
// }
// private IExecutionContext _jobEc;
// private Pipelines.AgentJobRequestMessage _message;
// private Mock<ITaskManager> _taskManager;
// private Mock<IAgentLogPlugin> _logPlugin;
// private Mock<IJobServerQueue> _jobServerQueue;
// private Mock<IVstsAgentWebProxy> _proxy;
// private Mock<IAgentCertificateManager> _cert;
// private Mock<IConfigurationStore> _config;
// private Mock<IPagingLogger> _logger;
// private Mock<IExpressionManager> _express;
// private Mock<IContainerOperationProvider> _containerProvider;
// private CancellationTokenSource _tokenSource;
// private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
// {
// var hc = new TestHostContext(this, testName);
// _jobEc = new Runner.Worker.ExecutionContext();
// _taskManager = new Mock<ITaskManager>();
// _jobServerQueue = new Mock<IJobServerQueue>();
// _config = new Mock<IConfigurationStore>();
// _logger = new Mock<IPagingLogger>();
// _proxy = new Mock<IVstsAgentWebProxy>();
// _cert = new Mock<IAgentCertificateManager>();
// _express = new Mock<IExpressionManager>();
// _containerProvider = new Mock<IContainerOperationProvider>();
// _logPlugin = new Mock<IAgentLogPlugin>();
// TaskRunner step1 = new TaskRunner();
// TaskRunner step2 = new TaskRunner();
// TaskRunner step3 = new TaskRunner();
// TaskRunner step4 = new TaskRunner();
// TaskRunner step5 = new TaskRunner();
// TaskRunner step6 = new TaskRunner();
// TaskRunner step7 = new TaskRunner();
// TaskRunner step8 = new TaskRunner();
// TaskRunner step9 = new TaskRunner();
// TaskRunner step10 = new TaskRunner();
// TaskRunner step11 = new TaskRunner();
// TaskRunner step12 = new TaskRunner();
// _logger.Setup(x => x.Setup(It.IsAny<Guid>(), It.IsAny<Guid>()));
// var settings = new AgentSettings
// {
// AgentId = 1,
// AgentName = "agent1",
// ServerUrl = "https://test.visualstudio.com",
// WorkFolder = "_work",
// };
// _config.Setup(x => x.GetSettings())
// .Returns(settings);
// _proxy.Setup(x => x.ProxyAddress)
// .Returns(string.Empty);
// if (_tokenSource != null)
// {
// _tokenSource.Dispose();
// _tokenSource = null;
// }
// _tokenSource = new CancellationTokenSource();
// TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
// TimelineReference timeline = new Timeline(Guid.NewGuid());
// JobEnvironment environment = new JobEnvironment();
// environment.Variables[Constants.Variables.System.Culture] = "en-US";
// environment.SystemConnection = new ServiceEndpoint()
// {
// Name = WellKnownServiceEndpointNames.SystemVssConnection,
// Url = new Uri("https://test.visualstudio.com"),
// Authorization = new EndpointAuthorization()
// {
// Scheme = "Test",
// }
// };
// environment.SystemConnection.Authorization.Parameters["AccessToken"] = "token";
// List<TaskInstance> tasks = new List<TaskInstance>()
// {
// new TaskInstance()
// {
// InstanceId = Guid.NewGuid(),
// DisplayName = "task1",
// },
// new TaskInstance()
// {
// InstanceId = Guid.NewGuid(),
// DisplayName = "task2",
// },
// new TaskInstance()
// {
// InstanceId = Guid.NewGuid(),
// DisplayName = "task3",
// },
// new TaskInstance()
// {
// InstanceId = Guid.NewGuid(),
// DisplayName = "task4",
// },
// new TaskInstance()
// {
// InstanceId = Guid.NewGuid(),
// DisplayName = "task5",
// },
// new TaskInstance()
// {
// InstanceId = Guid.NewGuid(),
// DisplayName = "task6",
// },
// new TaskInstance()
// {
// InstanceId = Guid.NewGuid(),
// DisplayName = "task7",
// },
// };
// Guid JobId = Guid.NewGuid();
// _message = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, testName, testName, environment, tasks));
// _taskManager.Setup(x => x.DownloadAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.TaskStep>>()))
// .Returns(Task.CompletedTask);
// _taskManager.Setup(x => x.Load(It.Is<Pipelines.TaskStep>(t => t.DisplayName == "task1")))
// .Returns(new Definition()
// {
// Data = new DefinitionData()
// {
// PreJobExecution = null,
// Execution = new ExecutionData(),
// PostJobExecution = null,
// },
// });
// _taskManager.Setup(x => x.Load(It.Is<Pipelines.TaskStep>(t => t.DisplayName == "task2")))
// .Returns(new Definition()
// {
// Data = new DefinitionData()
// {
// PreJobExecution = new ExecutionData(),
// Execution = new ExecutionData(),
// PostJobExecution = new ExecutionData(),
// },
// });
// _taskManager.Setup(x => x.Load(It.Is<Pipelines.TaskStep>(t => t.DisplayName == "task3")))
// .Returns(new Definition()
// {
// Data = new DefinitionData()
// {
// PreJobExecution = new ExecutionData(),
// Execution = null,
// PostJobExecution = new ExecutionData(),
// },
// });
// _taskManager.Setup(x => x.Load(It.Is<Pipelines.TaskStep>(t => t.DisplayName == "task4")))
// .Returns(new Definition()
// {
// Data = new DefinitionData()
// {
// PreJobExecution = new ExecutionData(),
// Execution = null,
// PostJobExecution = null,
// },
// });
// _taskManager.Setup(x => x.Load(It.Is<Pipelines.TaskStep>(t => t.DisplayName == "task5")))
// .Returns(new Definition()
// {
// Data = new DefinitionData()
// {
// PreJobExecution = null,
// Execution = null,
// PostJobExecution = new ExecutionData(),
// },
// });
// _taskManager.Setup(x => x.Load(It.Is<Pipelines.TaskStep>(t => t.DisplayName == "task6")))
// .Returns(new Definition()
// {
// Data = new DefinitionData()
// {
// PreJobExecution = new ExecutionData(),
// Execution = new ExecutionData(),
// PostJobExecution = null,
// },
// });
// _taskManager.Setup(x => x.Load(It.Is<Pipelines.TaskStep>(t => t.DisplayName == "task7")))
// .Returns(new Definition()
// {
// Data = new DefinitionData()
// {
// PreJobExecution = null,
// Execution = new ExecutionData(),
// PostJobExecution = new ExecutionData(),
// },
// });
// hc.SetSingleton(_taskManager.Object);
// hc.SetSingleton(_config.Object);
// hc.SetSingleton(_jobServerQueue.Object);
// hc.SetSingleton(_proxy.Object);
// hc.SetSingleton(_cert.Object);
// hc.SetSingleton(_express.Object);
// hc.SetSingleton(_containerProvider.Object);
// hc.SetSingleton(_logPlugin.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object); // jobcontext logger
// hc.EnqueueInstance<IPagingLogger>(_logger.Object); // init step logger
// hc.EnqueueInstance<IPagingLogger>(_logger.Object); // step 1
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object); // step 12
// hc.EnqueueInstance<ITaskRunner>(step1);
// hc.EnqueueInstance<ITaskRunner>(step2);
// hc.EnqueueInstance<ITaskRunner>(step3);
// hc.EnqueueInstance<ITaskRunner>(step4);
// hc.EnqueueInstance<ITaskRunner>(step5);
// hc.EnqueueInstance<ITaskRunner>(step6);
// hc.EnqueueInstance<ITaskRunner>(step7);
// hc.EnqueueInstance<ITaskRunner>(step8);
// hc.EnqueueInstance<ITaskRunner>(step9);
// hc.EnqueueInstance<ITaskRunner>(step10);
// hc.EnqueueInstance<ITaskRunner>(step11);
// hc.EnqueueInstance<ITaskRunner>(step12);
// _jobEc.Initialize(hc);
// _jobEc.InitializeJob(_message, _tokenSource.Token);
// return hc;
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task JobExtensioBuildStepsList()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// TestJobExtension testExtension = new TestJobExtension();
// testExtension.Initialize(hc);
// List<IStep> result = await testExtension.InitializeJob(_jobEc, _message);
// var trace = hc.GetTrace();
// trace.Info(string.Join(", ", result.Select(x => x.DisplayName)));
// Assert.Equal(12, result.Count);
// Assert.Equal("task2", result[0].DisplayName);
// Assert.Equal("task3", result[1].DisplayName);
// Assert.Equal("task4", result[2].DisplayName);
// Assert.Equal("task6", result[3].DisplayName);
// Assert.Equal("task1", result[4].DisplayName);
// Assert.Equal("task2", result[5].DisplayName);
// Assert.Equal("task6", result[6].DisplayName);
// Assert.Equal("task7", result[7].DisplayName);
// Assert.Equal("task7", result[8].DisplayName);
// Assert.Equal("task5", result[9].DisplayName);
// Assert.Equal("task3", result[10].DisplayName);
// Assert.Equal("task2", result[11].DisplayName);
// }
// }
// // [Fact]
// // [Trait("Level", "L0")]
// // [Trait("Category", "Worker")]
// // public async Task JobExtensionIntraTaskState()
// // {
// // using (TestHostContext hc = CreateTestContext())
// // {
// // TestJobExtension testExtension = new TestJobExtension();
// // testExtension.Initialize(hc);
// // List<IStep> result = await testExtension.InitializeJob(_jobEc, _message);
// // var trace = hc.GetTrace();
// // trace.Info(string.Join(", ", result.Select(x => x.DisplayName)));
// // Assert.Equal(12, result.Count);
// // result[0].ExecutionContext.TaskVariables.Set("state1", "value1", false);
// // Assert.Equal("value1", result[5].ExecutionContext.TaskVariables.Get("state1"));
// // Assert.Equal("value1", result[11].ExecutionContext.TaskVariables.Get("state1"));
// // Assert.Null(result[4].ExecutionContext.TaskVariables.Get("state1"));
// // Assert.Null(result[1].ExecutionContext.TaskVariables.Get("state1"));
// // Assert.Null(result[2].ExecutionContext.TaskVariables.Get("state1"));
// // Assert.Null(result[10].ExecutionContext.TaskVariables.Get("state1"));
// // Assert.Null(result[6].ExecutionContext.TaskVariables.Get("state1"));
// // Assert.Null(result[7].ExecutionContext.TaskVariables.Get("state1"));
// // }
// // }
// #if OS_WINDOWS
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task JobExtensionManagementScriptStep()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// hc.EnqueueInstance<IPagingLogger>(_logger.Object);
// Environment.SetEnvironmentVariable("VSTS_AGENT_INIT_INTERNAL_TEMP_HACK", "C:\\init.ps1");
// Environment.SetEnvironmentVariable("VSTS_AGENT_CLEANUP_INTERNAL_TEMP_HACK", "C:\\clenup.ps1");
// try
// {
// TestJobExtension testExtension = new TestJobExtension();
// testExtension.Initialize(hc);
// List<IStep> result = await testExtension.InitializeJob(_jobEc, _message);
// var trace = hc.GetTrace();
// trace.Info(string.Join(", ", result.Select(x => x.DisplayName)));
// Assert.Equal(14, result.Count);
// Assert.True(result[0] is ManagementScriptStep);
// Assert.True(result[13] is ManagementScriptStep);
// Assert.Equal(result[0].DisplayName, "Agent Initialization");
// Assert.Equal(result[13].DisplayName, "Agent Cleanup");
// }
// finally
// {
// Environment.SetEnvironmentVariable("VSTS_AGENT_INIT_INTERNAL_TEMP_HACK", "");
// Environment.SetEnvironmentVariable("VSTS_AGENT_CLEANUP_INTERNAL_TEMP_HACK", "");
// }
// }
// }
// #endif
// }
// }

View File

@@ -0,0 +1,223 @@
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xunit;
using System.Threading;
using System.Collections.ObjectModel;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class JobRunnerL0
{
private IExecutionContext _jobEc;
private JobRunner _jobRunner;
private List<IStep> _initResult = new List<IStep>();
private Pipelines.AgentJobRequestMessage _message;
private CancellationTokenSource _tokenSource;
private Mock<IJobServer> _jobServer;
private Mock<IJobServerQueue> _jobServerQueue;
private Mock<IRunnerWebProxy> _proxyConfig;
private Mock<IRunnerCertificateManager> _cert;
private Mock<IConfigurationStore> _config;
private Mock<IExtensionManager> _extensions;
private Mock<IStepsRunner> _stepRunner;
private Mock<IJobExtension> _jobExtension;
private Mock<IPagingLogger> _logger;
private Mock<ITempDirectoryManager> _temp;
private Mock<IDiagnosticLogManager> _diagnosticLogManager;
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
{
var hc = new TestHostContext(this, testName);
_jobEc = new Runner.Worker.ExecutionContext();
_config = new Mock<IConfigurationStore>();
_extensions = new Mock<IExtensionManager>();
_jobExtension = new Mock<IJobExtension>();
_jobServer = new Mock<IJobServer>();
_jobServerQueue = new Mock<IJobServerQueue>();
_proxyConfig = new Mock<IRunnerWebProxy>();
_cert = new Mock<IRunnerCertificateManager>();
_stepRunner = new Mock<IStepsRunner>();
_logger = new Mock<IPagingLogger>();
_temp = new Mock<ITempDirectoryManager>();
_diagnosticLogManager = new Mock<IDiagnosticLogManager>();
if (_tokenSource != null)
{
_tokenSource.Dispose();
_tokenSource = null;
}
_tokenSource = new CancellationTokenSource();
var expressionManager = new ExpressionManager();
expressionManager.Initialize(hc);
hc.SetSingleton<IExpressionManager>(expressionManager);
_jobRunner = new JobRunner();
_jobRunner.Initialize(hc);
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
TimelineReference timeline = new Timeline(Guid.NewGuid());
JobEnvironment environment = new JobEnvironment();
environment.Variables[Constants.Variables.System.Culture] = "en-US";
environment.SystemConnection = new ServiceEndpoint()
{
Name = WellKnownServiceEndpointNames.SystemVssConnection,
Url = new Uri("https://test.visualstudio.com"),
Authorization = new EndpointAuthorization()
{
Scheme = "Test",
}
};
environment.SystemConnection.Authorization.Parameters["AccessToken"] = "token";
List<TaskInstance> tasks = new List<TaskInstance>();
Guid JobId = Guid.NewGuid();
_message = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, testName, testName, environment, tasks));
_message.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Id = "github",
Version = "sha1"
});
_message.ContextData.Add("github", new Pipelines.ContextData.DictionaryContextData());
_initResult.Clear();
_jobExtension.Setup(x => x.InitializeJob(It.IsAny<IExecutionContext>(), It.IsAny<Pipelines.AgentJobRequestMessage>())).
Returns(Task.FromResult(_initResult));
_proxyConfig.Setup(x => x.ProxyAddress)
.Returns(string.Empty);
var settings = new RunnerSettings
{
AgentId = 1,
AgentName = "agent1",
ServerUrl = "https://test.visualstudio.com",
WorkFolder = "_work",
};
_config.Setup(x => x.GetSettings())
.Returns(settings);
_logger.Setup(x => x.Setup(It.IsAny<Guid>(), It.IsAny<Guid>()));
hc.SetSingleton(_config.Object);
hc.SetSingleton(_jobServer.Object);
hc.SetSingleton(_jobServerQueue.Object);
hc.SetSingleton(_proxyConfig.Object);
hc.SetSingleton(_cert.Object);
hc.SetSingleton(_stepRunner.Object);
hc.SetSingleton(_extensions.Object);
hc.SetSingleton(_temp.Object);
hc.SetSingleton(_diagnosticLogManager.Object);
hc.EnqueueInstance<IExecutionContext>(_jobEc);
hc.EnqueueInstance<IPagingLogger>(_logger.Object);
hc.EnqueueInstance<IJobExtension>(_jobExtension.Object);
return hc;
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task JobExtensionInitializeFailure()
{
using (TestHostContext hc = CreateTestContext())
{
_jobExtension.Setup(x => x.InitializeJob(It.IsAny<IExecutionContext>(), It.IsAny<Pipelines.AgentJobRequestMessage>()))
.Throws(new Exception());
await _jobRunner.RunAsync(_message, _tokenSource.Token);
Assert.Equal(TaskResult.Failed, _jobEc.Result);
_stepRunner.Verify(x => x.RunAsync(It.IsAny<IExecutionContext>()), Times.Never);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task JobExtensionInitializeCancelled()
{
using (TestHostContext hc = CreateTestContext())
{
_jobExtension.Setup(x => x.InitializeJob(It.IsAny<IExecutionContext>(), It.IsAny<Pipelines.AgentJobRequestMessage>()))
.Throws(new OperationCanceledException());
_tokenSource.Cancel();
await _jobRunner.RunAsync(_message, _tokenSource.Token);
Assert.Equal(TaskResult.Canceled, _jobEc.Result);
_stepRunner.Verify(x => x.RunAsync(It.IsAny<IExecutionContext>()), Times.Never);
}
}
// TODO: Move these tests over to JobExtensionL0.cs
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task UploadDiganosticLogIfEnvironmentVariableSet()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// _message.Variables[Constants.Variables.Actions.RunnerDebug] = "true";
// await _jobRunner.RunAsync(_message, _tokenSource.Token);
// _diagnosticLogManager.Verify(x =>
// x.UploadDiagnosticLogsAsync(
// It.IsAny<IExecutionContext>(),
// It.IsAny<Pipelines.AgentJobRequestMessage>(),
// It.IsAny<DateTime>()),
// Times.Once);
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task DontUploadDiagnosticLogIfEnvironmentVariableFalse()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// _message.Variables[Constants.Variables.Actions.RunnerDebug] = "false";
// await _jobRunner.RunAsync(_message, _tokenSource.Token);
// _diagnosticLogManager.Verify(x =>
// x.UploadDiagnosticLogsAsync(
// It.IsAny<IExecutionContext>(),
// It.IsAny<Pipelines.AgentJobRequestMessage>(),
// It.IsAny<DateTime>()),
// Times.Never);
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task DontUploadDiagnosticLogIfEnvironmentVariableMissing()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// await _jobRunner.RunAsync(_message, _tokenSource.Token);
// _diagnosticLogManager.Verify(x =>
// x.UploadDiagnosticLogsAsync(
// It.IsAny<IExecutionContext>(),
// It.IsAny<Pipelines.AgentJobRequestMessage>(),
// It.IsAny<DateTime>()),
// Times.Never);
// }
// }
}
}

View File

@@ -0,0 +1,728 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Handlers;
using Moq;
using Xunit;
using DTWebApi = GitHub.DistributedTask.WebApi;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class OutputManagerL0
{
private Mock<IExecutionContext> _executionContext;
private Mock<IActionCommandManager> _commandManager;
private Variables _variables;
private OnMatcherChanged _onMatcherChanged;
private List<Tuple<DTWebApi.Issue, string>> _issues;
private List<string> _messages;
private List<string> _commands;
private OutputManager _outputManager;
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void AddMatcher_Clobber()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR: (.+)",
Message = 1,
},
},
},
new IssueMatcherConfig
{
Owner = "my-matcher-2",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "NOT GOOD: (.+)",
Message = 1,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("ERROR: message 1");
Process("NOT GOOD: message 2");
Add(new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR: (.+) END MESSAGE",
Message = 1,
},
},
});
Process("ERROR: message 3 END MESSAGE");
Process("ERROR: message 4");
Process("NOT GOOD: message 5");
Assert.Equal(4, _issues.Count);
Assert.Equal("message 1", _issues[0].Item1.Message);
Assert.Equal("message 2", _issues[1].Item1.Message);
Assert.Equal("message 3", _issues[2].Item1.Message);
Assert.Equal("message 5", _issues[3].Item1.Message);
Assert.Equal(0, _commands.Count);
Assert.Equal(1, _messages.Count);
Assert.Equal("ERROR: message 4", _messages[0]);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void AddMatcher_Prepend()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR: (.+)",
Message = 1,
},
},
},
new IssueMatcherConfig
{
Owner = "my-matcher-2",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "NOT GOOD: (.+)",
Message = 1,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("ERROR: message 1");
Process("NOT GOOD: message 2");
Add(new IssueMatcherConfig
{
Owner = "new-matcher",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR: (.+) END MESSAGE",
Message = 1,
},
},
});
Process("ERROR: message 3 END MESSAGE");
Process("ERROR: message 4");
Process("NOT GOOD: message 5");
Assert.Equal(5, _issues.Count);
Assert.Equal("message 1", _issues[0].Item1.Message);
Assert.Equal("message 2", _issues[1].Item1.Message);
Assert.Equal("message 3", _issues[2].Item1.Message);
Assert.Equal("message 4", _issues[3].Item1.Message);
Assert.Equal("message 5", _issues[4].Item1.Message);
Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Code()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = @"(.*): (.+)",
Code = 1,
Message = 2,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("BAD: real bad");
Process(": not working");
Assert.Equal(2, _issues.Count);
Assert.Equal("real bad", _issues[0].Item1.Message);
Assert.Equal("BAD", _issues[0].Item1.Data["code"]);
Assert.Equal("not working", _issues[1].Item1.Message);
Assert.False(_issues[1].Item1.Data.ContainsKey("code"));
Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void DoesNotResetMatchingMatcher()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "Start: .+",
},
new IssuePatternConfig
{
Pattern = "Error: (.+)",
Message = 1,
Loop = true,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("Start: hello");
Process("Error: it broke");
Process("Error: oh no");
Process("Error: not good");
Process("regular message 1");
Process("Start: hello again");
Process("Error: it broke again");
Process("Error: real bad");
Process("regular message 2");
Assert.Equal(5, _issues.Count);
Assert.Equal("it broke", _issues[0].Item1.Message);
Assert.Equal("oh no", _issues[1].Item1.Message);
Assert.Equal("not good", _issues[2].Item1.Message);
Assert.Equal("it broke again", _issues[3].Item1.Message);
Assert.Equal("real bad", _issues[4].Item1.Message);
Assert.Equal(0, _commands.Count);
Assert.Equal(4, _messages.Count);
Assert.Equal("Start: hello", _messages[0]);
Assert.Equal("regular message 1", _messages[1]);
Assert.Equal("Start: hello again", _messages[2]);
Assert.Equal("regular message 2", _messages[3]);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void InitialMatchers()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR: (.+)",
Message = 1,
},
},
},
new IssueMatcherConfig
{
Owner = "my-matcher-2",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "NOT GOOD: (.+)",
Message = 1,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("ERROR: it is broken");
Process("NOT GOOD: that did not work");
Assert.Equal(2, _issues.Count);
Assert.Equal("it is broken", _issues[0].Item1.Message);
Assert.Equal("that did not work", _issues[1].Item1.Message);
Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void LineColumn()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = @"\((.+),(.+)\): (.+)",
Line = 1,
Column = 2,
Message = 3,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("(12,34): real bad");
Process("(12,thirty-four): it is broken");
Process("(twelve,34): not working");
Assert.Equal(3, _issues.Count);
Assert.Equal("real bad", _issues[0].Item1.Message);
Assert.Equal("12", _issues[0].Item1.Data["line"]);
Assert.Equal("34", _issues[0].Item1.Data["col"]);
Assert.Equal("it is broken", _issues[1].Item1.Message);
Assert.Equal("12", _issues[1].Item1.Data["line"]);
Assert.False(_issues[1].Item1.Data.ContainsKey("col"));
Assert.Equal("not working", _issues[2].Item1.Message);
Assert.False(_issues[2].Item1.Data.ContainsKey("line"));
Assert.Equal("34", _issues[2].Item1.Data["col"]);
Assert.Equal(0, _commands.Count);
Assert.Equal(2, _messages.Count);
Assert.Equal("##[debug]Unable to parse column number 'thirty-four'", _messages[0]);
Assert.Equal("##[debug]Unable to parse line number 'twelve'", _messages[1]);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void ProcessCommand()
{
using (Setup())
using (_outputManager)
{
Add(new IssueMatcherConfig
{
Owner = "my-matcher",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR: (.+)",
Message = 1,
},
},
});
Process("this line is an ERROR: it is broken");
Process("##[some-command]this line is a command even though it contains ERROR: not working");
Process("this line is a command too ##[some-command]even though it contains ERROR: not working again");
Process("##[not-command]this line is an ERROR: it is broken again");
Assert.Equal(2, _issues.Count);
Assert.Equal("it is broken", _issues[0].Item1.Message);
Assert.Equal("it is broken again", _issues[1].Item1.Message);
Assert.Equal(2, _commands.Count);
Assert.Equal("##[some-command]this line is a command even though it contains ERROR: not working", _commands[0]);
Assert.Equal("this line is a command too ##[some-command]even though it contains ERROR: not working again", _commands[1]);
Assert.Equal(0, _messages.Count);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void RemoveColorCodes()
{
using (Setup())
using (_outputManager)
{
Add(new IssueMatcherConfig
{
Owner = "my-matcher",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "^the error: (.+)$",
Message = 1,
},
},
});
Process("the error: \033[31mred, \033[1;31mbright red, \033[mreset");
Assert.Equal(1, _issues.Count);
Assert.Equal("red, bright red, reset", _issues[0].Item1.Message);
Assert.Equal("the error: red, bright red, reset", _issues[0].Item2);
Assert.Equal(0, _commands.Count);
Assert.Equal(0, _messages.Count);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void RemoveMatcher()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR: (.+)",
Message = 1,
},
},
},
new IssueMatcherConfig
{
Owner = "my-matcher-2",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "NOT GOOD: (.+)",
Message = 1,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("ERROR: message 1");
Process("NOT GOOD: message 2");
Remove("my-matcher-1");
Process("ERROR: message 3");
Process("NOT GOOD: message 4");
Assert.Equal(3, _issues.Count);
Assert.Equal("message 1", _issues[0].Item1.Message);
Assert.Equal("message 2", _issues[1].Item1.Message);
Assert.Equal("message 4", _issues[2].Item1.Message);
Assert.Equal(0, _commands.Count);
Assert.Equal(1, _messages.Count);
Assert.Equal("ERROR: message 3", _messages[0]);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void ResetsOtherMatchers()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "Matches both line 1: .+",
},
new IssuePatternConfig
{
Pattern = "Matches 1 only line 2: (.+)",
Message = 1,
},
},
},
new IssueMatcherConfig
{
Owner = "my-matcher-2",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "Matches both line 1: (.+)",
},
new IssuePatternConfig
{
Pattern = "(.+)",
Message = 1,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("Matches both line 1: hello");
Process("Matches 1 only line 2: it broke");
Process("regular message 1");
Process("regular message 2");
Process("Matches both line 1: hello again");
Process("oh no, another error");
Assert.Equal(2, _issues.Count);
Assert.Equal("it broke", _issues[0].Item1.Message);
Assert.Equal("oh no, another error", _issues[1].Item1.Message);
Assert.Equal(0, _commands.Count);
Assert.Equal(4, _messages.Count);
Assert.Equal("Matches both line 1: hello", _messages[0]);
Assert.Equal("regular message 1", _messages[1]);
Assert.Equal("regular message 2", _messages[2]);
Assert.Equal("Matches both line 1: hello again", _messages[3]);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Severity()
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "my-matcher-1",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "(.*): (.+)",
Severity = 1,
Message = 2,
},
},
},
new IssueMatcherConfig
{
Owner = "my-matcher-2",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = "ERROR! (.+)",
Message = 1,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("ERRor: real bad");
Process("WARNing: not great");
Process("info: hey");
Process(": not working");
Process("ERROR! uh oh");
Assert.Equal(4, _issues.Count);
Assert.Equal("real bad", _issues[0].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Error, _issues[0].Item1.Type);
Assert.Equal("not great", _issues[1].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Warning, _issues[1].Item1.Type);
Assert.Equal("not working", _issues[2].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Error, _issues[2].Item1.Type);
Assert.Equal("uh oh", _issues[3].Item1.Message);
Assert.Equal(DTWebApi.IssueType.Error, _issues[3].Item1.Type);
Assert.Equal(0, _commands.Count);
Assert.Equal(2, _messages.Count);
Assert.StartsWith("##[debug]Skipped", _messages[0]);
Assert.Contains("'info'", _messages[0]);
Assert.Equal("info: hey", _messages[1]);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Timeout()
{
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_ISSUE_MATCHER_TIMEOUT", "0:0:0.01");
var matchers = new IssueMatchersConfig
{
Matchers =
{
new IssueMatcherConfig
{
Owner = "email",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$",
Message = 0,
},
},
},
new IssueMatcherConfig
{
Owner = "err",
Patterns = new[]
{
new IssuePatternConfig
{
Pattern = @"ERR: (.+)",
Message = 1,
},
},
},
},
};
using (Setup(matchers: matchers))
using (_outputManager)
{
Process("john.doe@contoso.com");
Process("t@t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.c%20");
Process("jane.doe@contoso.com");
Process("ERR: this error");
Assert.Equal(3, _issues.Count);
Assert.Equal("john.doe@contoso.com", _issues[0].Item1.Message);
Assert.Contains("Removing issue matcher 'email'", _issues[1].Item1.Message);
Assert.Equal("this error", _issues[2].Item1.Message);
Assert.Equal(0, _commands.Count);
Assert.Equal(2, _messages.Where(x => x.StartsWith("##[debug]Timeout processing issue matcher")).Count());
Assert.Equal(1, _messages.Where(x => x.Equals("t@t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.c%20")).Count());
Assert.Equal(1, _messages.Where(x => x.StartsWith("jane.doe@contoso.com")).Count());
}
}
// todo: roots file against fromPath
// todo: roots file against system.defaultWorkingDirectory
// todo: matches repository
// todo: checks file exists
private TestHostContext Setup(
[CallerMemberName] string name = "",
IssueMatchersConfig matchers = null)
{
matchers?.Validate();
_onMatcherChanged = null;
_issues = new List<Tuple<DTWebApi.Issue, string>>();
_messages = new List<string>();
_commands = new List<string>();
var hostContext = new TestHostContext(this, name);
_variables = new Variables(hostContext, new Dictionary<string, DTWebApi.VariableValue>());
_executionContext = new Mock<IExecutionContext>();
_executionContext.Setup(x => x.WriteDebug)
.Returns(true);
_executionContext.Setup(x => x.Variables)
.Returns(_variables);
_executionContext.Setup(x => x.GetMatchers())
.Returns(matchers?.Matchers ?? new List<IssueMatcherConfig>());
_executionContext.Setup(x => x.Add(It.IsAny<OnMatcherChanged>()))
.Callback((OnMatcherChanged handler) =>
{
_onMatcherChanged = handler;
});
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
.Callback((DTWebApi.Issue issue, string logMessage) =>
{
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
});
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Callback((string tag, string message) =>
{
_messages.Add($"{tag}{message}");
hostContext.GetTrace().Info($"{tag}{message}");
});
_commandManager = new Mock<IActionCommandManager>();
_commandManager.Setup(x => x.TryProcessCommand(It.IsAny<IExecutionContext>(), It.IsAny<string>()))
.Returns((IExecutionContext executionContext, string line) =>
{
if (line.IndexOf("##[some-command]") >= 0)
{
_commands.Add(line);
return true;
}
return false;
});
_outputManager = new OutputManager(_executionContext.Object, _commandManager.Object);
return hostContext;
}
private void Add(IssueMatcherConfig matcher)
{
var matchers = new IssueMatchersConfig
{
Matchers =
{
matcher,
},
};
matchers.Validate();
_onMatcherChanged(null, new MatcherChangedEventArgs(matcher));
}
private void Remove(string owner)
{
var matcher = new IssueMatcherConfig { Owner = owner };
_onMatcherChanged(null, new MatcherChangedEventArgs(matcher));
}
private void Process(string line)
{
_outputManager.OnDataReceived(null, new ProcessDataReceivedEventArgs(line));
}
}
}

View File

@@ -0,0 +1,229 @@
using Pipelines = GitHub.DistributedTask.Pipelines;
using GitHub.Runner.Worker;
using Moq;
using System.IO;
using System.Runtime.CompilerServices;
using Xunit;
using System;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class PipelineDirectoryManagerL0
{
private PipelineDirectoryManager _pipelineDirectoryManager;
private Mock<IExecutionContext> _ec;
private Pipelines.WorkspaceOptions _workspaceOptions;
private TrackingConfig _existingConfig;
private TrackingConfig _newConfig;
private string _trackingFile;
private Mock<ITrackingManager> _trackingManager;
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void CreatesPipelineDirectories()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(default(TrackingConfig));
_trackingManager.Setup(x => x.Create(_ec.Object, _trackingFile)).Returns(new TrackingConfig(_ec.Object));
// Act.
_newConfig = _pipelineDirectoryManager.PrepareDirectory(_ec.Object, _workspaceOptions);
// Assert.
_trackingManager.Verify(x => x.Create(_ec.Object, _trackingFile));
Assert.True(Directory.Exists(Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _newConfig.WorkspaceDirectory)));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void DeletesResourceDirectoryWhenCleanIsResources()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_existingConfig = new TrackingConfig(_ec.Object);
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(_existingConfig);
_workspaceOptions.Clean = Pipelines.PipelineConstants.WorkspaceCleanOptions.Resources;
string workspaceDirectory = Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _existingConfig.WorkspaceDirectory);
string sourceFile = Path.Combine(workspaceDirectory, "some subdirectory", "some source file");
Directory.CreateDirectory(Path.GetDirectoryName(sourceFile));
File.WriteAllText(path: sourceFile, contents: "some source contents");
// Act.
_pipelineDirectoryManager.PrepareDirectory(_ec.Object, _workspaceOptions);
// Assert.
Assert.True(Directory.Exists(workspaceDirectory));
Assert.Equal(0, Directory.GetFileSystemEntries(workspaceDirectory, "*", SearchOption.AllDirectories).Length);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void DeletesNonResourceDirectoryWhenCleanIsOutputs()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_existingConfig = new TrackingConfig(_ec.Object);
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(_existingConfig);
_workspaceOptions.Clean = Pipelines.PipelineConstants.WorkspaceCleanOptions.Outputs;
string nonResourceDirectory = Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _existingConfig.PipelineDirectory, "somedir");
string sourceFile = Path.Combine(nonResourceDirectory, "some subdirectory", "some source file");
Directory.CreateDirectory(Path.GetDirectoryName(sourceFile));
File.WriteAllText(path: sourceFile, contents: "some source contents");
// Act.
_pipelineDirectoryManager.PrepareDirectory(_ec.Object, _workspaceOptions);
// Assert.
Assert.False(Directory.Exists(nonResourceDirectory));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void RecreatesPipelinesDirectoryWhenCleanIsAll()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_existingConfig = new TrackingConfig(_ec.Object);
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(_existingConfig);
_workspaceOptions.Clean = Pipelines.PipelineConstants.WorkspaceCleanOptions.All;
string pipelinesDirectory = Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _existingConfig.PipelineDirectory);
string looseFile = Path.Combine(pipelinesDirectory, "some loose directory", "some loose file");
Directory.CreateDirectory(Path.GetDirectoryName(looseFile));
File.WriteAllText(path: looseFile, contents: "some loose file contents");
// Act.
_pipelineDirectoryManager.PrepareDirectory(_ec.Object, _workspaceOptions);
// Assert.
Assert.Equal(1, Directory.GetFileSystemEntries(pipelinesDirectory, "*", SearchOption.AllDirectories).Length);
Assert.True(Directory.Exists(Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _existingConfig.WorkspaceDirectory)));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void UpdatesExistingConfig()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_existingConfig = new TrackingConfig(_ec.Object);
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(_existingConfig);
// Act.
_pipelineDirectoryManager.PrepareDirectory(_ec.Object, _workspaceOptions);
// Assert.
_trackingManager.Verify(x => x.LoadIfExists(_ec.Object, _trackingFile));
_trackingManager.Verify(x => x.Update(_ec.Object, _existingConfig, _trackingFile));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void UpdatesRepositoryDirectoryWorkspaceRepo()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_existingConfig = new TrackingConfig(_ec.Object);
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(_existingConfig);
// Act.
_pipelineDirectoryManager.UpdateRepositoryDirectory(_ec.Object, "actions/runner", Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _existingConfig.PipelineDirectory, "my_new_path"), true);
// Assert.
_trackingManager.Verify(x => x.LoadIfExists(_ec.Object, _trackingFile));
_trackingManager.Verify(x => x.Update(_ec.Object, _existingConfig, _trackingFile));
_ec.Verify(x => x.SetGitHubContext("workspace", Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _existingConfig.PipelineDirectory, "my_new_path")));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void UpdatesRepositoryDirectoryNoneWorkspaceRepo()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_existingConfig = new TrackingConfig(_ec.Object);
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(_existingConfig);
// Act.
_pipelineDirectoryManager.UpdateRepositoryDirectory(_ec.Object, "actions/notrunner", Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), _existingConfig.PipelineDirectory, "notrunner"), false);
// Assert.
_trackingManager.Verify(x => x.LoadIfExists(_ec.Object, _trackingFile));
_trackingManager.Verify(x => x.Update(_ec.Object, _existingConfig, _trackingFile));
_ec.Verify(x => x.SetGitHubContext("workspace", It.IsAny<string>()), Times.Never);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void UpdatesRepositoryDirectoryThrowOnInvalidPath()
{
// Arrange.
using (TestHostContext hc = Setup())
{
_existingConfig = new TrackingConfig(_ec.Object);
_trackingManager.Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)).Returns(_existingConfig);
// Act.
Assert.ThrowsAny<ArgumentException>(()=> _pipelineDirectoryManager.UpdateRepositoryDirectory(_ec.Object, "actions/notrunner", Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), "not_under_pipeline_directory"), false));
}
}
private TestHostContext Setup(
[CallerMemberName] string name = "")
{
// Setup the host context.
TestHostContext hc = new TestHostContext(this, name);
// Setup the execution context.
_ec = new Mock<IExecutionContext>();
GitHubContext githubContext = new GitHubContext();
_ec.Setup(x => x.GetGitHubContext("repository")).Returns("actions/runner");
// Store the expected tracking file path.
_trackingFile = Path.Combine(
hc.GetDirectory(WellKnownDirectory.Work),
Constants.Pipeline.Path.PipelineMappingDirectory,
"actions/runner",
Constants.Pipeline.Path.TrackingConfigFile);
_workspaceOptions = new Pipelines.WorkspaceOptions();
// Setup the tracking manager.
_trackingManager = new Mock<ITrackingManager>();
hc.SetSingleton<ITrackingManager>(_trackingManager.Object);
// Setup the build directory manager.
_pipelineDirectoryManager = new PipelineDirectoryManager();
_pipelineDirectoryManager.Initialize(hc);
return hc;
}
}
}

View File

@@ -0,0 +1,448 @@
// using GitHub.DistributedTask.WebApi;
// using GitHub.Runner.Worker;
// using Moq;
// using System;
// using System.Collections.Generic;
// using System.Globalization;
// using System.Linq;
// using System.Runtime.CompilerServices;
// using System.Threading.Tasks;
// using Xunit;
// using GitHub.DistributedTask.Expressions2;
// using GitHub.DistributedTask.Pipelines.ContextData;
// namespace GitHub.Runner.Common.Tests.Worker
// {
// public sealed class StepsRunnerL0
// {
// private Mock<IExecutionContext> _ec;
// private StepsRunner _stepsRunner;
// private Variables _variables;
// private Dictionary<string, PipelineContextData> _contexts;
// private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
// {
// var hc = new TestHostContext(this, testName);
// var expressionManager = new ExpressionManager();
// expressionManager.Initialize(hc);
// hc.SetSingleton<IExpressionManager>(expressionManager);
// Dictionary<string, VariableValue> variablesToCopy = new Dictionary<string, VariableValue>();
// variablesToCopy.Add(Constants.Variables.Agent.RetainDefaultEncoding, new VariableValue("true", false));
// _variables = new Variables(
// hostContext: hc,
// copy: variablesToCopy);
// _ec = new Mock<IExecutionContext>();
// _ec.SetupAllProperties();
// _ec.Setup(x => x.Variables).Returns(_variables);
// _contexts = new Dictionary<string, PipelineContextData>();
// _contexts["github"] = new DictionaryContextData();
// _contexts["runner"] = new DictionaryContextData();
// _contexts["actions"] = new DictionaryContextData();
// _ec.Setup(x => x.ExpressionValues).Returns(_contexts);
// var _stepContext = new StepsContext();
// _ec.Setup(x => x.StepsContext).Returns(_stepContext);
// _stepsRunner = new StepsRunner();
// _stepsRunner.Initialize(hc);
// return hc;
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task RunNormalStepsAllStepPass()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// // Arrange.
// var variableSets = new[]
// {
// new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.SucceededOrFailed) },
// new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) }
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Select(x => x.Object).ToList());
// // Assert.
// Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
// Assert.Equal(2, variableSet.Length);
// variableSet[0].Verify(x => x.RunAsync());
// variableSet[1].Verify(x => x.RunAsync());
// }
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task RunNormalStepsContinueOnError()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// // Arrange.
// var variableSets = new[]
// {
// new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, true), CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, true), CreateStep(TaskResult.Succeeded, ExpressionManager.SucceededOrFailed) },
// new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, true), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, true), CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, true) },
// new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, true), CreateStep(TaskResult.Failed, ExpressionManager.SucceededOrFailed, true) },
// new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, true), CreateStep(TaskResult.Failed, ExpressionManager.Always, true) }
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Select(x => x.Object).ToList());
// // Assert.
// Assert.Equal(TaskResult.SucceededWithIssues, _ec.Object.Result);
// Assert.Equal(2, variableSet.Length);
// variableSet[0].Verify(x => x.RunAsync());
// variableSet[1].Verify(x => x.RunAsync());
// }
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task RunsAfterFailureBasedOnCondition()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// // Arrange.
// var variableSets = new[]
// {
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// Expected = false,
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.SucceededOrFailed) },
// Expected = true,
// },
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Steps.Select(x => x.Object).ToList());
// // Assert.
// Assert.Equal(TaskResult.Failed, _ec.Object.Result ?? TaskResult.Succeeded);
// Assert.Equal(2, variableSet.Steps.Length);
// variableSet.Steps[0].Verify(x => x.RunAsync());
// variableSet.Steps[1].Verify(x => x.RunAsync(), variableSet.Expected ? Times.Once() : Times.Never());
// }
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task RunsAlwaysSteps()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// // Arrange.
// var variableSets = new[]
// {
// new
// {
// Steps = new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// Expected = TaskResult.Succeeded,
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// Expected = TaskResult.Failed,
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// Expected = TaskResult.Failed,
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Failed, ExpressionManager.Always) },
// Expected = TaskResult.Failed,
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Failed, ExpressionManager.Always, true) },
// Expected = TaskResult.SucceededWithIssues,
// },
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Steps.Select(x => x.Object).ToList());
// // Assert.
// Assert.Equal(variableSet.Expected, _ec.Object.Result ?? TaskResult.Succeeded);
// Assert.Equal(2, variableSet.Steps.Length);
// variableSet.Steps[0].Verify(x => x.RunAsync());
// variableSet.Steps[1].Verify(x => x.RunAsync());
// }
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task SetsJobResultCorrectly()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// // Arrange.
// var variableSets = new[]
// {
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// Expected = TaskResult.Failed
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.SucceededOrFailed) },
// Expected = TaskResult.Failed
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// Expected = TaskResult.Failed
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, continueOnError: true), CreateStep(TaskResult.Failed, ExpressionManager.Succeeded) },
// Expected = TaskResult.Failed
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, continueOnError: true), CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// Expected = TaskResult.SucceededWithIssues
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, continueOnError: true), CreateStep(TaskResult.Failed, ExpressionManager.Succeeded, continueOnError: true) },
// Expected = TaskResult.SucceededWithIssues
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.SucceededOrFailed) },
// Expected = TaskResult.Succeeded
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Failed, ExpressionManager.Succeeded) },
// Expected = TaskResult.Failed
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.SucceededWithIssues, ExpressionManager.Succeeded) },
// Expected = TaskResult.SucceededWithIssues
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.SucceededWithIssues, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// Expected = TaskResult.SucceededWithIssues
// },
// new
// {
// Steps = new[] { CreateStep(TaskResult.SucceededWithIssues, ExpressionManager.Succeeded), CreateStep(TaskResult.Failed, ExpressionManager.Succeeded) },
// Expected = TaskResult.Failed
// },
// // Abandoned
// // Canceled
// // Failed
// // Skipped
// // Succeeded
// // SucceededWithIssues
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Steps.Select(x => x.Object).ToList());
// // Assert.
// Assert.True(
// variableSet.Expected == (_ec.Object.Result ?? TaskResult.Succeeded),
// $"Expected '{variableSet.Expected}'. Actual '{_ec.Object.Result}'. Steps: {FormatSteps(variableSet.Steps)}");
// }
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task SkipsAfterFailureOnlyBaseOnCondition()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// // Arrange.
// var variableSets = new[]
// {
// new
// {
// Step = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// Expected = false
// },
// new
// {
// Step = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.SucceededOrFailed) },
// Expected = true
// },
// new
// {
// Step = new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// Expected = true
// }
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Step.Select(x => x.Object).ToList());
// // Assert.
// Assert.Equal(2, variableSet.Step.Length);
// variableSet.Step[0].Verify(x => x.RunAsync());
// variableSet.Step[1].Verify(x => x.RunAsync(), variableSet.Expected ? Times.Once() : Times.Never());
// }
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task AlwaysMeansAlways()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// // Arrange.
// var variableSets = new[]
// {
// new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// new[] { CreateStep(TaskResult.SucceededWithIssues, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// new[] { CreateStep(TaskResult.Failed, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) },
// new[] { CreateStep(TaskResult.Canceled, ExpressionManager.Succeeded), CreateStep(TaskResult.Succeeded, ExpressionManager.Always) }
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Select(x => x.Object).ToList());
// // Assert.
// Assert.Equal(2, variableSet.Length);
// variableSet[0].Verify(x => x.RunAsync());
// variableSet[1].Verify(x => x.RunAsync(), Times.Once());
// }
// }
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public async Task TreatsConditionErrorAsFailure()
// {
// using (TestHostContext hc = CreateTestContext())
// {
// var expressionManager = new Mock<IExpressionManager>();
// expressionManager.Object.Initialize(hc);
// hc.SetSingleton<IExpressionManager>(expressionManager.Object);
// expressionManager.Setup(x => x.Evaluate(It.IsAny<IExecutionContext>(), It.IsAny<IExpressionNode>(), It.IsAny<bool>())).Throws(new Exception());
// // Arrange.
// var variableSets = new[]
// {
// new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// new[] { CreateStep(TaskResult.Succeeded, ExpressionManager.Succeeded) },
// };
// foreach (var variableSet in variableSets)
// {
// _ec.Object.Result = null;
// // Act.
// await _stepsRunner.RunAsync(
// jobContext: _ec.Object,
// steps: variableSet.Select(x => x.Object).ToList());
// // Assert.
// Assert.Equal(TaskResult.Failed, _ec.Object.Result ?? TaskResult.Succeeded);
// }
// }
// }
// private Mock<IStep> CreateStep(TaskResult result, IExpressionNode condition, Boolean continueOnError = false)
// {
// // Setup the step.
// var step = new Mock<IStep>();
// step.Setup(x => x.Condition).Returns(condition);
// step.Setup(x => x.ContinueOnError).Returns(continueOnError);
// step.Setup(x => x.Enabled).Returns(true);
// step.Setup(x => x.RunAsync()).Returns(Task.CompletedTask);
// // Setup the step execution context.
// var stepContext = new Mock<IExecutionContext>();
// stepContext.SetupAllProperties();
// stepContext.Setup(x => x.Variables).Returns(_variables);
// stepContext.Setup(x => x.ExpressionValues).Returns(_contexts);
// stepContext.Setup(x => x.Complete(It.IsAny<TaskResult?>(), It.IsAny<string>(), It.IsAny<string>()))
// .Callback((TaskResult? r, string currentOperation, string resultCode) =>
// {
// if (r != null)
// {
// stepContext.Object.Result = r;
// }
// });
// stepContext.Object.Result = result;
// step.Setup(x => x.ExecutionContext).Returns(stepContext.Object);
// return step;
// }
// private string FormatSteps(IEnumerable<Mock<IStep>> steps)
// {
// return String.Join(
// " ; ",
// steps.Select(x => String.Format(
// CultureInfo.InvariantCulture,
// "Returns={0},Condition=[{1}],ContinueOnError={2},Enabled={3}",
// x.Object.ExecutionContext.Result,
// x.Object.Condition,
// x.Object.ContinueOnError,
// x.Object.Enabled)));
// }
// }
// }

View File

@@ -0,0 +1,181 @@
// using System;
// using System.Collections.Generic;
// using System.Runtime.CompilerServices;
// using GitHub.DistributedTask.WebApi;
// using GitHub.Runner.Worker;
// using Moq;
// using Xunit;
// namespace GitHub.Runner.Common.Tests.Worker
// {
// public sealed class TaskCommandExtensionL0
// {
// private TestHostContext _hc;
// private Mock<IExecutionContext> _ec;
// private ServiceEndpoint _endpoint;
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointAuthParameter()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// commandExtension.Initialize(_hc);
// var cmd = new Command("task", "setEndpoint");
// cmd.Data = "blah";
// cmd.Properties.Add("field", "authParameter");
// cmd.Properties.Add("id", Guid.Empty.ToString());
// cmd.Properties.Add("key", "test");
// commandExtension.ProcessCommand(_ec.Object, cmd);
// Assert.Equal(_endpoint.Authorization.Parameters["test"], "blah");
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointDataParameter()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// cmd.Data = "blah";
// cmd.Properties.Add("field", "dataParameter");
// cmd.Properties.Add("id", Guid.Empty.ToString());
// cmd.Properties.Add("key", "test");
// commandExtension.ProcessCommand(_ec.Object, cmd);
// Assert.Equal(_endpoint.Data["test"], "blah");
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointUrlParameter()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// cmd.Data = "http://blah/";
// cmd.Properties.Add("field", "url");
// cmd.Properties.Add("id", Guid.Empty.ToString());
// commandExtension.ProcessCommand(_ec.Object, cmd);
// Assert.Equal(_endpoint.Url.ToString(), cmd.Data);
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointWithoutValue()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// Assert.Throws<Exception>(() => commandExtension.ProcessCommand(_ec.Object, cmd));
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointWithoutEndpointField()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// Assert.Throws<Exception>(() => commandExtension.ProcessCommand(_ec.Object, cmd));
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointInvalidEndpointField()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// cmd.Properties.Add("field", "blah");
// Assert.Throws<Exception>(() => commandExtension.ProcessCommand(_ec.Object, cmd));
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointWithoutEndpointId()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// cmd.Properties.Add("field", "url");
// Assert.Throws<Exception>(() => commandExtension.ProcessCommand(_ec.Object, cmd));
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointInvalidEndpointId()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// cmd.Properties.Add("field", "url");
// cmd.Properties.Add("id", "blah");
// Assert.Throws<Exception>(() => commandExtension.ProcessCommand(_ec.Object, cmd));
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointIdWithoutEndpointKey()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// cmd.Properties.Add("field", "authParameter");
// cmd.Properties.Add("id", Guid.Empty.ToString());
// Assert.Throws<Exception>(() => commandExtension.ProcessCommand(_ec.Object, cmd));
// }
// [Fact]
// [Trait("Level", "L0")]
// [Trait("Category", "Worker")]
// public void SetEndpointUrlWithInvalidValue()
// {
// SetupMocks();
// TaskCommandExtension commandExtension = new TaskCommandExtension();
// var cmd = new Command("task", "setEndpoint");
// cmd.Data = "blah";
// cmd.Properties.Add("field", "url");
// cmd.Properties.Add("id", Guid.Empty.ToString());
// Assert.Throws<Exception>(() => commandExtension.ProcessCommand(_ec.Object, cmd));
// }
// private void SetupMocks([CallerMemberName] string name = "")
// {
// _hc = new TestHostContext(this, name);
// _ec = new Mock<IExecutionContext>();
// _endpoint = new ServiceEndpoint()
// {
// Id = Guid.Empty,
// Url = new Uri("https://test.com"),
// Authorization = new EndpointAuthorization()
// {
// Scheme = "Test",
// }
// };
// _ec.Setup(x => x.Endpoints).Returns(new List<ServiceEndpoint> { _endpoint });
// }
// }
// }

View File

@@ -0,0 +1,129 @@
using GitHub.Runner.Worker;
using Moq;
using System;
using System.IO;
using System.Runtime.CompilerServices;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class TrackingManagerL0
{
private Mock<IExecutionContext> _ec;
private TrackingManager _trackingManager;
private string _workFolder;
public TestHostContext Setup([CallerMemberName] string name = "")
{
// Setup the host context.
TestHostContext hc = new TestHostContext(this, name);
// Create a random work path.
_workFolder = hc.GetDirectory(WellKnownDirectory.Work);
// Setup the execution context.
_ec = new Mock<IExecutionContext>();
GitHubContext githubContext = new GitHubContext();
_ec.Setup(x => x.GetGitHubContext("repository")).Returns("actions/runner");
// Setup the tracking manager.
_trackingManager = new TrackingManager();
_trackingManager.Initialize(hc);
return hc;
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void CreatesTrackingConfig()
{
using (TestHostContext hc = Setup())
{
// Arrange.
string trackingFile = Path.Combine(_workFolder, "trackingconfig.json");
DateTimeOffset testStartOn = DateTimeOffset.Now;
// Act.
_trackingManager.Create(_ec.Object, trackingFile);
// Assert.
TrackingConfig config = _trackingManager.LoadIfExists(_ec.Object, trackingFile);
Assert.Equal("runner", config.PipelineDirectory);
Assert.Equal($"runner{Path.DirectorySeparatorChar}runner", config.WorkspaceDirectory);
Assert.Equal("actions/runner", config.RepositoryName);
Assert.Equal(1, config.Repositories.Count);
Assert.Equal($"runner{Path.DirectorySeparatorChar}runner", config.Repositories["actions/runner"].RepositoryPath);
// Manipulate the expected seconds due to loss of granularity when the
// date-time-offset is serialized in a friendly format.
Assert.True(testStartOn.AddSeconds(-1) <= config.LastRunOn);
Assert.True(DateTimeOffset.Now.AddSeconds(1) >= config.LastRunOn);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void LoadsTrackingConfig()
{
using (TestHostContext hc = Setup())
{
// Arrange.
Directory.CreateDirectory(_workFolder);
string filePath = Path.Combine(_workFolder, "trackingconfig.json");
_trackingManager.Create(_ec.Object, filePath);
// Act.
TrackingConfig config = _trackingManager.LoadIfExists(_ec.Object, filePath);
// Assert.
Assert.NotNull(config);
Assert.Equal("actions/runner", config.RepositoryName);
Assert.Equal("runner", config.PipelineDirectory);
Assert.Equal($"runner{Path.DirectorySeparatorChar}runner", config.WorkspaceDirectory);
Assert.Equal(1, config.Repositories.Count);
Assert.Equal($"runner{Path.DirectorySeparatorChar}runner", config.Repositories["actions/runner"].RepositoryPath);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void LoadsTrackingConfig_NotExists()
{
using (TestHostContext hc = Setup())
{
// Act.
TrackingConfig config = _trackingManager.LoadIfExists(
_ec.Object,
Path.Combine(_workFolder, "foo.json"));
// Assert.
Assert.Null(config);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void UpdatesTrackingConfigJobRunProperties()
{
using (TestHostContext hc = Setup())
{
// Arrange.
TrackingConfig config = new TrackingConfig() { RepositoryName = "actions/runner" };
string trackingFile = Path.Combine(_workFolder, "trackingconfig.json");
// Act.
_trackingManager.Update(_ec.Object, config, trackingFile);
// Assert.
config = _trackingManager.LoadIfExists(_ec.Object, trackingFile);
Assert.NotNull(config);
Assert.Equal("actions/runner", config.RepositoryName);
}
}
}
}

View File

@@ -0,0 +1,193 @@
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class VariablesL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Constructor_AppliesMaskHints()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
var copy = new Dictionary<string, VariableValue>
{
{ "MySecretName", new VariableValue("My secret value", true) },
{ "MyPublicVariable", "My public value" },
};
var variables = new Variables(hc, copy);
// Assert.
Assert.Equal(2, variables.AllVariables.Count());
Assert.Equal("My public value", variables.Get("MyPublicVariable"));
Assert.Equal("My secret value", variables.Get("MySecretName"));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Constructor_HandlesNullValue()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
var copy = new Dictionary<string, VariableValue>
{
{ "variable1", new VariableValue(null, false) },
{ "variable2", "some variable 2 value" },
};
// Act.
var variables = new Variables(hc, copy);
// Assert.
Assert.Equal(string.Empty, variables.Get("variable1"));
Assert.Equal("some variable 2 value", variables.Get("variable2"));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Constructor_SetsNullAsEmpty()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
var copy = new Dictionary<string, VariableValue>
{
{ "variable1", new VariableValue(null, false) },
};
// Act.
var variables = new Variables(hc, copy);
// Assert.
Assert.Equal(string.Empty, variables.Get("variable1"));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Constructor_SetsOrdinalIgnoreCaseComparer()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
CultureInfo currentCulture = CultureInfo.CurrentCulture;
CultureInfo currentUICulture = CultureInfo.CurrentUICulture;
try
{
CultureInfo.CurrentCulture = new CultureInfo("tr-TR");
CultureInfo.CurrentUICulture = new CultureInfo("tr-TR");
var copy = new Dictionary<string, VariableValue>
{
{ "i", "foo" },
{ "I", "foo" },
};
// Act.
var variables = new Variables(hc, copy);
// Assert.
Assert.Equal(1, variables.AllVariables.Count());
}
finally
{
// Cleanup.
CultureInfo.CurrentCulture = currentCulture;
CultureInfo.CurrentUICulture = currentUICulture;
}
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Constructor_SkipVariableWithEmptyName()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
var copy = new Dictionary<string, VariableValue>
{
{ "", "" },
{ " ", "" },
{ "MyPublicVariable", "My public value" },
};
var variables = new Variables(hc, copy);
// Assert.
Assert.Equal(1, variables.AllVariables.Count());
Assert.Equal("MyPublicVariable", variables.AllVariables.Single().Name);
Assert.Equal("My public value", variables.AllVariables.Single().Value);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Get_ReturnsNullIfNotFound()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
var variables = new Variables(hc, new Dictionary<string, VariableValue>());
// Act.
string actual = variables.Get("no such");
// Assert.
Assert.Equal(null, actual);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void GetBoolean_DoesNotThrowWhenNull()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
var variables = new Variables(hc, new Dictionary<string, VariableValue>());
// Act.
bool? actual = variables.GetBoolean("no such");
// Assert.
Assert.Null(actual);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void GetEnum_DoesNotThrowWhenNull()
{
using (TestHostContext hc = new TestHostContext(this))
{
// Arrange.
var variables = new Variables(hc, new Dictionary<string, VariableValue>());
// Act.
System.IO.FileShare? actual = variables.GetEnum<System.IO.FileShare>("no such");
// Assert.
Assert.Null(actual);
}
}
}
}

View File

@@ -0,0 +1,308 @@
using GitHub.DistributedTask.WebApi;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.Runner.Worker;
using Moq;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using GitHub.Services.WebApi;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class WorkerL0
{
private Mock<IProcessChannel> _processChannel;
private Mock<IJobRunner> _jobRunner;
private Mock<IRunnerWebProxy> _proxy;
private Mock<IRunnerCertificateManager> _cert;
public WorkerL0()
{
_processChannel = new Mock<IProcessChannel>();
_jobRunner = new Mock<IJobRunner>();
_proxy = new Mock<IRunnerWebProxy>();
_cert = new Mock<IRunnerCertificateManager>();
}
private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
{
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference() { PlanId = Guid.NewGuid() };
TimelineReference timeline = null;
Dictionary<string, VariableValue> variables = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
variables[Constants.Variables.System.Culture] = "en-US";
Pipelines.JobResources resources = new Pipelines.JobResources();
var serviceEndpoint = new ServiceEndpoint();
serviceEndpoint.Authorization = new EndpointAuthorization();
serviceEndpoint.Authorization.Parameters.Add("nullValue", null);
resources.Endpoints.Add(serviceEndpoint);
List<Pipelines.JobStep> tasks = new List<Pipelines.JobStep>();
tasks.Add(new Pipelines.TaskStep()
{
Id = Guid.NewGuid(),
Reference = new Pipelines.TaskStepDefinitionReference()
{
Id = Guid.NewGuid(),
Name = "TestTask",
Version = "1.0.0"
}
});
Guid JobId = Guid.NewGuid();
var sidecarContainers = new MappingToken(null, null, null)
{
{
new StringToken(null, null, null, "nginx"),
new MappingToken(null, null, null)
{
{
new StringToken(null, null, null, "image"),
new StringToken(null, null, null, "nginx")
},
}
},
};
var context = new Pipelines.ContextData.DictionaryContextData
{
{
"github",
new Pipelines.ContextData.DictionaryContextData()
},
};
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, new StringToken(null, null, null, "ubuntu"), sidecarContainers, null, variables, new List<MaskHint>(), resources, context, null, tasks, null);
return jobRequest;
}
private JobCancelMessage CreateJobCancelMessage(Guid jobId)
{
return new JobCancelMessage(jobId, TimeSpan.FromSeconds(0));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void DispatchRunNewJob()
{
//Arrange
using (var hc = new TestHostContext(this))
using (var tokenSource = new CancellationTokenSource())
{
var worker = new GitHub.Runner.Worker.Worker();
hc.EnqueueInstance<IProcessChannel>(_processChannel.Object);
hc.EnqueueInstance<IJobRunner>(_jobRunner.Object);
hc.SetSingleton<IRunnerWebProxy>(_proxy.Object);
hc.SetSingleton<IRunnerCertificateManager>(_cert.Object);
worker.Initialize(hc);
var jobMessage = CreateJobRequestMessage("job1");
var arWorkerMessages = new WorkerMessage[]
{
new WorkerMessage
{
Body = JsonUtility.ToString(jobMessage),
MessageType = MessageType.NewJobRequest
}
};
var workerMessages = new Queue<WorkerMessage>(arWorkerMessages);
_processChannel
.Setup(x => x.ReceiveAsync(It.IsAny<CancellationToken>()))
.Returns(async () =>
{
// Return the job message.
if (workerMessages.Count > 0)
{
return workerMessages.Dequeue();
}
// Wait for the text to run
await Task.Delay(-1, tokenSource.Token);
return default(WorkerMessage);
});
_jobRunner.Setup(x => x.RunAsync(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult<TaskResult>(TaskResult.Succeeded));
//Act
await worker.RunAsync(pipeIn: "1", pipeOut: "2");
//Assert
_processChannel.Verify(x => x.StartClient("1", "2"), Times.Once());
_jobRunner.Verify(x => x.RunAsync(
It.Is<Pipelines.AgentJobRequestMessage>(y => IsMessageIdentical(y, jobMessage)), It.IsAny<CancellationToken>()));
tokenSource.Cancel();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void DispatchCancellation()
{
//Arrange
using (var hc = new TestHostContext(this))
{
var worker = new GitHub.Runner.Worker.Worker();
hc.EnqueueInstance<IProcessChannel>(_processChannel.Object);
hc.EnqueueInstance<IJobRunner>(_jobRunner.Object);
hc.SetSingleton<IRunnerWebProxy>(_proxy.Object);
hc.SetSingleton<IRunnerCertificateManager>(_cert.Object);
worker.Initialize(hc);
var jobMessage = CreateJobRequestMessage("job1");
var cancelMessage = CreateJobCancelMessage(jobMessage.JobId);
var arWorkerMessages = new WorkerMessage[]
{
new WorkerMessage
{
Body = JsonUtility.ToString(jobMessage),
MessageType = MessageType.NewJobRequest
},
new WorkerMessage
{
Body = JsonUtility.ToString(cancelMessage),
MessageType = MessageType.CancelRequest
}
};
var workerMessages = new Queue<WorkerMessage>(arWorkerMessages);
_processChannel.Setup(x => x.ReceiveAsync(It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(workerMessages.Dequeue()));
_jobRunner.Setup(x => x.RunAsync(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<CancellationToken>()))
.Returns(
async (Pipelines.AgentJobRequestMessage jm, CancellationToken ct) =>
{
await Task.Delay(-1, ct);
return TaskResult.Canceled;
});
//Act
await Assert.ThrowsAsync<TaskCanceledException>(
async () => await worker.RunAsync("1", "2"));
//Assert
_processChannel.Verify(x => x.StartClient("1", "2"), Times.Once());
_jobRunner.Verify(x => x.RunAsync(
It.Is<Pipelines.AgentJobRequestMessage>(y => IsMessageIdentical(y, jobMessage)), It.IsAny<CancellationToken>()));
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void VerifyJobRequestMessagePiiDataIsScrubbed()
{
// Arrange
Pipelines.AgentJobRequestMessage message = CreateJobRequestMessage("jobwithpiidata");
// Populate PII variables
foreach (string piiVariable in Variables.PiiVariables)
{
message.Variables.Add(piiVariable, "MyPiiVariable");
}
foreach (string piiVariableSuffix in Variables.PiiArtifactVariableSuffixes)
{
message.Variables.Add($"{Variables.PiiArtifactVariablePrefix}.MyArtifact.{piiVariableSuffix}", "MyPiiVariable");
}
// Populate the repository PII data
Pipelines.RepositoryResource repository = new Pipelines.RepositoryResource();
repository.Properties.Set(
Pipelines.RepositoryPropertyNames.VersionInfo,
new Pipelines.VersionInfo()
{
Author = "MyAuthor",
Message = "MyMessage"
});
message.Resources.Repositories.Add(repository);
// Act
Pipelines.AgentJobRequestMessage scrubbedMessage = WorkerUtilities.ScrubPiiData(message);
// Assert
foreach (string piiVariable in Variables.PiiVariables)
{
scrubbedMessage.Variables.TryGetValue(piiVariable, out VariableValue value);
Assert.Equal("[PII]", value.Value);
}
foreach (string piiVariableSuffix in Variables.PiiArtifactVariableSuffixes)
{
scrubbedMessage.Variables.TryGetValue($"{Variables.PiiArtifactVariablePrefix}.MyArtifact.{piiVariableSuffix}", out VariableValue value);
Assert.Equal("[PII]", value.Value);
}
Pipelines.RepositoryResource scrubbedRepo = scrubbedMessage.Resources.Repositories[0];
Pipelines.VersionInfo scrubbedInfo = scrubbedRepo.Properties.Get<Pipelines.VersionInfo>(Pipelines.RepositoryPropertyNames.VersionInfo);
Assert.Equal("[PII]", scrubbedInfo.Author);
}
private bool IsMessageIdentical(Pipelines.AgentJobRequestMessage source, Pipelines.AgentJobRequestMessage target)
{
if (source == null && target == null)
{
return true;
}
if (source != null && target == null)
{
return false;
}
if (source == null && target != null)
{
return false;
}
if (JsonUtility.ToString(source.JobContainer) != JsonUtility.ToString(target.JobContainer))
{
return false;
}
if (source.JobDisplayName != target.JobDisplayName)
{
return false;
}
if (source.JobId != target.JobId)
{
return false;
}
if (source.JobName != target.JobName)
{
return false;
}
if (source.MaskHints.Count != target.MaskHints.Count)
{
return false;
}
if (source.MessageType != target.MessageType)
{
return false;
}
if (source.Plan.PlanId != target.Plan.PlanId)
{
return false;
}
if (source.RequestId != target.RequestId)
{
return false;
}
if (source.Resources.Endpoints.Count != target.Resources.Endpoints.Count)
{
return false;
}
if (source.Steps.Count != target.Steps.Count)
{
return false;
}
if (source.Variables.Count != target.Variables.Count)
{
return false;
}
return true;
}
}
}