Skip to content

Commit

Permalink
Enable Windows device tests on net7.0 (#16756)
Browse files Browse the repository at this point in the history
* Windows (packaged) Device tests for CI (#15629)

* Build & Run Packaged Windows DeviceTests

* Disable running for Core

* Skip failing tests

* Skip Launcher tests

* Update Launcher_Tests.cs

* Update windows.cake

* Revert "Update windows.cake"

This reverts commit f94dfdd.

* Update windows.cake

* Enable WinUI Core Tests on CI (#15885)

* Fix WinUI test issues

* - enable core tests

* - core test fixes

* - ignore semantics tests

* - fix

* - syntax errors

* Correct merge mistake

* Make it work?

* Fix some tests

* Fix WebView tests

* Essentials & Graphics Windows app id fix

* Update TestUtils.DeviceTests.Runners.csproj

* Revert "Update TestUtils.DeviceTests.Runners.csproj"

This reverts commit bec734e.

* Set correct TFMs for DeviceTests

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
  • Loading branch information
jfversluis and PureWeen authored Aug 22, 2023
1 parent 2ae20b6 commit da6568b
Show file tree
Hide file tree
Showing 33 changed files with 664 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"cake.tool": {
"version": "1.2.0",
"version": "2.3.0",
"commands": [
"dotnet-cake"
]
Expand Down
44 changes: 43 additions & 1 deletion eng/cake/dotnet.cake
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// Contains .NET 6-related Cake targets
// Contains .NET related Cake targets

#addin "nuget:?package=Cake.FileHelpers&version=3.2.1"

var ext = IsRunningOnWindows() ? ".exe" : "";
var dotnetPath = $"./bin/dotnet/dotnet{ext}";
Expand Down Expand Up @@ -728,6 +730,46 @@ void RunTestWithLocalDotNet(string csproj)
});
}

void RunTestWithLocalDotNet(string csproj, string config, string pathDotnet = null, Dictionary<string,string> argsExtra = null, bool noBuild = false)
{
var name = System.IO.Path.GetFileNameWithoutExtension(csproj);
var binlog = $"{GetLogDirectory()}/{name}-{config}.binlog";
var results = $"{name}-{config}.trx";

Information("Run Test binlog: {0}", binlog);

var settings = new DotNetTestSettings
{
Configuration = config,
NoBuild = noBuild,
Loggers = {
$"trx;LogFileName={results}"
},
ResultsDirectory = GetTestResultsDirectory(),
//Verbosity = Cake.Common.Tools.DotNetCore.DotNetCoreVerbosity.Diagnostic,
ArgumentCustomization = args =>
{
args.Append($"-bl:{binlog}");
// args.Append($"/tl");
if(argsExtra != null)
{
foreach(var prop in argsExtra)
{
args.Append($"/p:{prop.Key}={prop.Value}");
}
}
return args;
}
};

if(!string.IsNullOrEmpty(pathDotnet))
{
settings.ToolPath = pathDotnet;
}

DotNetTest(csproj, settings);
}

DirectoryPath PrepareSeparateBuildContext(string dirName, bool generateDirectoryProps = false)
{
var dir = GetTempDirectory().Combine(dirName);
Expand Down
302 changes: 302 additions & 0 deletions eng/devices/windows.cake
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
#load "../cake/helpers.cake"
#load "../cake/dotnet.cake"

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

string TARGET = Argument("target", "Test");

const string defaultVersion = "10.0.19041";
const string dotnetVersion = "net7.0";

// required
FilePath PROJECT = Argument("project", EnvironmentVariable("WINDOWS_TEST_PROJECT") ?? "");
// Not used for Windows. TODO Use this for packaged vs unpackaged?
string TEST_DEVICE = Argument("device", EnvironmentVariable("WINDOWS_TEST_DEVICE") ?? $"");
// Package ID of the WinUI Application
var PACKAGEID = Argument("packageid", EnvironmentVariable("WINDOWS_TEST_PACKAGE_ID") ?? $"");

// optional
var DOTNET_PATH = Argument("dotnet-path", EnvironmentVariable("DOTNET_PATH"));
var TARGET_FRAMEWORK = Argument("tfm", EnvironmentVariable("TARGET_FRAMEWORK") ?? $"{dotnetVersion}-windows{defaultVersion}");
var BINLOG_ARG = Argument("binlog", EnvironmentVariable("WINDOWS_TEST_BINLOG") ?? "");
DirectoryPath BINLOG_DIR = string.IsNullOrEmpty(BINLOG_ARG) && !string.IsNullOrEmpty(PROJECT.FullPath) ? PROJECT.GetDirectory() : BINLOG_ARG;
var TEST_APP = Argument("app", EnvironmentVariable("WINDOWS_TEST_APP") ?? "");
var DEVICETEST_APP = Argument("devicetestapp", EnvironmentVariable("WINDOWS_DEVICETEST_APP") ?? "");
FilePath TEST_APP_PROJECT = Argument("appproject", EnvironmentVariable("WINDOWS_TEST_APP_PROJECT") ?? PROJECT);
FilePath DEVICETEST_APP_PROJECT = Argument("appproject", EnvironmentVariable("WINDOWS_DEVICETEST_APP_PROJECT") ?? PROJECT);
var TEST_RESULTS = Argument("results", EnvironmentVariable("WINDOWS_TEST_RESULTS") ?? "");
string CONFIGURATION = Argument("configuration", "Debug");

var windowsVersion = Argument("apiversion", EnvironmentVariable("WINDOWS_PLATFORM_VERSION") ?? defaultVersion);

// other
string PLATFORM = "windows";
string DOTNET_PLATFORM = $"win10-x64";
bool DEVICE_CLEANUP = Argument("cleanup", true);
string certificateThumbprint = "";

// Certificate Common Name to use/generate (eg: CN=DotNetMauiTests)
var certCN = Argument("commonname", "DotNetMAUITests");

// Uninstall the deployed app
var uninstallPS = new Action(() =>
{
try {
StartProcess("powershell",
"$app = Get-AppxPackage -Name " + PACKAGEID + "; if ($app) { Remove-AppxPackage -Package $app.PackageFullName }");
} catch { }
});

Information("Project File: {0}", PROJECT);
Information("Application ID: {0}", PACKAGEID);
Information("Build Binary Log (binlog): {0}", BINLOG_DIR);
Information("Build Platform: {0}", PLATFORM);
Information("Build Configuration: {0}", CONFIGURATION);

Information("Windows version: {0}", windowsVersion);

Setup(context =>
{
Cleanup();
});

Teardown(context =>
{
Cleanup();
});

void Cleanup()
{
if (!DEVICE_CLEANUP)
return;
}

Task("Cleanup");

Task("GenerateMsixCert")
.Does(() =>
{
// We need the key to be in LocalMachine -> TrustedPeople to install the msix signed with the key
var localTrustedPeopleStore = new X509Store("TrustedPeople", StoreLocation.LocalMachine);
localTrustedPeopleStore.Open(OpenFlags.ReadWrite);
// We need to have the key also in CurrentUser -> My so that the msix can be built and signed
// with the key by passing the key's thumbprint to the build
var currentUserMyStore = new X509Store("My", StoreLocation.CurrentUser);
currentUserMyStore.Open(OpenFlags.ReadWrite);
certificateThumbprint = localTrustedPeopleStore.Certificates.FirstOrDefault(c => c.Subject.Contains(certCN))?.Thumbprint;
Information("Cert thumbprint: " + certificateThumbprint ?? "null");
if (string.IsNullOrEmpty(certificateThumbprint))
{
Information("Generating cert");
var rsa = RSA.Create();
var req = new CertificateRequest("CN=" + certCN, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection
{
new Oid
{
Value = "1.3.6.1.5.5.7.3.3",
FriendlyName = "Code Signing"
}
}, false));
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
req.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
false));
req.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(req.PublicKey, false));
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
if (OperatingSystem.IsWindows())
{
cert.FriendlyName = certCN;
}
var tmpCert = new X509Certificate2(cert.Export(X509ContentType.Pfx), "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
certificateThumbprint = tmpCert.Thumbprint;
localTrustedPeopleStore.Add(tmpCert);
currentUserMyStore.Add(tmpCert);
}
localTrustedPeopleStore.Close();
currentUserMyStore.Close();
});

