diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ab514ed1c3..f1151e7c7b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -62,12 +62,6 @@ jobs: arguments: '-TargetFile binver\binver\version.h -BuildVersion $(BuildVer)' workingDirectory: 'src' - - task: DownloadSecureFile@1 - name: signingCert - displayName: 'Download signing certificate' - inputs: - secureFile: 'AppInstallerTest.pfx' - - task: VSBuild@1 displayName: Build Solution inputs: @@ -77,10 +71,7 @@ jobs: msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always - /p:UapAppxPackageBuildMode=StoreUpload - /p:AppxPackageSigningEnabled=true - /p:PackageCertificateThumbprint="" - /p:PackageCertificateKeyFile="$(signingCert.secureFilePath)"' + /p:UapAppxPackageBuildMode=StoreUpload' - task: CmdLine@2 displayName: Run Unit Tests Unpackaged @@ -155,7 +146,8 @@ jobs: testSelector: 'testAssemblies' testAssemblyVer2: 'src\AnyCPU\Release\AppInstallerCLIE2ETests\AppInstallerCLIE2ETests.dll' runSettingsFile: 'src\AnyCPU\Release\AppInstallerCLIE2ETests\Test.runsettings' - overrideTestrunParameters: '-PackagedContext false -AICLIPath $(system.defaultWorkingDirectory)\src\x64\Release\AppInstallerCLI\AppInstallerCLI.exe' + overrideTestrunParameters: '-PackagedContext false + -AICLIPath $(system.defaultWorkingDirectory)\src\x64\Release\AppInstallerCLI\AppInstallerCLI.exe' - task: VSTest@2 displayName: Run E2E Tests Packaged @@ -163,7 +155,11 @@ jobs: testSelector: 'testAssemblies' testAssemblyVer2: 'src\AnyCPU\Release\AppInstallerCLIE2ETests\AppInstallerCLIE2ETests.dll' runSettingsFile: 'src\AnyCPU\Release\AppInstallerCLIE2ETests\Test.runsettings' - overrideTestrunParameters: '-PackagedContext true -AICLIPackagePath $(appxPackageDir)AppInstallerCLIPackage_0.0.0.2_Test\AppInstallerCLIPackage_0.0.0.2_x86_x64_ARM.appxbundle -AICLIPath AppInst.exe' + overrideTestrunParameters: '-PackagedContext true + -AICLIPackagePath $(system.defaultWorkingDirectory)\src\AppInstallerCLIPackage\bin\x64\Release + -AICLIPath AppInstallerCLI\AppInstallerCLI.exe + -LooseFileRegistration true + -InvokeCommandInDesktopPackage true' - task: PublishBuildArtifacts@1 displayName: Publish CLI Binary diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 1a35719528..3808961823 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -9,6 +9,8 @@ public class Constants public const string AICLIPathParameter = "AICLIPath"; public const string AICLIPackagePathParameter = "AICLIPackagePath"; public const string VerboseLoggingParameter = "VerboseLogging"; + public const string LooseFileRegistrationParameter = "LooseFileRegistration"; + public const string InvokeCommandInDesktopPackageParameter = "InvokeCommandInDesktopPackage"; public const string AppInstallerTestCert = "AppInstallerTest.cer"; public const string AppInstallerTestCertThumbprint = "d03e7a688b388b1edde8476a627531c49db88017"; @@ -17,8 +19,9 @@ public class Constants public const string IndexPackageCert = "IndexPackageInt.cer"; public const string IndexPackageCertThumbprint = "1da968e670a0257f61628aad20c60c64fdecd41a"; - public const string AICLIPackageFile = "AppInstallerCLIPackage.appxbundle"; + public const string AICLIPackageFamilyName = "AppInstallerCLI_8wekyb3d8bbwe"; public const string AICLIPackageName = "AppInstallerCLI"; + public const string AICLIAppId = "AppInst"; // Todo: there's a deployment bug that if the last optional package is removed, the main package is also removed. // We should remove this when the deployment bug is fixed. diff --git a/src/AppInstallerCLIE2ETests/SetUpFixture.cs b/src/AppInstallerCLIE2ETests/SetUpFixture.cs index fb337f835e..e0eb2761c7 100644 --- a/src/AppInstallerCLIE2ETests/SetUpFixture.cs +++ b/src/AppInstallerCLIE2ETests/SetUpFixture.cs @@ -6,6 +6,7 @@ namespace AppInstallerCLIE2ETests using Microsoft.Win32; using NUnit.Framework; using System; + using System.IO; [SetUpFixture] public class SetUpFixture @@ -15,19 +16,25 @@ public class SetUpFixture [OneTimeSetUp] public void Setup() { - TestCommon.IsPackagedContext = TestContext.Parameters.Exists(Constants.PackagedContextParameter) && + TestCommon.PackagedContext = TestContext.Parameters.Exists(Constants.PackagedContextParameter) && TestContext.Parameters.Get(Constants.PackagedContextParameter).Equals("true", StringComparison.OrdinalIgnoreCase); TestCommon.VerboseLogging = TestContext.Parameters.Exists(Constants.VerboseLoggingParameter) && TestContext.Parameters.Get(Constants.VerboseLoggingParameter).Equals("true", StringComparison.OrdinalIgnoreCase); + TestCommon.LooseFileRegistration = TestContext.Parameters.Exists(Constants.LooseFileRegistrationParameter) && + TestContext.Parameters.Get(Constants.LooseFileRegistrationParameter).Equals("true", StringComparison.OrdinalIgnoreCase); + + TestCommon.InvokeCommandInDesktopPackage = TestContext.Parameters.Exists(Constants.InvokeCommandInDesktopPackageParameter) && + TestContext.Parameters.Get(Constants.InvokeCommandInDesktopPackageParameter).Equals("true", StringComparison.OrdinalIgnoreCase); + if (TestContext.Parameters.Exists(Constants.AICLIPathParameter)) { TestCommon.AICLIPath = TestContext.Parameters.Get(Constants.AICLIPathParameter); } else { - if (TestCommon.IsPackagedContext) + if (TestCommon.PackagedContext) { TestCommon.AICLIPath = "AppInst.exe"; } @@ -43,7 +50,12 @@ public void Setup() } else { - TestCommon.AICLIPackagePath = TestCommon.GetTestFile(Constants.AICLIPackageFile); + TestCommon.AICLIPackagePath = TestCommon.GetTestFile("AppInstallerCLIPackage.appxbundle"); + } + + if (TestCommon.LooseFileRegistration && TestCommon.InvokeCommandInDesktopPackage) + { + TestCommon.AICLIPath = Path.Combine(TestCommon.AICLIPackagePath, TestCommon.AICLIPath); } ShouldDisableDevModeOnExit = EnableDevMode(true); @@ -51,9 +63,16 @@ public void Setup() Assert.True(TestCommon.RunCommand("certutil.exe", "-addstore -f \"TRUSTEDPEOPLE\" " + TestCommon.GetTestDataFile(Constants.AppInstallerTestCert))); Assert.True(TestCommon.RunCommand("certutil.exe", "-addstore -f \"TRUSTEDPEOPLE\" " + TestCommon.GetTestDataFile(Constants.IndexPackageCert))); - if (TestCommon.IsPackagedContext) + if (TestCommon.PackagedContext) { - Assert.True(TestCommon.InstallMsix(TestCommon.AICLIPackagePath)); + if (TestCommon.LooseFileRegistration) + { + Assert.True(TestCommon.InstallMsixRegister(TestCommon.AICLIPackagePath)); + } + else + { + Assert.True(TestCommon.InstallMsix(TestCommon.AICLIPackagePath)); + } Assert.True(TestCommon.InstallMsix(TestCommon.GetTestDataFile(Constants.PlaceholderPackageFile))); } } @@ -69,7 +88,7 @@ public void TearDown() TestCommon.RunCommand("certutil.exe", $"-delstore \"TRUSTEDPEOPLE\" {Constants.AppInstallerTestCertThumbprint}"); TestCommon.RunCommand("certutil.exe", $"-delstore \"TRUSTEDPEOPLE\" {Constants.IndexPackageCertThumbprint}"); - if (TestCommon.IsPackagedContext) + if (TestCommon.PackagedContext) { TestCommon.RemoveMsix(Constants.PlaceholderPackageName); TestCommon.RemoveMsix(Constants.AICLIPackageName); diff --git a/src/AppInstallerCLIE2ETests/SourceCommand.cs b/src/AppInstallerCLIE2ETests/SourceCommand.cs index 6e2aa14846..1489c4824f 100644 --- a/src/AppInstallerCLIE2ETests/SourceCommand.cs +++ b/src/AppInstallerCLIE2ETests/SourceCommand.cs @@ -75,6 +75,8 @@ public void SourceCommands() Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Done")); + TestCommon.WaitForDeploymentFinish(); + // List should show no source result = TestCommon.RunAICLICommand("source", "list"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); diff --git a/src/AppInstallerCLIE2ETests/Test.runsettings b/src/AppInstallerCLIE2ETests/Test.runsettings index 3843ae27f8..b97ff53de3 100644 --- a/src/AppInstallerCLIE2ETests/Test.runsettings +++ b/src/AppInstallerCLIE2ETests/Test.runsettings @@ -7,5 +7,7 @@ + + \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestCommon.cs b/src/AppInstallerCLIE2ETests/TestCommon.cs index 95f7d85d91..4857eaeeb0 100644 --- a/src/AppInstallerCLIE2ETests/TestCommon.cs +++ b/src/AppInstallerCLIE2ETests/TestCommon.cs @@ -15,10 +15,14 @@ public class TestCommon public static string AICLIPackagePath { get; set; } - public static bool IsPackagedContext { get; set; } + public static bool PackagedContext { get; set; } public static bool VerboseLogging { get; set; } + public static bool LooseFileRegistration { get; set; } + + public static bool InvokeCommandInDesktopPackage { get; set; } + public struct RunCommandResult { public int ExitCode; @@ -35,8 +39,20 @@ public static RunCommandResult RunAICLICommand(string command, string parameters (string.IsNullOrEmpty(stdIn) ? "" : " StdIn: " + stdIn) + " Timeout: " + timeOut; - TestContext.Out.WriteLine("Started command run. " + inputMsg); + TestContext.Out.WriteLine($"Starting command run. {inputMsg} InvokeCommandInDesktopPackage: {InvokeCommandInDesktopPackage}"); + + if (InvokeCommandInDesktopPackage) + { + return RunAICLICommandViaInvokeCommandInDesktopPackage(command, parameters, stdIn, timeOut); + } + else + { + return RunAICLICommandViaDirectProcess(command, parameters, stdIn, timeOut); + } + } + public static RunCommandResult RunAICLICommandViaDirectProcess(string command, string parameters, string stdIn = null, int timeOut = 60000) + { RunCommandResult result = new RunCommandResult(); Process p = new Process(); p.StartInfo = new ProcessStartInfo(AICLIPath, command + ' ' + parameters); @@ -49,16 +65,7 @@ public static RunCommandResult RunAICLICommand(string command, string parameters p.StartInfo.RedirectStandardInput = true; } - try - { - p.Start(); - } - catch (System.ComponentModel.Win32Exception) - { - // App may be updating, give another try. - WaitForDeploymentFinish(); - p.Start(); - } + p.Start(); if (!string.IsNullOrEmpty(stdIn)) { @@ -75,22 +82,64 @@ public static RunCommandResult RunAICLICommand(string command, string parameters if (!string.IsNullOrEmpty(result.StdErr)) { - TestContext.Error.WriteLine("Command run error. " + inputMsg + " Error: " + result.StdErr); + TestContext.Error.WriteLine("Command run error. Error: " + result.StdErr); } if (VerboseLogging && !string.IsNullOrEmpty(result.StdOut)) { - TestContext.Out.WriteLine("Command run output. " + inputMsg + " Output: " + result.StdOut); + TestContext.Out.WriteLine("Command run output. Output: " + result.StdOut); } } else { - throw new TimeoutException("Command run timed out. " + inputMsg); + throw new TimeoutException("Command run timed out."); } return result; } + public static RunCommandResult RunAICLICommandViaInvokeCommandInDesktopPackage(string command, string parameters, string stdIn = null, int timeOut = 60000) + { + string cmdCommandPiped = ""; + if (!string.IsNullOrEmpty(stdIn)) + { + cmdCommandPiped += $"echo {stdIn} | "; + } + + string workDirectory = GetRandomTestDir(); + string exitCodeFile = Path.Combine(workDirectory, "ExitCode.txt"); + string stdOutFile = Path.Combine(workDirectory, "StdOut.txt"); + string stdErrFile = Path.Combine(workDirectory, "StdErr.txt"); + + cmdCommandPiped += $"{AICLIPath} {command} {parameters} > {stdOutFile} 2> {stdErrFile} & call echo %^ERRORLEVEL% > {exitCodeFile}"; + + string psCommand = $"Invoke-CommandInDesktopPackage -PackageFamilyName {Constants.AICLIPackageFamilyName} -AppId {Constants.AICLIAppId} -PreventBreakaway -Command cmd.exe -Args '/c \"{cmdCommandPiped}\"'"; + + var psInvokeResult = RunCommandWithResult("powershell", psCommand); + + if (psInvokeResult.ExitCode != 0) + { + // PS invocation failed, return result and no need to check piped output. + return psInvokeResult; + } + + // The PS command just launches the app and immediately returns, we'll have to wait the timeOut specified here + int waitedTime = 0; + while (!File.Exists(exitCodeFile) && waitedTime <= timeOut) + { + Thread.Sleep(1000); + waitedTime += 1000; + } + + RunCommandResult result = new RunCommandResult(); + + result.ExitCode = File.Exists(exitCodeFile) ? int.Parse(File.ReadAllText(exitCodeFile).Trim()) : unchecked((int)0x80004005); + result.StdOut = File.Exists(stdOutFile) ? File.ReadAllText(stdOutFile) : ""; + result.StdErr = File.Exists(stdErrFile) ? File.ReadAllText(stdErrFile) : ""; + + return result; + } + public static bool RunCommand(string fileName, string args, int timeOut = 60000) { return RunCommandWithResult(fileName, args, timeOut).ExitCode == 0; @@ -136,7 +185,7 @@ public static string GetTestDataFile(string fileName) public static string GetRandomTestDir() { - string randDir = Path.Combine(TestContext.CurrentContext.TestDirectory, Path.GetRandomFileName()); + string randDir = Path.Combine(TestContext.CurrentContext.TestDirectory, Path.Combine("WorkDirectory", Path.GetRandomFileName())); Directory.CreateDirectory(randDir); return randDir; } @@ -146,6 +195,12 @@ public static bool InstallMsix(string file) return RunCommand("powershell", $"Add-AppxPackage \"{file}\""); } + public static bool InstallMsixRegister(string packagePath) + { + string manifestFile = Path.Combine(packagePath, "AppxManifest.xml"); + return RunCommand("powershell", $"Add-AppxPackage -Register \"{manifestFile}\""); + } + public static bool RemoveMsix(string name) { return RunCommand("powershell", $"Get-AppxPackage \"{name}\" | Remove-AppxPackage"); @@ -153,7 +208,7 @@ public static bool RemoveMsix(string name) public static void WaitForDeploymentFinish() { - if (IsPackagedContext) + if (PackagedContext) { // Since we are doing a lot index add/remove, and some of the methods are fire and forget. // Sometimes process start will fail because app is updating.