Skip to content

Commit

Permalink
Fix E2E test on build server
Browse files Browse the repository at this point in the history
  • Loading branch information
yao-msft committed Apr 17, 2020
1 parent 2024465 commit d50a975
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 36 deletions.
20 changes: 8 additions & 12 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -155,15 +146,20 @@ 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
inputs:
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
Expand Down
5 changes: 4 additions & 1 deletion src/AppInstallerCLIE2ETests/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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.
Expand Down
31 changes: 25 additions & 6 deletions src/AppInstallerCLIE2ETests/SetUpFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace AppInstallerCLIE2ETests
using Microsoft.Win32;
using NUnit.Framework;
using System;
using System.IO;

[SetUpFixture]
public class SetUpFixture
Expand All @@ -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";
}
Expand All @@ -43,17 +50,29 @@ 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);

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)));
}
}
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCLIE2ETests/SourceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCLIE2ETests/Test.runsettings
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
<Parameter name="VerboseLogging" value="false" />
<Parameter name="AICLIPath" value="AppInst.exe" />
<Parameter name="AICLIPackagePath" value="AppInstallerCLIPackage.appxbundle" />
<Parameter name="LooseFileRegistration" value="false" />
<Parameter name="InvokeCommandInDesktopPackage" value="false" />
</TestRunParameters>
</RunSettings>
89 changes: 72 additions & 17 deletions src/AppInstallerCLIE2ETests/TestCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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))
{
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -146,14 +195,20 @@ 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");
}

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.
Expand Down

0 comments on commit d50a975

Please sign in to comment.