Task("Build")
.IsDependentOn("GenerateMsixCert")
.WithCriteria(!string.IsNullOrEmpty(PROJECT.FullPath))
.WithCriteria(!string.IsNullOrEmpty(PACKAGEID))
.Does(() =>
{
var name = System.IO.Path.GetFileNameWithoutExtension(PROJECT.FullPath);
var binlog = $"{BINLOG_DIR}/{name}-{CONFIGURATION}-windows.binlog";
SetDotNetEnvironmentVariables(DOTNET_PATH);
Information("Building and publishing device test app");
// Build the app in publish mode
// Using the certificate thumbprint for the cert we just created
var s = new DotNetPublishSettings();
s.Configuration = CONFIGURATION;
s.Framework = TARGET_FRAMEWORK;
s.MSBuildSettings = new DotNetMSBuildSettings();
s.MSBuildSettings.Properties.Add("RuntimeIdentifierOverride", new List<string> { "win10-x64" });
s.MSBuildSettings.Properties.Add("PackageCertificateThumbprint", new List<string> { certificateThumbprint });
s.MSBuildSettings.Properties.Add("AppxPackageSigningEnabled", new List<string> { "True" });
DotNetPublish(PROJECT.FullPath, s);
});

Task("Test")
.IsDependentOn("Build")
.IsDependentOn("SetupTestPaths")
.Does(() =>
{
CleanDirectories(TEST_RESULTS);
Information("Cleaned directories");
// Try to uninstall the app if it exists from before
uninstallPS();
Information("Uninstalled previously deployed app");
var projectDir = PROJECT.GetDirectory();
var cerPath = GetFiles(projectDir.FullPath + "/**/AppPackages/*/*.cer").First();
var msixPath = GetFiles(projectDir.FullPath + "/**/AppPackages/*/*.msix").First();
var testResultsFile = MakeAbsolute((DirectoryPath)TEST_RESULTS).FullPath.Replace("/", "\\") + $"\\TestResults-{PACKAGEID.Replace(".", "_")}.xml";
Information($"Found MSIX, installing: {msixPath}");
Information($"Test Results File: {testResultsFile}");
if (FileExists(testResultsFile))
{
DeleteFile(testResultsFile);
}
// Install dependencies
var dependencies = GetFiles(projectDir.FullPath + "/**/AppPackages/**/Dependencies/x64/*.msix");
foreach (var dep in dependencies) {
Information("Installing Dependency MSIX: {0}", dep);
StartProcess("powershell", "Add-AppxPackage -Path \"" + MakeAbsolute(dep).FullPath + "\"");
}
// Install the DeviceTests app
StartProcess("powershell", "Add-AppxPackage -Path \"" + MakeAbsolute(msixPath).FullPath + "\"");
var startArgs = "Start-Process shell:AppsFolder\\$((Get-AppxPackage -Name \"" + PACKAGEID + "\").PackageFamilyName)!App -Args \"" + testResultsFile + "\"";
Information(startArgs);
// Start the DeviceTests app
StartProcess("powershell", startArgs);
var waited = 0;
while (!FileExists(testResultsFile)) {
System.Threading.Thread.Sleep(1000);
waited++;
Information($"Waiting {waited} second(s) for tests to finish...");
if (waited >= 120)
break;
}
if(!FileExists(testResultsFile))
{
throw new Exception($"Test results file not found after {waited} seconds, process might have crashed or not completed yet.");
}
Information($"Tests Finished");
});


