diff --git a/Simulation.sln b/Simulation.sln
index 1250f77e65b..2242d066ddd 100644
--- a/Simulation.sln
+++ b/Simulation.sln
@@ -49,7 +49,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QsharpExe", "src\Simulation
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntryPointDriver", "src\Simulation\EntryPointDriver\EntryPointDriver.csproj", "{944FE7EF-9220-4CC6-BB20-CE517195B922}"
EndProject
-Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "EntryPointDriver.Tests", "src\Simulation\EntryPointDriver.Tests\EntryPointDriver.Tests.fsproj", "{E2F30496-19D8-46A8-9BC0-26936FFE70D2}"
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests.EntryPointDriver", "src\Simulation\EntryPointDriver.Tests\Tests.EntryPointDriver.fsproj", "{E2F30496-19D8-46A8-9BC0-26936FFE70D2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/build/test.ps1 b/build/test.ps1
index 95133997b06..4a879f1fb13 100644
--- a/build/test.ps1
+++ b/build/test.ps1
@@ -34,6 +34,8 @@ function Test-One {
Test-One '../src/Simulation/CsharpGeneration.Tests/Tests.CsharpGeneration.fsproj'
+Test-One '../src/Simulation/EntryPointDriver.Tests/Tests.EntryPointDriver.fsproj'
+
Test-One '../src/Simulation/RoslynWrapper.Tests/Tests.RoslynWrapper.fsproj'
Test-One '../src/Simulation/QCTraceSimulator.Tests/Tests.Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime.csproj'
diff --git a/src/Simulation/EntryPointDriver.Tests/EntryPointDriver.Tests.fsproj b/src/Simulation/EntryPointDriver.Tests/Tests.EntryPointDriver.fsproj
similarity index 92%
rename from src/Simulation/EntryPointDriver.Tests/EntryPointDriver.Tests.fsproj
rename to src/Simulation/EntryPointDriver.Tests/Tests.EntryPointDriver.fsproj
index db1f59c022f..24ea7c82cda 100644
--- a/src/Simulation/EntryPointDriver.Tests/EntryPointDriver.Tests.fsproj
+++ b/src/Simulation/EntryPointDriver.Tests/Tests.EntryPointDriver.fsproj
@@ -4,6 +4,7 @@
netcoreapp3.1
false
false
+ Microsoft.Quantum.CsharpGeneration.EntryPointDriver.Tests
diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs
index 16e38992919..6df1ce5472d 100644
--- a/src/Simulation/EntryPointDriver.Tests/Tests.fs
+++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs
@@ -142,19 +142,28 @@ let private run (assembly : Assembly) (args : string[]) =
Console.SetOut previousOut
CultureInfo.DefaultThreadCurrentCulture <- previousCulture
+/// Replaces every sequence of whitespace characters in the string with a single space.
+let private normalize s = Regex.Replace(s, @"\s+", " ").Trim()
+
/// Asserts that running the entry point in the assembly with the given arguments succeeds and yields the expected
-/// output.
+/// output. The standard error and out streams of the actual output are concatenated in that order.
let private yields expected (assembly, args) =
- let normalize text = Regex.Replace(text, @"\s+", " ").Trim()
let out, error, exitCode = run assembly args
Assert.True (0 = exitCode, sprintf "Expected exit code 0, but got %d with:\n\n%s\n\n%s" exitCode out error)
- Assert.Equal (normalize expected, normalize out)
+ Assert.Equal (normalize expected, normalize (error + out))
/// Asserts that running the entry point in the assembly with the given arguments fails.
let private fails (assembly, args) =
let out, error, exitCode = run assembly args
Assert.True (0 <> exitCode, sprintf "Expected non-zero exit code, but got 0 with:\n\n%s\n\n%s" out error)
+/// Asserts that running the entry point in the assembly with the given arguments fails and the error message starts
+/// with the expected message.
+let private failsWith expected (assembly, args) =
+ let out, error, exitCode = run assembly args
+ Assert.True (0 <> exitCode, sprintf "Expected non-zero exit code, but got 0 with:\n\n%s\n\n%s" out error)
+ Assert.StartsWith (normalize expected, normalize error)
+
/// A tuple of the test assembly and arguments using the standard default simulator. The tuple can be passed to yields
/// or fails.
let private test testNum =
@@ -406,8 +415,13 @@ let ``Uses single-dash short names`` () =
[]
let ``Shadows --simulator`` () =
let given = test 27
- given ["--simulator"; "foo"] |> yields "foo"
- given ["--simulator"; AssemblyConstants.ResourcesEstimator] |> yields AssemblyConstants.ResourcesEstimator
+ given ["--simulator"; "foo"]
+ |> yields "Warning: Option --simulator is overridden by an entry point parameter name. Using default value QuantumSimulator.
+ foo"
+ given ["--simulator"; AssemblyConstants.ResourcesEstimator]
+ |> yields (sprintf "Warning: Option --simulator is overridden by an entry point parameter name. Using default value QuantumSimulator.
+ %s"
+ AssemblyConstants.ResourcesEstimator)
given ["-s"; AssemblyConstants.ResourcesEstimator; "--simulator"; "foo"] |> fails
given ["-s"; "foo"] |> fails
@@ -427,15 +441,16 @@ let ``Shadows --version`` () =
// Simulators
// The expected output from the resources estimator.
-let private resourceSummary = "Metric Sum
-CNOT 0
-QubitClifford 1
-R 0
-Measure 1
-T 0
-Depth 0
-Width 1
-BorrowedWidth 0"
+let private resourceSummary =
+ "Metric Sum
+ CNOT 0
+ QubitClifford 1
+ R 0
+ Measure 1
+ T 0
+ Depth 0
+ Width 1
+ BorrowedWidth 0"
[]
let ``Supports QuantumSimulator`` () =
@@ -481,7 +496,86 @@ let ``Supports default custom simulator`` () =
given ["--simulator"; typeof.FullName; "--use-h"; "false"] |> fails
-// TODO: Add tests for the "submit" command.
+// Azure Quantum Submission
+
+/// Standard command-line arguments for the "submit" command without specifying a target.
+let private submitWithoutTarget =
+ [ "submit"
+ "--storage"
+ "myStorage"
+ "--subscription"
+ "mySubscription"
+ "--resource-group"
+ "myResourceGroup"
+ "--workspace"
+ "myWorkspace" ]
+
+/// Standard command-line arguments for the "submit" command using the "nothing" target.
+let private submitWithTestTarget = submitWithoutTarget @ ["--target"; "nothing"]
+
+[]
+let ``Submit can submit a job`` () =
+ let given = test 1
+ given submitWithTestTarget
+ |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead.
+ 00000000-0000-0000-0000-0000000000000"
+
+[]
+let ``Submit can show only the ID`` () =
+ let given = test 1
+ given (submitWithTestTarget @ ["--output"; "id"]) |> yields "00000000-0000-0000-0000-0000000000000"
+
+[]
+let ``Submit uses default values`` () =
+ let given = test 1
+ given (submitWithTestTarget @ ["--verbose"])
+ |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead.
+ Target: nothing
+ Storage: myStorage
+ Subscription: mySubscription
+ Resource Group: myResourceGroup
+ Workspace: myWorkspace
+ AAD Token:
+ Base URI:
+ Shots: 500
+ Output: FriendlyUri
+ Dry Run: False
+ Verbose: True
+
+ 00000000-0000-0000-0000-0000000000000"
+
+[]
+let ``Submit allows overriding default values`` () =
+ let given = test 1
+ given (submitWithTestTarget @ ["--verbose"; "--aad-token"; "myToken"; "--base-uri"; "myBaseUri"; "--shots"; "750"])
+ |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead.
+ Target: nothing
+ Storage: myStorage
+ Subscription: mySubscription
+ Resource Group: myResourceGroup
+ Workspace: myWorkspace
+ AAD Token: myToken
+ Base URI: myBaseUri
+ Shots: 750
+ Output: FriendlyUri
+ Dry Run: False
+ Verbose: True
+
+ 00000000-0000-0000-0000-0000000000000"
+
+[]
+let ``Submit requires a positive number of shots`` () =
+ let given = test 1
+ given (submitWithTestTarget @ ["--shots"; "1"])
+ |> yields "The friendly URI for viewing job results is not available yet. Showing the job ID instead.
+ 00000000-0000-0000-0000-0000000000000"
+ given (submitWithTestTarget @ ["--shots"; "0"]) |> fails
+ given (submitWithTestTarget @ ["--shots"; "-1"]) |> fails
+
+[]
+let ``Submit fails with unknown target`` () =
+ let given = test 1
+ given (submitWithoutTarget @ ["--target"; "foo"]) |> failsWith "The target 'foo' was not recognized."
// Help
@@ -489,24 +583,54 @@ let ``Supports default custom simulator`` () =
[]
let ``Uses documentation`` () =
let name = Path.GetFileNameWithoutExtension (Assembly.GetEntryAssembly().Location)
- let message = (name, name) ||> sprintf "%s:
- This test checks that the entry point documentation appears correctly in the command line help message.
+ let message =
+ (name, name)
+ ||> sprintf "%s:
+ This test checks that the entry point documentation appears correctly in the command line help message.
-Usage:
- %s [options] [command]
+ Usage:
+ %s [options] [command]
-Options:
- -n (REQUIRED) A number.
- --pauli (REQUIRED) The name of a Pauli matrix.
- --my-cool-bool (REQUIRED) A neat bit.
- -s, --simulator The name of the simulator to use.
- --version Show version information
- -?, -h, --help Show help and usage information
+ Options:
+ -n (REQUIRED) A number.
+ --pauli (REQUIRED) The name of a Pauli matrix.
+ --my-cool-bool (REQUIRED) A neat bit.
+ -s, --simulator The name of the simulator to use.
+ --version Show version information
+ -?, -h, --help Show help and usage information
-Commands:
- simulate (default) Run the program using a local simulator."
+ Commands:
+ simulate (default) Run the program using a local simulator."
let given = test 31
given ["--help"] |> yields message
given ["-h"] |> yields message
given ["-?"] |> yields message
+
+[]
+let ``Shows help text for submit command`` () =
+ let name = Path.GetFileNameWithoutExtension (Assembly.GetEntryAssembly().Location)
+ let message =
+ name
+ |> sprintf "Usage:
+ %s submit [options]
+
+ Options:
+ --target (REQUIRED) The target device ID.
+ --storage (REQUIRED) The storage account connection string.
+ --subscription (REQUIRED) The subscription ID.
+ --resource-group (REQUIRED) The resource group name.
+ --workspace (REQUIRED) The workspace name.
+ --aad-token The Azure Active Directory authentication token.
+ --base-uri The base URI of the Azure Quantum endpoint.
+ --output The information to show in the output after the job is submitted.
+ --shots The number of times the program is executed on the target machine.
+ --dry-run Validate the program and options, but do not submit to Azure Quantum.
+ --verbose Show additional information about the submission.
+ -n (REQUIRED) A number.
+ --pauli (REQUIRED) The name of a Pauli matrix.
+ --my-cool-bool (REQUIRED) A neat bit.
+ -?, -h, --help Show help and usage information"
+
+ let given = test 31
+ given ["submit"; "--help"] |> yields message
diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs
index e5d2604c21e..6f6f14d0335 100644
--- a/src/Simulation/EntryPointDriver/Azure.cs
+++ b/src/Simulation/EntryPointDriver/Azure.cs
@@ -26,6 +26,12 @@ internal static class Azure
internal static async Task Submit(
IEntryPoint entryPoint, ParseResult parseResult, AzureSettings settings)
{
+ if (settings.Verbose)
+ {
+ Console.WriteLine(settings);
+ Console.WriteLine();
+ }
+
var machine = CreateMachine(settings);
if (machine is null)
{
@@ -109,13 +115,13 @@ internal enum OutputFormat
/// Show a friendly message with a URI that can be used to see the job results.
///
FriendlyUri,
-
+
///
/// Show only the job ID.
///
Id
}
-
+
///
/// Settings for a submission to Azure Quantum.
///
@@ -125,37 +131,37 @@ internal sealed class AzureSettings
/// The target device ID.
///
public string? Target { get; set; }
-
+
///
/// The storage account connection string.
///
public string? Storage { get; set; }
-
+
///
/// The subscription ID.
///
public string? Subscription { get; set; }
-
+
///
/// The resource group name.
///
public string? ResourceGroup { get; set; }
-
+
///
/// The workspace name.
///
public string? Workspace { get; set; }
-
+
///
/// The Azure Active Directory authentication token.
///
public string? AadToken { get; set; }
-
+
///
/// The base URI of the Azure Quantum endpoint.
///
public Uri? BaseUri { get; set; }
-
+
///
/// The number of times the program is executed on the target machine.
///
@@ -171,6 +177,11 @@ internal sealed class AzureSettings
///
public bool DryRun { get; set; }
+ ///
+ /// Show additional information about the submission.
+ ///
+ public bool Verbose { get; set; }
+
///
/// Creates a based on the settings.
///
@@ -179,5 +190,19 @@ internal Workspace CreateWorkspace() =>
AadToken is null
? new Workspace(Subscription, ResourceGroup, Workspace, baseUri: BaseUri)
: new Workspace(Subscription, ResourceGroup, Workspace, AadToken, BaseUri);
+
+ public override string ToString() =>
+ string.Join(System.Environment.NewLine,
+ $"Target: {Target}",
+ $"Storage: {Storage}",
+ $"Subscription: {Subscription}",
+ $"Resource Group: {ResourceGroup}",
+ $"Workspace: {Workspace}",
+ $"AAD Token: {AadToken}",
+ $"Base URI: {BaseUri}",
+ $"Shots: {Shots}",
+ $"Output: {Output}",
+ $"Dry Run: {DryRun}",
+ $"Verbose: {Verbose}");
}
}
diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs
index 38b68ebe94e..9bca59fe327 100644
--- a/src/Simulation/EntryPointDriver/Driver.cs
+++ b/src/Simulation/EntryPointDriver/Driver.cs
@@ -87,6 +87,7 @@ public async Task Run(string[] args)
AddOptionIfAvailable(submit, OutputOption);
AddOptionIfAvailable(submit, ShotsOption);
AddOptionIfAvailable(submit, DryRunOption);
+ AddOptionIfAvailable(submit, VerboseOption);
var root = new RootCommand(entryPoint.Summary) { simulate, submit };
foreach (var option in entryPoint.Options)
@@ -136,7 +137,8 @@ private async Task Submit(ParseResult parseResult, AzureSettings settings)
BaseUri = DefaultIfShadowed(BaseUriOption, settings.BaseUri),
Shots = DefaultIfShadowed(ShotsOption, settings.Shots),
Output = DefaultIfShadowed(OutputOption, settings.Output),
- DryRun = DefaultIfShadowed(DryRunOption, settings.DryRun)
+ DryRun = DefaultIfShadowed(DryRunOption, settings.DryRun),
+ Verbose = DefaultIfShadowed(VerboseOption, settings.Verbose)
});
///
@@ -268,6 +270,12 @@ internal static class Driver
false,
"Validate the program and options, but do not submit to Azure Quantum.");
+ ///
+ /// The verbose option.
+ ///
+ internal static readonly OptionInfo VerboseOption = new OptionInfo(
+ new[] { "--verbose" }, false, "Show additional information about the submission.");
+
///
/// Displays a message to the console using the given color and text writer.
///