Skip to content

Commit

Permalink
manage C#-only experiments with ExperimentsManager (#10868)
Browse files Browse the repository at this point in the history
  • Loading branch information
brettfo authored Nov 7, 2024
1 parent 2c549d2 commit e719655
Show file tree
Hide file tree
Showing 17 changed files with 525 additions and 170 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.IO;
using System.Text;

using NuGetUpdater.Core;
Expand All @@ -18,6 +19,8 @@ public async Task WithSolution()
await Run(path =>
[
"update",
"--job-path",
Path.Combine(path, "job.json"),
"--repo-root",
path,
"--solution-or-project",
Expand Down Expand Up @@ -119,6 +122,8 @@ public async Task WithProject()
await Run(path =>
[
"update",
"--job-path",
Path.Combine(path, "job.json"),
"--repo-root",
path,
"--solution-or-project",
Expand Down Expand Up @@ -197,6 +202,8 @@ public async Task WithDirsProjAndDirectoryBuildPropsThatIsOutOfDirectoryButStill
await Run(path =>
[
"update",
"--job-path",
Path.Combine(path, "job.json"),
"--repo-root",
path,
"--solution-or-project",
Expand Down Expand Up @@ -325,6 +332,7 @@ public async Task UpdaterDoesNotUseRepoGlobalJsonForMSBuildTasks(string? working
MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net8.0"),
];
await MockNuGetPackagesInDirectory(testPackages, tempDir.DirectoryPath);
await MockJobFileInDirectory(tempDir.DirectoryPath);

var globalJsonPath = Path.Join(tempDir.DirectoryPath, "global.json");
var srcGlobalJsonPath = Path.Join(tempDir.DirectoryPath, "src", "global.json");
Expand Down Expand Up @@ -353,6 +361,8 @@ await File.WriteAllTextAsync(projectPath, """
IEnumerable<string> executableArgs = [
executableName,
"update",
"--job-path",
Path.Combine(tempDir.DirectoryPath, "job.json"),
"--repo-root",
tempDir.DirectoryPath,
"--solution-or-project",
Expand Down Expand Up @@ -402,6 +412,7 @@ private static async Task Run(Func<string, string[]> getArgs, (string Path, stri
try
{
await MockJobFileInDirectory(path);
await MockNuGetPackagesInDirectory(packages, path);
var args = getArgs(path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace NuGetUpdater.Cli.Commands;

internal static class UpdateCommand
{
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { IsRequired = true };
internal static readonly Option<DirectoryInfo> RepoRootOption = new("--repo-root", () => new DirectoryInfo(Environment.CurrentDirectory)) { IsRequired = false };
internal static readonly Option<FileInfo> SolutionOrProjectFileOption = new("--solution-or-project") { IsRequired = true };
internal static readonly Option<string> DependencyNameOption = new("--dependency") { IsRequired = true };
Expand All @@ -18,6 +19,7 @@ internal static Command GetCommand(Action<int> setExitCode)
{
Command command = new("update", "Applies the changes from an analysis report to update a dependency.")
{
JobPathOption,
RepoRootOption,
SolutionOrProjectFileOption,
DependencyNameOption,
Expand All @@ -29,12 +31,14 @@ internal static Command GetCommand(Action<int> setExitCode)

command.TreatUnmatchedTokensAsErrors = true;

command.SetHandler(async (repoRoot, solutionOrProjectFile, dependencyName, newVersion, previousVersion, isTransitive, resultOutputPath) =>
command.SetHandler(async (jobPath, repoRoot, solutionOrProjectFile, dependencyName, newVersion, previousVersion, isTransitive, resultOutputPath) =>
{
var worker = new UpdaterWorker(new ConsoleLogger());
var logger = new ConsoleLogger();
var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobPath.FullName, logger);
var worker = new UpdaterWorker(experimentsManager, logger);
await worker.RunAsync(repoRoot.FullName, solutionOrProjectFile.FullName, dependencyName, previousVersion, newVersion, isTransitive, resultOutputPath);
setExitCode(0);
}, RepoRootOption, SolutionOrProjectFileOption, DependencyNameOption, NewVersionOption, PreviousVersionOption, IsTransitiveOption, ResultOutputPathOption);
}, JobPathOption, RepoRootOption, SolutionOrProjectFileOption, DependencyNameOption, NewVersionOption, PreviousVersionOption, IsTransitiveOption, ResultOutputPathOption);

return command;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,162 @@ public void DeserializeJob()
Assert.Equal("specific-sdk", jobWrapper.Job.Source.Directory);
}

[Fact]
public void DeserializeJob_FieldsNotYetSupported()
{
// the `source` field is required in the C# model; the remaining fields might exist in the JSON file, but are
// not yet supported in the C# model (some keys missing, others deserialize to `object?`; deserialization
// should not fail
var jobWrapper = RunWorker.Deserialize("""
{
"job": {
"source": {
"provider": "github",
"repo": "some-org/some-repo",
"directory": "specific-sdk",
"hostname": null,
"api-endpoint": null
},
"id": "some-id",
"allowed-updates": [
{
"dependency-type": "direct",
"update-type": "all"
}
],
"credentials": [
{
"name": "some-cred",
"token": "abc123"
}
],
"existing-pull-requests": [
[
{
"dependency-name": "Some.Package",
"dependency-version": "1.2.3"
}
]
],
"repo-contents-path": "/path/to/repo",
"token": "abc123"
}
}
""");
Assert.Equal("github", jobWrapper.Job.Source.Provider);
Assert.Equal("some-org/some-repo", jobWrapper.Job.Source.Repo);
Assert.Equal("specific-sdk", jobWrapper.Job.Source.Directory);
}

[Fact]
public void DeserializeExperimentsManager()
{
var jobWrapper = RunWorker.Deserialize("""
{
"job": {
"package-manager": "nuget",
"allowed-updates": [
{
"update-type": "all"
}
],
"source": {
"provider": "github",
"repo": "some-org/some-repo",
"directory": "some-dir"
},
"experiments": {
"nuget_legacy_dependency_solver": true,
"unexpected_bool": true,
"unexpected_number": 42,
"unexpected_null": null,
"unexpected_string": "abc",
"unexpected_array": [1, "two", 3.0],
"unexpected_object": {
"a": 1,
"b": "two"
}
}
}
}
""");
var experimentsManager = ExperimentsManager.GetExperimentsManager(jobWrapper.Job.Experiments);
Assert.True(experimentsManager.UseLegacyDependencySolver);
}

[Fact]
public void DeserializeExperimentsManager_EmptyExperiments()
{
var jobWrapper = RunWorker.Deserialize("""
{
"job": {
"package-manager": "nuget",
"allowed-updates": [
{
"update-type": "all"
}
],
"source": {
"provider": "github",
"repo": "some-org/some-repo",
"directory": "some-dir"
},
"experiments": {
}
}
}
""");
var experimentsManager = ExperimentsManager.GetExperimentsManager(jobWrapper.Job.Experiments);
Assert.False(experimentsManager.UseLegacyDependencySolver);
}

[Fact]
public void DeserializeExperimentsManager_NoExperiments()
{
var jobWrapper = RunWorker.Deserialize("""
{
"job": {
"package-manager": "nuget",
"allowed-updates": [
{
"update-type": "all"
}
],
"source": {
"provider": "github",
"repo": "some-org/some-repo",
"directory": "some-dir"
}
}
}
""");
var experimentsManager = ExperimentsManager.GetExperimentsManager(jobWrapper.Job.Experiments);
Assert.False(experimentsManager.UseLegacyDependencySolver);
}

[Fact]
public async Task DeserializeExperimentsManager_UnsupportedJobFileShape_InfoIsReportedAndEmptyExperimentSetIsReturned()
{
// arrange
using var tempDir = new TemporaryDirectory();
var jobFilePath = Path.Combine(tempDir.DirectoryPath, "job.json");
var jobContent = """
{
"this-is-not-a-job-and-parsing-will-fail-but-an-empty-experiment-set-should-sill-be-returned": {
}
}
""";
await File.WriteAllTextAsync(jobFilePath, jobContent);
var capturingTestLogger = new CapturingTestLogger();

// act - this is the entrypoint the update command uses to parse the job file
var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobFilePath, capturingTestLogger);

// assert
Assert.False(experimentsManager.UseLegacyDependencySolver);
Assert.Single(capturingTestLogger.Messages.Where(m => m.Contains("Error deserializing job file")));
}

[Fact]
public void SerializeError()
{
Expand All @@ -67,4 +223,16 @@ public void SerializeError()
var expected = """{"data":{"error-type":"job_repo_not_found","error-details":{"message":"some message"}}}""";
Assert.Equal(expected, actual);
}

private class CapturingTestLogger : ILogger
{
private readonly List<string> _messages = new();

public IReadOnlyList<string> Messages => _messages;

public void Log(string message)
{
_messages.Add(message);
}
}
}
Loading

0 comments on commit e719655

Please sign in to comment.