Task("SetupTestPaths")
.Does(() => {
// UI Tests
if (string.IsNullOrEmpty(TEST_APP) )
{
if (string.IsNullOrEmpty(TEST_APP_PROJECT.FullPath))
{
throw new Exception("If no app was specified, an app must be provided.");
}
var binDir = TEST_APP_PROJECT.GetDirectory().Combine("bin").Combine(CONFIGURATION + "/" + $"{dotnetVersion}-windows{windowsVersion}").Combine(DOTNET_PLATFORM).FullPath;
Information("BinDir: {0}", binDir);
var apps = GetFiles(binDir + "/*.exe").Where(c => !c.FullPath.EndsWith("createdump.exe"));
TEST_APP = apps.First().FullPath;
}
if (string.IsNullOrEmpty(TEST_RESULTS))
{
TEST_RESULTS = TEST_APP + "-results";
}
// Device Tests
if (string.IsNullOrEmpty(DEVICETEST_APP) )
{
if (string.IsNullOrEmpty(DEVICETEST_APP_PROJECT.FullPath))
{
throw new Exception("If no app was specified, an app must be provided.");
}
DEVICETEST_APP = DEVICETEST_APP_PROJECT.GetFilenameWithoutExtension().ToString();
}
if (string.IsNullOrEmpty(TEST_RESULTS))
{
TEST_RESULTS = DEVICETEST_APP + "-results";
}
CreateDirectory(TEST_RESULTS);
Information("Test Device: {0}", TEST_DEVICE);
Information("UITest App: {0}", TEST_APP);
Information("DeviceTest App: {0}", DEVICETEST_APP);
Information("Test Results Directory: {0}", TEST_RESULTS);
});

Task("uitest")
.IsDependentOn("SetupTestPaths")
.Does(() =>
{
CleanDirectories(TEST_RESULTS);
Information("Build UITests project {0}",PROJECT.FullPath);
var name = System.IO.Path.GetFileNameWithoutExtension(PROJECT.FullPath);
var binlog = $"{BINLOG_DIR}/{name}-{CONFIGURATION}-windows.binlog";
var dd = MakeAbsolute(Directory("../../bin/dotnet/"));
Information("DOTNET_PATH: {0}", dd);
var toolPath = $"{dd}/dotnet.exe";
Information("toolPath: {0}", toolPath);
SetDotNetEnvironmentVariables(dd.FullPath);
DotNetBuild(PROJECT.FullPath, new DotNetBuildSettings {
Configuration = CONFIGURATION,
ToolPath = toolPath,
ArgumentCustomization = args => args
.Append("/p:ExtraDefineConstants=WINTEST")
.Append("/bl:" + binlog)
.Append("/maxcpucount:1")
//.Append("/tl")
});
SetEnvironmentVariable("WINDOWS_APP_PATH", TEST_APP);
SetEnvironmentVariable("APPIUM_LOG_FILE", $"{BINLOG_DIR}/appium_windows.log");
Information("Run UITests project {0}",PROJECT.FullPath);
RunTestWithLocalDotNet(PROJECT.FullPath, CONFIGURATION, toolPath, noBuild: true);
});

RunTarget(TARGET);
Loading

0 comments on commit da6568b

Please sign in to comment.