[tests] Add sample tester. (#5870)
* [tests] Add sample tester.
Add a unit project that looks for iOS/macOS/tvOS sample projects in several
repositories, and builds them all.
* [tests][sampletester] Remove known issue which has now been fixed.
* [tests] Only run sample tests on CI in Azure Devops.
* Remove the possibility of automatically running the sample tests with
xharness (so the sample tests won't run on PR bots or internal bots when the
'run-all-tests' label is added). It's still possible to run the sample tests
manually from the xharness web UI.
* Automatically trigger the sample test run in Azure Devops if the
'run-sample-tests' label is applied to a PR (and that PR is executed on
internal Jenkins).
* Fix typo.
* Fix path.
* Verbose output to track down scheduling failure.
* Bump maccore to get improved debug spew.
Diff: f527c9c526..f89d74b165
* [tests][sampletester] Fix build for TodoWCF.
This commit is contained in:
Родитель
d988124684
Коммит
bd457212d5
|
@ -588,15 +588,15 @@ timestamps {
|
|||
dir ('xamarin-macios') {
|
||||
stage ('Launch external tests') {
|
||||
currentStage = "${STAGE_NAME}"
|
||||
// VSTS does not allow any branch name anymore, it has to be an existing branch.
|
||||
// Since pull requests don't create branches in the xamarin org (that VSTS sees at least),
|
||||
// I've created a 'pull-request' branch which we'll use for pull requests.
|
||||
def vsts_branch = isPr ? "pull-request" : branchName;
|
||||
if (isPr) {
|
||||
echo "Currently not launching external tests for pull requests"
|
||||
} else {
|
||||
def outputFile = "${workspace}/xamarin-macios/wrench-launch-external.output.tmp"
|
||||
try {
|
||||
// VSTS does not allow any branch name anymore, it has to be an existing branch.
|
||||
// Since pull requests don't create branches in the xamarin org (that VSTS sees at least),
|
||||
// I've created a 'pull-request' branch which we'll use for pull requests.
|
||||
def vsts_branch = isPr ? "pull-request" : branchName;
|
||||
withCredentials ([string (credentialsId: 'macios_provisionator_pat', variable: 'PROVISIONATOR_VSTS_PAT')]) {
|
||||
sh ("make -C ${workspace}/xamarin-macios/tests wrench-launch-external MAC_PACKAGE_URL=${xmPackageUrl} IOS_PACKAGE_URL=${xiPackageUrl} WRENCH_URL=${env.RUN_DISPLAY_URL} BUILD_REVISION=${gitHash} BUILD_LANE=${vsts_branch} BUILD_WORK_HOST=${env.NODE_NAME} 2>&1 | tee ${outputFile}")
|
||||
}
|
||||
|
@ -608,6 +608,21 @@ timestamps {
|
|||
sh ("rm -f '${outputFile}'")
|
||||
}
|
||||
}
|
||||
|
||||
if (isPr && githubGetPullRequestLabels ().contains ("run-sample-tests")) {
|
||||
def outputFile = "${workspace}/xamarin-macios/wrench-launch-external.output.tmp"
|
||||
try {
|
||||
withCredentials ([string (credentialsId: 'macios_provisionator_pat', variable: 'PROVISIONATOR_VSTS_PAT')]) {
|
||||
sh ("make -C ${workspace}/maccore/tests/external wrench-launch-sample-builds 'BUILD_REVISION=${gitHash}' 'BUILD_BRANCH=${vsts_branch}' V=1 2>&1 | tee ${outputFile}")
|
||||
}
|
||||
processAtMonkeyWrench (outputFile)
|
||||
} catch (error) {
|
||||
echo ("🚫 Launching external sample tests failed: ${error} 🚫")
|
||||
manager.addWarningBadge ("Failed to launch external sample tests")
|
||||
} finally {
|
||||
sh ("rm -f '${outputFile}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Install Provisioning Profiles') {
|
||||
|
|
|
@ -56,8 +56,14 @@ else
|
|||
echo "Not enabling device build; no label named 'enable-device-build' was found."
|
||||
fi
|
||||
fi
|
||||
|
||||
if ./jenkins/fetch-pr-labels.sh --check=run-sample-tests; then
|
||||
echo "The sample tests won't be triggered from public jenkins even if the 'run-sample-tests' label is set (build on internal Jenkins instead)."
|
||||
printf "ℹ️ The sample tests won't be triggered from public jenkins even if the 'run-sample-tests' label is set (build on internal Jenkins instead)." >> "$WORKSPACE/jenkins/pr-comments.md"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if test -z "$ENABLE_DEVICE_BUILD"; then
|
||||
CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-ios-device"
|
||||
fi
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
ifdef ENABLE_XAMARIN
|
||||
NEEDED_MACCORE_VERSION := 9eef60828aa36655aeae220143e8d088e0f073ce
|
||||
NEEDED_MACCORE_VERSION := f89d74b16528b736b9d449def06335b2c4cddbf3
|
||||
NEEDED_MACCORE_BRANCH := master
|
||||
|
||||
MACCORE_DIRECTORY := maccore
|
||||
|
|
|
@ -10,7 +10,7 @@ using Xamarin.Utils;
|
|||
|
||||
namespace Xamarin.Tests
|
||||
{
|
||||
class Configuration
|
||||
static partial class Configuration
|
||||
{
|
||||
public const string XI_ProductName = "MonoTouch";
|
||||
public const string XM_ProductName = "Xamarin.Mac";
|
||||
|
@ -19,6 +19,7 @@ namespace Xamarin.Tests
|
|||
|
||||
static string mt_root;
|
||||
static string ios_destdir;
|
||||
static string mac_destdir;
|
||||
public static string mt_src_root;
|
||||
public static string sdk_version;
|
||||
public static string watchos_sdk_version;
|
||||
|
@ -42,6 +43,32 @@ namespace Xamarin.Tests
|
|||
public static bool include_watchos;
|
||||
public static bool include_device;
|
||||
|
||||
static bool? use_system; // if the system-installed XI/XM should be used instead of the local one.
|
||||
public static bool UseSystem {
|
||||
get {
|
||||
if (!use_system.HasValue)
|
||||
use_system = !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("TESTS_USE_SYSTEM"));
|
||||
return use_system.Value;
|
||||
}
|
||||
set {
|
||||
use_system = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string XcodeLocation {
|
||||
get {
|
||||
return xcode_root;
|
||||
}
|
||||
}
|
||||
|
||||
public static string IOS_DESTDIR {
|
||||
get { return ios_destdir; }
|
||||
}
|
||||
|
||||
public static string MAC_DESTDIR {
|
||||
get { return mac_destdir; }
|
||||
}
|
||||
|
||||
// This is the location of an Xcode which is older than the recommended one.
|
||||
public static string GetOldXcodeRoot (Version min_version = null)
|
||||
{
|
||||
|
@ -99,7 +126,7 @@ namespace Xamarin.Tests
|
|||
|
||||
static void ParseConfigFiles ()
|
||||
{
|
||||
var test_config = FindConfigFiles ("test.config");
|
||||
var test_config = FindConfigFiles (UseSystem ? "test-system.config" : "test.config");
|
||||
if (!test_config.Any ()) {
|
||||
// Run 'make test.config' in the tests/ directory
|
||||
// First find the tests/ directory
|
||||
|
@ -202,6 +229,7 @@ namespace Xamarin.Tests
|
|||
|
||||
mt_root = GetVariable ("MONOTOUCH_PREFIX", "/Library/Frameworks/Xamarin.iOS.framework/Versions/Current");
|
||||
ios_destdir = GetVariable ("IOS_DESTDIR", null);
|
||||
mac_destdir = GetVariable ("MAC_DESTDIR", null);
|
||||
sdk_version = GetVariable ("IOS_SDK_VERSION", "8.0");
|
||||
watchos_sdk_version = GetVariable ("WATCH_SDK_VERSION", "2.0");
|
||||
tvos_sdk_version = GetVariable ("TVOS_SDK_VERSION", "9.0");
|
||||
|
@ -235,6 +263,7 @@ namespace Xamarin.Tests
|
|||
Console.WriteLine ("Test configuration:");
|
||||
Console.WriteLine (" MONOTOUCH_PREFIX={0}", mt_root);
|
||||
Console.WriteLine (" IOS_DESTDIR={0}", ios_destdir);
|
||||
Console.WriteLine (" MAC_DESTDIR={0}", mac_destdir);
|
||||
Console.WriteLine (" SDK_VERSION={0}", sdk_version);
|
||||
Console.WriteLine (" XCODE_ROOT={0}", xcode_root);
|
||||
Console.WriteLine (" XCODE5_ROOT={0}", xcode5_root);
|
||||
|
|
|
@ -419,15 +419,96 @@ namespace Xamarin.Tests
|
|||
}
|
||||
|
||||
static class ExecutionHelper {
|
||||
static int Execute (string fileName, string[] arguments, StringBuilder stdout, StringBuilder stderr, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, string.Join (" ", arguments.Select ((v) => StringUtils.Quote (v))));
|
||||
return Execute (psi, (line) => {
|
||||
lock (stdout)
|
||||
stdout.AppendLine (line);
|
||||
}, (line) => {
|
||||
lock (stderr)
|
||||
stderr.AppendLine (line);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
static int Execute (ProcessStartInfo psi, StringBuilder stdout, StringBuilder stderr, TimeSpan? timeout = null)
|
||||
{
|
||||
return Execute (psi, (line) => {
|
||||
lock (stdout)
|
||||
stdout.AppendLine (line);
|
||||
}, (line) => {
|
||||
lock (stderr)
|
||||
stderr.AppendLine (line);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, string [] arguments, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
return Execute (fileName, arguments, null, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, string [] arguments, string working_directory = null, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, string.Join (" ", arguments.Select ((v) => StringUtils.Quote (v))));
|
||||
psi.WorkingDirectory = working_directory;
|
||||
return Execute (psi, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, string [] arguments, out StringBuilder output, string working_directory = null, TimeSpan? timeout = null)
|
||||
{
|
||||
output = new StringBuilder ();
|
||||
var psi = new ProcessStartInfo (fileName, string.Join (" ", arguments.Select ((v) => StringUtils.Quote (v))));
|
||||
psi.WorkingDirectory = working_directory;
|
||||
var capturedOutput = output;
|
||||
var callback = new Action<string> ((v) => {
|
||||
lock (psi)
|
||||
capturedOutput.AppendLine (v);
|
||||
});
|
||||
return Execute (psi, callback, callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, string [] arguments, out bool timed_out, Dictionary<string, string> environment_variables = null, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, string.Join (" ", StringUtils.Quote (arguments)));
|
||||
if (environment_variables != null) {
|
||||
foreach (var ev in environment_variables)
|
||||
psi.EnvironmentVariables [ev.Key] = ev.Value;
|
||||
}
|
||||
return Execute (psi, out timed_out, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, string [] arguments, out bool timed_out, string working_directory = null, Dictionary<string, string> environment_variables = null, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, string.Join (" ", StringUtils.Quote (arguments)));
|
||||
psi.WorkingDirectory = working_directory;
|
||||
if (environment_variables != null) {
|
||||
foreach (var ev in environment_variables)
|
||||
psi.EnvironmentVariables [ev.Key] = ev.Value;
|
||||
}
|
||||
return Execute (psi, out timed_out, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (ProcessStartInfo psi, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
return Execute (psi, out var _, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (ProcessStartInfo psi, out bool timed_out, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var watch = new Stopwatch ();
|
||||
watch.Start ();
|
||||
|
||||
if (stdout_callback == null)
|
||||
stdout_callback = Console.WriteLine;
|
||||
if (stderr_callback == null)
|
||||
stderr_callback = Console.Error.WriteLine;
|
||||
|
||||
try {
|
||||
psi.UseShellExecute = false;
|
||||
psi.RedirectStandardError = true;
|
||||
psi.RedirectStandardOutput = true;
|
||||
if (!string.IsNullOrEmpty (psi.WorkingDirectory))
|
||||
Console.Write ($"cd {StringUtils.Quote (psi.WorkingDirectory)} && ");
|
||||
Console.WriteLine ("{0} {1}", psi.FileName, psi.Arguments);
|
||||
using (var p = new Process ()) {
|
||||
p.StartInfo = psi;
|
||||
|
@ -442,8 +523,7 @@ namespace Xamarin.Tests
|
|||
{
|
||||
string l;
|
||||
while ((l = p.StandardOutput.ReadLine ()) != null) {
|
||||
lock (stdout)
|
||||
stdout.AppendLine (l);
|
||||
stdout_callback (l);
|
||||
}
|
||||
})
|
||||
{
|
||||
|
@ -455,8 +535,7 @@ namespace Xamarin.Tests
|
|||
{
|
||||
string l;
|
||||
while ((l = p.StandardError.ReadLine ()) != null) {
|
||||
lock (stderr)
|
||||
stderr.AppendLine (l);
|
||||
stderr_callback (l);
|
||||
}
|
||||
})
|
||||
{
|
||||
|
@ -467,6 +546,7 @@ namespace Xamarin.Tests
|
|||
if (timeout == null)
|
||||
timeout = TimeSpan.FromMinutes (5);
|
||||
if (!p.WaitForExit ((int) timeout.Value.TotalMilliseconds)) {
|
||||
timed_out = true;
|
||||
Console.WriteLine ("Command didn't finish in {0} minutes:", timeout.Value.TotalMinutes);
|
||||
Console.WriteLine ("{0} {1}", p.StartInfo.FileName, p.StartInfo.Arguments);
|
||||
Console.WriteLine ("Will now kill the process");
|
||||
|
@ -475,6 +555,8 @@ namespace Xamarin.Tests
|
|||
Console.WriteLine ("Kill failed to kill in 1 second !?");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
timed_out = false;
|
||||
}
|
||||
|
||||
outReader.Join (TimeSpan.FromSeconds (1));
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
TestResult*.html
|
||||
TestResult*.xml
|
||||
.failed-stamp
|
|
@ -0,0 +1,55 @@
|
|||
using System.Reflection;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Xamarin.Tests {
|
||||
[TestFixture]
|
||||
[Parallelizable (ParallelScope.Self)]
|
||||
public abstract class BaseTester {
|
||||
string repository;
|
||||
public virtual string Repository {
|
||||
get {
|
||||
if (repository == null)
|
||||
repository = (string) GetType ().GetField ("REPO", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).GetValue (null);
|
||||
return repository;
|
||||
}
|
||||
protected set {
|
||||
repository = value;
|
||||
}
|
||||
}
|
||||
|
||||
string hash;
|
||||
public virtual string Hash {
|
||||
get {
|
||||
if (hash == null)
|
||||
hash = (string) GetType ().GetField ("HASH", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).GetValue (null);
|
||||
return hash;
|
||||
}
|
||||
protected set {
|
||||
hash = value;
|
||||
}
|
||||
}
|
||||
|
||||
string org;
|
||||
public virtual string Org {
|
||||
get {
|
||||
if (org == null)
|
||||
org = (string) GetType ().GetField ("ORG", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)?.GetValue (null) ?? "xamarin";
|
||||
return org;
|
||||
}
|
||||
protected set {
|
||||
org = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected BaseTester ()
|
||||
{
|
||||
}
|
||||
|
||||
protected BaseTester (string repository, string hash)
|
||||
{
|
||||
this.repository = repository;
|
||||
this.hash = hash;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Xamarin.Tests {
|
||||
public partial class Configuration {
|
||||
public static string SampleRootDirectory {
|
||||
get {
|
||||
var rv = Path.Combine (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location), "repositories");
|
||||
var nuget_conf = Path.Combine (rv, "NuGet.config");
|
||||
Directory.CreateDirectory (rv);
|
||||
if (!File.Exists (nuget_conf)) {
|
||||
// We're cloning into a subdirectory of xamarin-macios, which already has a NuGet.config
|
||||
// So create a Nuget.config that clears out any previous configuration, so that none of the
|
||||
// sample tests pick up xamarin-macios' NuGet.config.
|
||||
File.WriteAllText (nuget_conf,
|
||||
@"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<configuration>
|
||||
<config>
|
||||
<clear />
|
||||
</config>
|
||||
</configuration>
|
||||
");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Xamarin.Tests;
|
||||
|
||||
public static class GitHub {
|
||||
public static string [] GetProjects (string user, string repo, string hash)
|
||||
{
|
||||
IEnumerable<string> files;
|
||||
|
||||
var dir = CloneRepository (user, repo, hash);
|
||||
files = Directory.GetFiles (dir, "*.*", SearchOption.AllDirectories);
|
||||
files = files.Select ((v) => v.Substring (dir.Length).TrimStart ('/'));
|
||||
|
||||
return files
|
||||
.Where ((v) => {
|
||||
var ext = Path.GetExtension (v).ToLowerInvariant ();
|
||||
return ext == ".csproj" || ext == ".fsproj";
|
||||
}).ToArray ();
|
||||
}
|
||||
|
||||
public static string CloneRepository (string org, string repo, string hash, bool clean = true)
|
||||
{
|
||||
var repo_dir = Path.Combine (Configuration.SampleRootDirectory, repo);
|
||||
|
||||
if (!Directory.Exists (repo_dir)) {
|
||||
Console.WriteLine ($"Cloning https://github.com/{org}/{repo} (hash: {hash})");
|
||||
Directory.CreateDirectory (repo_dir);
|
||||
Assert.AreEqual (0, ExecutionHelper.Execute ("git", new string [] { "init" }, working_directory: repo_dir, timeout: TimeSpan.FromSeconds (10)), "git init");
|
||||
Assert.AreEqual (0, ExecutionHelper.Execute ("git", new string [] { "remote", "add", "origin", $"https://github.com/{org}/{repo}" }, working_directory: repo_dir, timeout: TimeSpan.FromSeconds (10)), "git remote add");
|
||||
Assert.AreEqual (0, ExecutionHelper.Execute ("git", new string [] { "fetch" }, working_directory: repo_dir, timeout: TimeSpan.FromMinutes (10)), "git fetch");
|
||||
Assert.AreEqual (0, ExecutionHelper.Execute ("git", new string [] { "checkout", "-b", "temporary-sample-testing-branch", hash }, working_directory: repo_dir, timeout: TimeSpan.FromMinutes (1)), "git checkout");
|
||||
Assert.AreEqual (0, ExecutionHelper.Execute ("git", new string [] { "submodule", "update", "--init", "--recursive" }, working_directory: repo_dir, timeout: TimeSpan.FromMinutes (10)), "git submodule update");
|
||||
|
||||
ExecutionHelper.Execute ("git", new string [] { "log", "--oneline", "--pretty=* @%h %s", "HEAD", "^origin/master" }, out var output1, working_directory: repo_dir, timeout: TimeSpan.FromSeconds (10));
|
||||
if (output1.Length > 0) {
|
||||
Console.WriteLine ("Commits not in origin/master:");
|
||||
Console.WriteLine (output1.ToString ().TrimEnd ('\n'));
|
||||
}
|
||||
ExecutionHelper.Execute ("git", new string [] { "log", "--oneline", "--pretty=* %h %s", "origin/master", "^HEAD" }, out var output2, working_directory: repo_dir, timeout: TimeSpan.FromSeconds (10));
|
||||
if (output2.Length > 0) {
|
||||
Console.WriteLine ("Commits in origin/master not being tested:");
|
||||
Console.WriteLine (output2.ToString ().TrimEnd ('\n'));
|
||||
}
|
||||
|
||||
} else if (clean) {
|
||||
Assert.AreEqual (0, ExecutionHelper.Execute ("git", new string [] { "reset", "--hard", hash }, working_directory: repo_dir, timeout: TimeSpan.FromMinutes (1)), "git checkout");
|
||||
CleanRepository (repo_dir);
|
||||
}
|
||||
|
||||
return repo_dir;
|
||||
}
|
||||
|
||||
public static void CleanRepository (string directory, bool submodules = true)
|
||||
{
|
||||
ExecutionHelper.Execute ("git", new string [] { "clean", "-xffdq" }, working_directory: directory, timeout: TimeSpan.FromSeconds (30));
|
||||
if (submodules)
|
||||
ExecutionHelper.Execute ("git", new string [] { "submodule", "foreach", "--recursive", "clean -xffdq" }, working_directory: directory, timeout: TimeSpan.FromSeconds (60));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
<xsl:output omit-xml-declaration="yes" indent="yes" />
|
||||
<xsl:template match="/">
|
||||
<xsl:text disable-output-escaping="yes"><!DOCTYPE html> </xsl:text>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test results</title>
|
||||
<style type="text/css">
|
||||
|
||||
.strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.smllabel {
|
||||
width:110px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
li {
|
||||
font-family: monospace
|
||||
}
|
||||
.collapsed
|
||||
{
|
||||
border:0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<xsl:apply-templates/>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-run">
|
||||
|
||||
<!-- Command Line -->
|
||||
<h4>Command Line</h4>
|
||||
<pre>
|
||||
<xsl:value-of select="command-line"/>
|
||||
</pre>
|
||||
|
||||
<!-- Runtime Environment -->
|
||||
<h4>Runtime Environment</h4>
|
||||
|
||||
<table id="runtime">
|
||||
<tr>
|
||||
<td class="smllabel right">OS Version:</td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="test-suite/environment/@os-version[1]"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="smllabel right">CLR Version:</td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="@clr-version"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<xsl:text disable-output-escaping="yes"><![CDATA[ ]]></xsl:text>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="smllabel right">NUnit Version:</td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="@engine-version"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Test Files -->
|
||||
<div id="testfiles">
|
||||
<h4>Test Files</h4>
|
||||
<xsl:if test="count(test-suite[@type='Assembly']) > 0">
|
||||
<ol>
|
||||
<xsl:for-each select="test-suite[@type='Assembly']">
|
||||
<li>
|
||||
<xsl:value-of select="@fullname"/>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ol>
|
||||
</xsl:if>
|
||||
</div>
|
||||
|
||||
<!-- Successes -->
|
||||
<xsl:if test="//test-case[@result='Passed']">
|
||||
<details>
|
||||
<summary>Successes</summary>
|
||||
<ol>
|
||||
<xsl:apply-templates select="//test-case[@result='Passed']"/>
|
||||
</ol>
|
||||
</details>
|
||||
</xsl:if>
|
||||
|
||||
<!-- Tests Not Run -->
|
||||
<xsl:if test="//test-case[@result='Skipped']">
|
||||
<details>
|
||||
<summary>Tests Not Run</summary>
|
||||
<ol>
|
||||
<xsl:apply-templates select="//test-case[@result='Skipped']"/>
|
||||
</ol>
|
||||
</details>
|
||||
</xsl:if>
|
||||
|
||||
<!-- Errors and Failures -->
|
||||
<xsl:if test="//test-case[failure]">
|
||||
<details>
|
||||
<summary>Errors and Failures</summary>
|
||||
<ol>
|
||||
<xsl:apply-templates select="//test-case[failure]"/>
|
||||
</ol>
|
||||
</details>
|
||||
</xsl:if>
|
||||
|
||||
<!-- Run Settings (gets first one found) -->
|
||||
<xsl:variable name="settings" select ="//settings[1]" />
|
||||
|
||||
<h4>Run Settings</h4>
|
||||
<ul>
|
||||
<li>
|
||||
DefaultTimeout: <xsl:value-of select="$settings/setting[@name='DefaultTimeout']/@value"/>
|
||||
</li>
|
||||
<li>
|
||||
WorkDirectory: <xsl:value-of select="$settings/setting[@name='WorkDirectory']/@value"/>
|
||||
</li>
|
||||
<li>
|
||||
ImageRuntimeVersion: <xsl:value-of select="$settings/setting[@name='ImageRuntimeVersion']/@value"/>
|
||||
</li>
|
||||
<li>
|
||||
ImageTargetFrameworkName: <xsl:value-of select="$settings/setting[@name='ImageTargetFrameworkName']/@value"/>
|
||||
</li>
|
||||
<li>
|
||||
ImageRequiresX86: <xsl:value-of select="$settings/setting[@name='ImageRequiresX86']/@value"/>
|
||||
</li>
|
||||
<li>
|
||||
ImageRequiresDefaultAppDomainAssemblyResolver: <xsl:value-of select="$settings/setting[@name='ImageRequiresDefaultAppDomainAssemblyResolver']/@value"/>
|
||||
</li>
|
||||
<li>
|
||||
NumberOfTestWorkers: <xsl:value-of select="$settings/setting[@name='NumberOfTestWorkers']/@value"/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Test Run Summary</h4>
|
||||
<table id="summary" class="collapsed">
|
||||
<tr>
|
||||
<td class="smllabel right">Overall result:</td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="@result"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="smllabel right">Test Count:</td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="@total"/>, Passed: <xsl:value-of select="@passed"/>, Failed: <xsl:value-of select="@failed"/>, Inconclusive: <xsl:value-of select="@inconclusive"/>, Skipped: <xsl:value-of select="@skipped"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- [Optional] - Failed Test Summary -->
|
||||
<xsl:if test="@failed > 0">
|
||||
<xsl:variable name="failedTotal" select="count(//test-case[@result='Failed' and not(@label)])" />
|
||||
<xsl:variable name="errorsTotal" select="count(//test-case[@result='Failed' and @label='Error'])" />
|
||||
<xsl:variable name="invalidTotal" select="count(//test-case[@result='Failed' and @label='Invalid'])" />
|
||||
<tr>
|
||||
<td class="smllabel right">Failed Tests: </td>
|
||||
<td class="left">
|
||||
Failures: <xsl:value-of select="$failedTotal"/>, Errors: <xsl:value-of select="$errorsTotal"/>, Invalid: <xsl:value-of select="$invalidTotal"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:if>
|
||||
|
||||
<!-- [Optional] - Skipped Test Summary -->
|
||||
<xsl:if test="@skipped > 0">
|
||||
<xsl:variable name="ignoredTotal" select="count(//test-case[@result='Skipped' and @label='Ignored'])" />
|
||||
<xsl:variable name="explicitTotal" select="count(//test-case[@result='Skipped' and @label='Explicit'])" />
|
||||
<xsl:variable name="otherTotal" select="count(//test-case[@result='Skipped' and not(@label='Explicit' or @label='Ignored')])" />
|
||||
<tr>
|
||||
<td class="smllabel right">Skipped Tests: </td>
|
||||
<td class="left">
|
||||
Ignored: <xsl:value-of select="$ignoredTotal"/>, Explicit: <xsl:value-of select="$explicitTotal"/>, Other: <xsl:value-of select="$otherTotal"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:if>
|
||||
|
||||
<!-- Times -->
|
||||
<tr>
|
||||
<td class="smllabel right">Start time: </td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="@start-time"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="smllabel right">End time: </td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="@end-time"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="smllabel right">Duration: </td>
|
||||
<td class="left">
|
||||
<xsl:value-of select="format-number(@duration,'0.000')"/> seconds
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="test-case">
|
||||
<!-- Determine type of test-case for formatting -->
|
||||
<xsl:variable name="type">
|
||||
<xsl:choose>
|
||||
<xsl:when test="@result='Skipped'">
|
||||
<xsl:choose>
|
||||
<xsl:when test="@label='Ignored' or @label='Explicit'">
|
||||
<xsl:value-of select="@label"/>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="'Other'"/>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:when>
|
||||
<xsl:when test="@result='Failed'">
|
||||
<xsl:choose>
|
||||
<xsl:when test="@label='Error' or @label='Invalid'">
|
||||
<xsl:value-of select="@label"/>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="'Failed'"/>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:when>
|
||||
<xsl:when test="@result='Passed'">
|
||||
<xsl:value-of select="'Success'"/>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="'Unknown'"/>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:variable>
|
||||
|
||||
<!-- Show details of test-cases either skipped or errored -->
|
||||
<li>
|
||||
<xsl:value-of select="concat($type,' : ', @fullname)" />
|
||||
<br/>
|
||||
<pre>
|
||||
<xsl:value-of select="child::node()/message"/>
|
||||
|
||||
<xsl:choose>
|
||||
<xsl:when test="$type='Failed'">
|
||||
<br/>
|
||||
</xsl:when>
|
||||
<xsl:when test="$type='Error'">
|
||||
<br/>
|
||||
</xsl:when>
|
||||
<xsl:when test="$type='Passed'">
|
||||
<br/>
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
|
||||
<!-- Stack trace for failures -->
|
||||
<xsl:if test="failure and ($type='Failed' or $type='Error')">
|
||||
<xsl:value-of select="failure/stack-trace"/>
|
||||
</xsl:if>
|
||||
</pre>
|
||||
|
||||
<xsl:if test="child::node()/attachment">
|
||||
<ul>
|
||||
<xsl:for-each select="child::node()/attachment">
|
||||
<li> <a href="file://{filePath}"><xsl:value-of select="description"/></a></li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</xsl:if>
|
||||
</li>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,29 @@
|
|||
TOP=../..
|
||||
|
||||
include $(TOP)/Make.config
|
||||
|
||||
NUNIT_MSBUILD_DIR=$(TOP)/packages/NUnit.ConsoleRunner.3.9.0/tools/
|
||||
|
||||
all-local:: run-tests
|
||||
|
||||
ifneq ($(TEST_CATEGORY),)
|
||||
WHERE_CONDITION=/where "cat == $(TEST_CATEGORY)"
|
||||
endif
|
||||
|
||||
TEST_RESULT=TestResult-$(shell date "+%Y%m%d-%H%M%S")
|
||||
|
||||
build: bin/Debug/sampletester.dll
|
||||
|
||||
run-tests: bin/Debug/sampletester.dll
|
||||
$(Q) rm -f .failed-stamp
|
||||
$(Q) $(SYSTEM_MONO) --debug $(NUNIT_MSBUILD_DIR)/nunit3-console.exe $(WHERE_CONDITION) $(abspath $(CURDIR)/bin/Debug/sampletester.dll) "--result=$(abspath $(CURDIR)/$(TEST_RESULT).xml)" $${TEST_FIXTURE:+"$$TEST_FIXTURE"} --labels=All --inprocess || touch $(CURDIR)/.failed-stamp
|
||||
$(Q) xsltproc HtmlReport.xslt "$(TEST_RESULT).xml" > "$(TEST_RESULT).html"
|
||||
$(Q) $(CP) "$(TEST_RESULT).html" index.html
|
||||
@[[ ! -e .failed-stamp ]]
|
||||
|
||||
run-tests-system: bin/Debug/sampletester.dll
|
||||
$(MAKE) TESTS_USE_SYSTEM=1
|
||||
|
||||
bin/Debug/sampletester.dll: $(wildcard *.cs */*.cs *.csproj Makefile)
|
||||
$(Q) $(SYSTEM_MONO) /Library/Frameworks//Mono.framework/Versions/Current/lib/mono/nuget/NuGet.exe restore
|
||||
$(Q_BUILD) $(SYSTEM_MSBUILD) sampletester.csproj $(MSBUILD_VERBOSITY)
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Xamarin;
|
||||
using Xamarin.Tests;
|
||||
using Xamarin.Utils;
|
||||
|
||||
public static class ProcessHelper
|
||||
{
|
||||
static int counter;
|
||||
|
||||
static string log_directory;
|
||||
static string LogDirectory {
|
||||
get {
|
||||
if (log_directory == null)
|
||||
log_directory = Cache.CreateTemporaryDirectory ("execution-logs");
|
||||
return log_directory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void AssertRunProcess (string filename, string[] arguments, TimeSpan timeout, string workingDirectory, Dictionary<string, string> environment_variables, string message)
|
||||
{
|
||||
var exitCode = 0;
|
||||
var output = new List<string> ();
|
||||
|
||||
Action<string> output_callback = (v) => {
|
||||
lock (output)
|
||||
output.Add (v);
|
||||
};
|
||||
|
||||
if (environment_variables == null)
|
||||
environment_variables = new Dictionary<string, string> ();
|
||||
environment_variables ["XCODE_DEVELOPER_DIR_PATH"] = null;
|
||||
environment_variables ["DEVELOPER_DIR"] = Configuration.XcodeLocation;
|
||||
|
||||
exitCode = ExecutionHelper.Execute (filename, arguments, out var timed_out, workingDirectory, environment_variables, output_callback, output_callback, timeout);
|
||||
|
||||
// Write execution log to disk (and print the path)
|
||||
var logfile = Path.Combine (LogDirectory, $"{filename}-{Interlocked.Increment (ref counter)}.log");
|
||||
File.WriteAllLines (logfile, output);
|
||||
TestContext.AddTestAttachment (logfile, $"Execution log for {filename}");
|
||||
Console.WriteLine ("Execution log for {0}: {1}", filename, logfile);
|
||||
|
||||
var errors = new List<string> ();
|
||||
var errorMessage = "";
|
||||
if ((!timed_out || exitCode != 0) && output.Count > 0) {
|
||||
var regex = new Regex (@"error\s*(MSB....)?(CS....)?(MT....)?(MM....)?:", RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
foreach (var line in output) {
|
||||
if (regex.IsMatch (line) && !errors.Contains (line))
|
||||
errors.Add (line);
|
||||
}
|
||||
if (errors.Count > 0)
|
||||
errorMessage = "\n\t[Summary of errors from the build output below]\n\t" + string.Join ("\n\t", errors);
|
||||
}
|
||||
Assert.IsFalse (timed_out, $"{message} timed out after {timeout.TotalMinutes} minutes{errorMessage}");
|
||||
Assert.AreEqual (0, exitCode, $"{message} failed (unexpected exit code){errorMessage}");
|
||||
}
|
||||
|
||||
public static void BuildSolution (string solution, string platform, string configuration, Dictionary<string, string> environment_variables, string target = "")
|
||||
{
|
||||
// nuget restore
|
||||
var solution_dir = string.Empty;
|
||||
var solutions = new string [] { solution };
|
||||
var nuget_args = new List<string> ();
|
||||
nuget_args.Add ("restore");
|
||||
nuget_args.Add ("sln"); // replaced later
|
||||
nuget_args.Add ("-Verbosity");
|
||||
nuget_args.Add ("detailed");
|
||||
if (!solution.EndsWith (".sln", StringComparison.Ordinal)) {
|
||||
var slndir = Path.GetDirectoryName (solution);
|
||||
while ((solutions = Directory.GetFiles (slndir, "*.sln", SearchOption.TopDirectoryOnly)).Length == 0 && slndir.Length > 1)
|
||||
slndir = Path.GetDirectoryName (slndir);
|
||||
nuget_args.Add ("-SolutionDir");
|
||||
nuget_args.Add (slndir);
|
||||
}
|
||||
|
||||
foreach (var sln in solutions) {
|
||||
nuget_args [1] = sln; // replacing here
|
||||
AssertRunProcess ("nuget", nuget_args.ToArray (), TimeSpan.FromMinutes (2), Configuration.SampleRootDirectory, environment_variables, "nuget restore");
|
||||
}
|
||||
|
||||
// msbuild
|
||||
var sb = new List<string> ();
|
||||
sb.Add ("/verbosity:diag");
|
||||
if (!string.IsNullOrEmpty (platform))
|
||||
sb.Add ($"/p:Platform={platform}");
|
||||
if (!string.IsNullOrEmpty (configuration))
|
||||
sb.Add ($"/p:Configuration={configuration}");
|
||||
sb.Add (solution);
|
||||
if (!string.IsNullOrEmpty (target))
|
||||
sb.Add ($"/t:{target}");
|
||||
AssertRunProcess ("msbuild", sb.ToArray (), TimeSpan.FromMinutes (5), Configuration.SampleRootDirectory, environment_variables, "build");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
using NUnit.Framework;
|
||||
|
||||
// Two tests at the same time is enough: mtouch already parallelizes things, so we don't want to overload the bots either.
|
||||
[assembly: LevelOfParallelism (2)]
|
|
@ -0,0 +1,32 @@
|
|||
# Sample testing
|
||||
|
||||
These unit tests clone a series of known repositories that contain sample
|
||||
projects, and build all the relevant projects in those repositories.
|
||||
|
||||
It is executed automatically in [Azure DevOps][1] every [Saturday][2] for the
|
||||
following branches:
|
||||
|
||||
* master
|
||||
* d16-*
|
||||
* xcode*
|
||||
|
||||
It can also be triggered manually, but have in mind that the commit in
|
||||
question must already have packages (as GitHub statuses).
|
||||
|
||||
It's also possible to use the sample tests from one commit, and then test with
|
||||
Xamarin.iOS/Xamarin.Mac a completely different commit, by setting the
|
||||
`PROVISION_FROM_COMMIT` variable to the commit that's to be tested:
|
||||
|
||||
![screenshot](images/provision_from_commit.png)
|
||||
|
||||
The previous point is still required: the commit to provision from must have
|
||||
packages.
|
||||
|
||||
There are two ways to run these tests locally:
|
||||
|
||||
* Launching xharness in server mode and execute the "Sample tests" (they're
|
||||
disabled by default).
|
||||
* Executing `make` in this directory.
|
||||
|
||||
[1]: https://dev.azure.com/xamarin/internal/_build?definitionId=23
|
||||
[1]: https://dev.azure.com/xamarin/internal/_apps/hub/ms.vss-ciworkflow.build-ci-hub?_a=edit-build-definition&id=23&view=Tab_Triggers
|
|
@ -0,0 +1,250 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Xamarin.Tests;
|
||||
|
||||
namespace Samples {
|
||||
public class SampleTest {
|
||||
public ProjectInfo Project;
|
||||
public string Solution;
|
||||
public bool BuildSolution;
|
||||
public string KnownFailure;
|
||||
public string[] DebugConfigurations;
|
||||
public string[] ReleaseConfigurations;
|
||||
public string[] Platforms;
|
||||
}
|
||||
|
||||
public class SampleTestData {
|
||||
public SampleTest SampleTest;
|
||||
public string Configuration;
|
||||
public string Platform;
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
if (string.IsNullOrEmpty (Platform))
|
||||
return $"{SampleTest.Project.Title}: {Configuration}";
|
||||
return $"{SampleTest.Project.Title}: {Configuration}|{Platform}";
|
||||
}
|
||||
}
|
||||
|
||||
public class ProjectInfo {
|
||||
public string Title;
|
||||
public string RelativePath;
|
||||
public string FullPath;
|
||||
public bool IsExecutable;
|
||||
public string [] Imports;
|
||||
public TestPlatform Platform;
|
||||
|
||||
public bool IsApplicable (bool assert)
|
||||
{
|
||||
if (!IsExecutable) {
|
||||
if (assert)
|
||||
Assert.Ignore ("Project is not an executable project");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Platform == TestPlatform.None) {
|
||||
if (assert)
|
||||
Assert.Ignore ("Project is not an Xamarin.iOS/Xamarin.Mac/Xamarin.WatchOS/Xamarin.TVOS project. Imports:\n\t{0}", string.Join ("\t\n", Imports));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Platform == TestPlatform.watchOS) {
|
||||
if (assert)
|
||||
Assert.Ignore ("Project is a watchOS app"); // no need to build watchOS apps, they're built as part of their containing iOS project.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class SampleTester : BaseTester {
|
||||
protected SampleTester ()
|
||||
{
|
||||
}
|
||||
|
||||
protected SampleTester (string repo, string hash)
|
||||
: base (repo, hash)
|
||||
{
|
||||
}
|
||||
|
||||
static ProjectInfo GetProjectInfo (string relative_path, string full_path)
|
||||
{
|
||||
var xml = File.ReadAllText (full_path);
|
||||
var info = new ProjectInfo ();
|
||||
info.FullPath = full_path;
|
||||
info.RelativePath = relative_path;
|
||||
info.IsExecutable = xml.Contains ("<OutputType>Exe</OutputType>");
|
||||
|
||||
var xml_lines = xml.Split ('\n');
|
||||
var xml_imports = xml_lines.
|
||||
Where ((v) => v.Contains ("<Import Project=")).
|
||||
Select ((v) => v.Split ('"') [1]);
|
||||
info.Imports = xml_imports.ToArray ();
|
||||
|
||||
var test_platform = TestPlatform.None;
|
||||
if (xml_imports.Any ((v) => v.Contains ("Xamarin.iOS"))) {
|
||||
test_platform = TestPlatform.iOS;
|
||||
} else if (xml_imports.Any ((v) => v.Contains ("Xamarin.TVOS"))) {
|
||||
test_platform = TestPlatform.tvOS;
|
||||
} else if (xml_imports.Any ((v) => v.Contains ("Xamarin.WatchOS"))) {
|
||||
test_platform = TestPlatform.watchOS;
|
||||
} else if (xml_imports.Any ((v) => v.Contains ("Xamarin.Mac"))) {
|
||||
test_platform = TestPlatform.macOS;
|
||||
} else {
|
||||
test_platform = TestPlatform.None;
|
||||
}
|
||||
info.Platform = test_platform;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildSample ([ValueSource ("GetSampleData")] SampleTestData sampleTestData)
|
||||
{
|
||||
try {
|
||||
var data = sampleTestData.SampleTest;
|
||||
if (!string.IsNullOrEmpty (data.KnownFailure))
|
||||
Assert.Ignore (data.KnownFailure);
|
||||
|
||||
var environment_variables = new Dictionary<string, string> ();
|
||||
environment_variables ["MD_APPLE_SDK_ROOT"] = Path.GetDirectoryName (Path.GetDirectoryName (Configuration.XcodeLocation));
|
||||
switch (data.Project.Platform) {
|
||||
case TestPlatform.iOS:
|
||||
case TestPlatform.tvOS:
|
||||
case TestPlatform.watchOS:
|
||||
environment_variables ["MD_MTOUCH_SDK_ROOT"] = Path.Combine (Configuration.IOS_DESTDIR, "Library", "Frameworks", "Xamarin.iOS.framework", "Versions", "Current");
|
||||
environment_variables ["TargetFrameworkFallbackSearchPaths"] = Path.Combine (Configuration.IOS_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild-frameworks");
|
||||
environment_variables ["MSBuildExtensionsPathFallbackPathsOverride"] = Path.Combine (Configuration.IOS_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild");
|
||||
break;
|
||||
case TestPlatform.macOS:
|
||||
environment_variables ["TargetFrameworkFallbackSearchPaths"] = Path.Combine (Configuration.MAC_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild-frameworks");
|
||||
environment_variables ["MSBuildExtensionsPathFallbackPathsOverride"] = Path.Combine (Configuration.MAC_DESTDIR, "Library", "Frameworks", "Mono.framework", "External", "xbuild");
|
||||
environment_variables ["XamarinMacFrameworkRoot"] = Path.Combine (Configuration.MAC_DESTDIR, "Library", "Frameworks", "Xamarin.Mac.framework", "Versions", "Current");
|
||||
environment_variables ["XAMMAC_FRAMEWORK_PATH"] = Path.Combine (Configuration.MAC_DESTDIR, "Library", "Frameworks", "Xamarin.Mac.framework", "Versions", "Current");
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException (sampleTestData.Platform.ToString ());
|
||||
}
|
||||
|
||||
var file_to_build = sampleTestData.SampleTest.Project.RelativePath;
|
||||
var target = string.Empty;
|
||||
if (data.BuildSolution) {
|
||||
file_to_build = data.Solution;
|
||||
target = Path.GetFileNameWithoutExtension (data.Project.RelativePath).Replace ('.', '_');
|
||||
}
|
||||
|
||||
file_to_build = Path.Combine (CloneRepo (), file_to_build);
|
||||
ProcessHelper.BuildSolution (file_to_build, sampleTestData.Platform, sampleTestData.Configuration, environment_variables, target);
|
||||
Console.WriteLine ("✅ {0} succeeded.", TestContext.CurrentContext.Test.FullName);
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine ("❌ {0} failed: {1}", TestContext.CurrentContext.Test.FullName, e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<string, ProjectInfo []> projects = new Dictionary<string, ProjectInfo []> ();
|
||||
protected static ProjectInfo [] GetExecutableProjects (string org, string repo, string hash)
|
||||
{
|
||||
if (!projects.TryGetValue (repo, out var rv)) {
|
||||
var project_paths = GitHub.GetProjects (org, repo, hash);
|
||||
|
||||
// We can filter out project we don't care about.
|
||||
rv = project_paths.
|
||||
Select ((v) => GetProjectInfo (v, Path.Combine (GitHub.CloneRepository (org, repo, hash, false), v))).
|
||||
Where ((v) => v.IsApplicable (false)).
|
||||
ToArray ();
|
||||
|
||||
projects [repo] = rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected static IEnumerable<SampleTestData> GetSampleTestData (Dictionary<string, SampleTest> samples, string org, string repo, string hash)
|
||||
{
|
||||
var defaultDebugConfigurations = new string [] { "Debug" };
|
||||
var defaultReleaseConfigurations = new string [] { "Release" };
|
||||
|
||||
if (samples == null) {
|
||||
samples = new Dictionary<string, SampleTest> ();
|
||||
} else {
|
||||
samples = new Dictionary<string, SampleTest> (samples);
|
||||
}
|
||||
|
||||
// If a project's filename is unique in this repo, use the filename (without extension) as the name of the test.
|
||||
// Otherwise use the project's relative path.
|
||||
var executable_projects = GetExecutableProjects (org, repo, hash);
|
||||
var duplicateProjects = executable_projects.GroupBy ((v) => Path.GetFileNameWithoutExtension (v.RelativePath)).Where ((v) => v.Count () > 1);
|
||||
foreach (var group in duplicateProjects) {
|
||||
foreach (var proj in group) {
|
||||
proj.Title = proj.RelativePath;
|
||||
}
|
||||
}
|
||||
foreach (var proj in executable_projects) {
|
||||
if (proj.Title == null) {
|
||||
proj.Title = Path.GetFileNameWithoutExtension (proj.RelativePath);
|
||||
}
|
||||
}
|
||||
|
||||
var platform_filter = Environment.GetEnvironmentVariable ("TEST_PLATFORM_FILTER_EXPRESSION");
|
||||
var config_filter = Environment.GetEnvironmentVariable ("TEST_CONFIG_FILTER_EXPRESSION");
|
||||
var name_filter = Environment.GetEnvironmentVariable ("TEST_NAME_FILTER_EXPRESSION");
|
||||
|
||||
IEnumerable<T> filter<T> (string name, string proj, IEnumerable<T> input, string filter_expression, Func<T, string> tostring)
|
||||
{
|
||||
if (string.IsNullOrEmpty (filter_expression))
|
||||
return input;
|
||||
|
||||
var filtered = input.Where ((v) => Regex.IsMatch (tostring (v), filter_expression));
|
||||
var removed = input.Where ((v) => !filtered.Contains (v));
|
||||
if (removed.Any ()) {
|
||||
return filtered;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
// Create the test variations for each project.
|
||||
foreach (var proj in filter ("name", "*", executable_projects, name_filter, (v) => Path.GetFileName (v.RelativePath))) {
|
||||
if (!samples.TryGetValue (proj.RelativePath, out var sample))
|
||||
samples [proj.RelativePath] = sample = new SampleTest ();
|
||||
sample.Project = proj;
|
||||
IEnumerable<string> platforms = sample.Platforms;
|
||||
if (platforms == null) {
|
||||
switch (proj.Platform) {
|
||||
case TestPlatform.iOS:
|
||||
case TestPlatform.tvOS:
|
||||
platforms = new string [] { "iPhone", "iPhoneSimulator" };
|
||||
break;
|
||||
case TestPlatform.macOS:
|
||||
platforms = new string [] { "" };
|
||||
break;
|
||||
case TestPlatform.watchOS:
|
||||
default:
|
||||
throw new NotImplementedException (proj.Platform.ToString ());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var platform in filter ("platform", proj.Title, platforms, platform_filter, (v) => v)) {
|
||||
var configs = new List<string> ();
|
||||
configs.AddRange (sample.DebugConfigurations ?? defaultDebugConfigurations);
|
||||
configs.AddRange (sample.ReleaseConfigurations ?? defaultReleaseConfigurations);
|
||||
foreach (var config in filter ("config", proj.Title, configs, config_filter, (v) => v)) {
|
||||
yield return new SampleTestData { SampleTest = sample, Configuration = config, Platform = platform };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string CloneRepo ()
|
||||
{
|
||||
return GitHub.CloneRepository (Org, Repository, Hash);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Samples {
|
||||
[Category (CATEGORY)]
|
||||
public class IosSampleTester : SampleTester {
|
||||
const string ORG = "xamarin";
|
||||
const string REPO = "ios-samples"; // monotouch-samples redirects to ios-samples
|
||||
const string CATEGORY = "iossamples"; // categories can't contain dashes
|
||||
const string HASH = "1d0f3270c394e9c15c014813e804972b17ce3e48";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
// Build solution instead of csproj
|
||||
{ "BindingSample/XMBindingLibrarySample/XMBindingLibrarySample.csproj", new SampleTest { BuildSolution = true, Solution = "BindingSample/BindingSample.sln" } },
|
||||
{ "BouncingGameCompleteiOS/BouncingGame.iOS/BouncingGame.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "BouncingGameCompleteiOS/BouncingGame.sln" } },
|
||||
{ "BouncingGameEmptyiOS/BouncingGame.iOS/BouncingGame.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "BouncingGameEmptyiOS/BouncingGame.sln" } },
|
||||
{ "FileSystemSampleCode/FileSystem/FileSystem.csproj", new SampleTest { BuildSolution = true, Solution = "FileSystemSampleCode/WorkingWithTheFileSystem.sln" } },
|
||||
{ "InfColorPicker/InfColorPickerBinding/InfColorPickerSample/InfColorPickerSample.csproj", new SampleTest { BuildSolution = true, Solution = "InfColorPicker/InfColorPickerBinding/InfColorPickerBinding.sln" } },
|
||||
{ "ios10/ElizaChat/ElizaChat/ElizaChat.csproj", new SampleTest { BuildSolution = true, Solution = "ios10/ElizaChat/ElizaChat.sln" } },
|
||||
{ "ios10/IceCreamBuilder/IceCreamBuilder/IceCreamBuilder.csproj", new SampleTest { BuildSolution = true, Solution = "ios10/IceCreamBuilder/IceCreamBuilder.sln" } },
|
||||
{ "ios11/ARKitPlacingObjects/PlacingObjects/PlacingObjects.csproj", new SampleTest { BuildSolution = true, Solution = "ios11/ARKitPlacingObjects/PlacingObjects.sln" } },
|
||||
{ "ios11/WeatherWidget/WeatherWidget/WeatherWidget.csproj", new SampleTest { BuildSolution = true, Solution = "ios11/WeatherWidget/WeatherWidget.sln" } },
|
||||
{ "ios12/SoupChef/SoupChef/SoupChef.csproj", new SampleTest { BuildSolution = true, Solution = "ios12/SoupChef/SoupChef.sln" } },
|
||||
{ "ios12/XamarinShot/XamarinShot/XamarinShot.csproj", new SampleTest { BuildSolution = true, Solution = "ios12/XamarinShot/XamarinShot.sln", Platforms = new string [] { "iPhone" } } }, // Requires Metal, which doesn't work/build in the simulator.
|
||||
{ "ios8/Lister/Lister/Lister.csproj", new SampleTest { BuildSolution = true, Solution = "ios8/Lister/Lister.sln" } },
|
||||
{ "ios8/MetalBasic3D/MetalBasic3D/MetalBasic3D.csproj", new SampleTest { Platforms = new string [] { "iPhone" } } }, // Requires Metal, which doesn't work/build in the simulator.
|
||||
{ "ios8/MetalImageProcessing/MetalImageProcessing/MetalImageProcessing.csproj", new SampleTest { Platforms = new string [] { "iPhone" } } }, // Requires Metal, which doesn't work/build in the simulator.
|
||||
{ "ios8/MetalTexturedQuad/MetalTexturedQuad/MetalTexturedQuad.csproj", new SampleTest { Platforms = new string [] { "iPhone" } } }, // Requires Metal, which doesn't work/build in the simulator.
|
||||
{ "ios9/iTravel/iTravel/iTravel.csproj", new SampleTest { BuildSolution = true, Solution = "ios9/iTravel/iTravel.sln" } },
|
||||
{ "Profiling/MemoryDemo/MemoryDemo/MemoryDemo.csproj", new SampleTest { BuildSolution = true, Solution = "Profiling/MemoryDemo/MemoryDemo.sln", DebugConfigurations = new string [] { "Before-Debug", "After-Debug" }, ReleaseConfigurations = new string [] { "Before-Release", "After-Release" } } },
|
||||
{ "WalkingGameCompleteiOS/WalkingGame.iOS/WalkingGame.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "WalkingGameCompleteiOS/WalkingGame.sln" } },
|
||||
{ "WalkingGameEmptyiOS/WalkingGame.iOS/WalkingGame.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "WalkingGameEmptyiOS/WalkingGame.sln" } },
|
||||
{ "watchOS/WatchConnectivity/WatchConnectivity/WatchConnectivity.csproj", new SampleTest { BuildSolution = true, Solution = "watchOS/WatchConnectivity/WatchConnectivity.sln" } },
|
||||
{ "watchOS/WatchKitCatalog/WatchKitCatalog/WatchKitCatalog.csproj", new SampleTest { BuildSolution = true, Solution = "watchOS/WatchKitCatalog/WatchKitCatalog.sln" } },
|
||||
|
||||
// known failures
|
||||
{ "ios9/Emporium/Emporium/Emporium.csproj", new SampleTest { BuildSolution = true, Solution = "ios9/Emporium/Emporium.sln", KnownFailure = "error : Xcode 10 does not support watchOS 1 apps. Either upgrade to watchOS 2 apps, or use an older version of Xcode." } },
|
||||
{ "WatchKit/GpsWatch/GpsWatch/MainApp.csproj", new SampleTest { KnownFailure = "error : Xcode 10 does not support watchOS 1 apps. Either upgrade to watchOS 2 apps, or use an older version of Xcode." } },
|
||||
{ "WatchKit/WatchNotifications/WatchNotifications_iOS/WatchNotifications_iOS.csproj", new SampleTest { KnownFailure = "error : Xcode 10 does not support watchOS 1 apps. Either upgrade to watchOS 2 apps, or use an older version of Xcode." } },
|
||||
{ "PassKit/PassLibrary/PassLibrary.csproj", new SampleTest { BuildSolution = true, Solution = "PassKit/PassLibrary/PassLibrary.sln", KnownFailure = "Requires custom provisioning to get a proper pass." } },
|
||||
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
[Category (CATEGORY)]
|
||||
public class MacIosSampleTester : SampleTester {
|
||||
const string ORG = "xamarin";
|
||||
const string REPO = "mac-ios-samples";
|
||||
const string CATEGORY = "maciossamples"; // categories can't contain dashes
|
||||
const string HASH = "2ab4faf9254cecdf5766af573e508f9ac8691663";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
// Build solution instead of csproj
|
||||
{ "ExceptionMarshaling/ExceptionMarshaling.Mac.csproj", new SampleTest { BuildSolution = true, Solution = "ExceptionMarshaling/ExceptionMarshaling.sln" } },
|
||||
{ "Fox2/Fox2.macOS/Fox2.macOS.csproj", new SampleTest { BuildSolution = true, Solution = "Fox2/Fox2.sln" } },
|
||||
{ "MetalKitEssentials/MetalKitEssentials.iOS/MetalKitEssentials.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "MetalKitEssentials/MetalKitEssentials.sln", Platforms = new string [] { "iPhone" } } }, // Requires Metal, which doesn't work/build in the simulator.
|
||||
{ "SceneKitReel/SceneKitReelMac/SceneKitReelMac.csproj", new SampleTest { BuildSolution = true, Solution = "SceneKitReel/SceneKitReel.sln" } },
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
[Category (CATEGORY)]
|
||||
public class MacSampleTester : SampleTester {
|
||||
const string ORG = "xamarin";
|
||||
const string REPO = "mac-samples";
|
||||
const string CATEGORY = "macsamples"; // categories can't contain dashes
|
||||
const string HASH = "6f905972c98e64759ff84a25e4e2b42366fa197b";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
[Category (CATEGORY)]
|
||||
public class MobileSampleTester : SampleTester {
|
||||
const string ORG = "xamarin";
|
||||
const string REPO = "mobile-samples";
|
||||
const string CATEGORY = "mobilesamples"; // categories can't contain dashes
|
||||
const string HASH = "45182f311db77bb05971a22d58edfdef44a4b657";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
// Build solution instead of csproj
|
||||
{ "BouncingGame/BouncingGame/BouncingGame.iOS/BouncingGame.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "BouncingGame/BouncingGame.sln" } },
|
||||
{ "CCAction/ActionProject/ActionProject.iOS/ActionProject.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "CCAction/ActionProject.sln" } },
|
||||
{ "CCRenderTexture/RenderTextureExample/RenderTextureExample.iOS/RenderTextureExample.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "CCRenderTexture/RenderTextureExample.sln" } },
|
||||
{ "EmbeddedResources/EmbeddedResources/EmbeddedResources.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "EmbeddedResources/EmbeddedResources.sln" } },
|
||||
{ "FruityFalls/FruityFalls/FruityFalls.iOS/FruityFalls.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "FruityFalls/FruityFalls.sln" } },
|
||||
{ "LivePlayer/BasicCalculator/Calculator.iOS/Calculator.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "LivePlayer/BasicCalculator/Calculator.sln" } },
|
||||
{ "MonoGameTvOs/MonoGameTvOs/MonoGameTvOs.csproj", new SampleTest { BuildSolution = true, Solution = "MonoGameTvOs/MonoGameTvOs.sln" } },
|
||||
{ "SpriteSheetDemo/iOS/SpriteSheetDemo.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "SpriteSheetDemo/SpriteSheetDemo.sln" } },
|
||||
{ "TaskyPortable/TaskyiOS/TaskyiOS.csproj", new SampleTest { BuildSolution = true, Solution = "TaskyPortable/TaskyPortable.sln" } },
|
||||
{ "TipCalc/TipCalc-UI-iOS/TipCalc-UI-iOS.csproj", new SampleTest { BuildSolution = true, Solution = "TipCalc/TipCalc.sln" } },
|
||||
|
||||
// Known failures
|
||||
{ "RazorTodo/RazorNativeTodo/RazorNativeTodo.iOS/RazorNativeTodo.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "RazorTodo/RazorNativeTodo/RazorNativeTodo.sln", KnownFailure = "There's a Xamarin.Android project in the solution, and I can't figure out how to build only the Xamarin.iOS project." } },
|
||||
{ "RazorTodo/RazorTodo/RazorTodo.iOS/RazorTodo.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "RazorTodo/RazorTodo/RazorTodo.sln", KnownFailure = "There's a Xamarin.Android project in the solution, and I can't figure out how to build only the Xamarin.iOS project." } },
|
||||
{ "VisualBasic/TaskyPortableVB/TaskyiOS/TaskyiOS.csproj", new SampleTest { KnownFailure = "VisualBasic not supported on macOS: error MSB4057: The target \"Build\" does not exist in the project." } },
|
||||
{ "VisualBasic/XamarinFormsVB/XamarinForms.iOS/XamarinForms.iOS.csproj", new SampleTest { KnownFailure = "VisualBasic not supported on macOS." } },
|
||||
{ "WebServices/WebServiceSamples/WebServices.RxNorm/src/WebServices.RxNormSample/WebServices.RxNormSample.csproj", new SampleTest { KnownFailure = "Xamarin.iOS Classic isn't supported anymore." } },
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
[Category (CATEGORY)]
|
||||
public class PrebuiltAppTester : SampleTester {
|
||||
const string ORG = "xamarin";
|
||||
const string REPO = "prebuilt-apps";
|
||||
const string CATEGORY = "prebuiltapps"; // categories can't contain dashes
|
||||
const string HASH = "6f237ada6b687498f0aac45c1b55983201e39410";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
// Known failures
|
||||
{ "FieldService/FieldService.iOS/FieldService.iOS.csproj", new SampleTest { KnownFailure = "The sample uses Xamarin Components which don't work anymore." } },
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
[Category (CATEGORY)]
|
||||
public class XamarinFormsTester : SampleTester {
|
||||
const string ORG = "xamarin";
|
||||
const string REPO = "xamarin-forms-samples";
|
||||
const string CATEGORY = "xamarinformssamples"; // categories can't contain dashes
|
||||
const string HASH = "206f4c3a2be1e988eda2ad9130a37019c60f1c7e";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
// Build solution instead of csproj.
|
||||
{ "WebServices/TodoWCF/iOS/TodoWCF.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "WebServices/TodoWCF/TodoWCF.sln" } },
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
[Category (CATEGORY)]
|
||||
public class XamarinFormsBooksTester : SampleTester {
|
||||
const string ORG = "xamarin";
|
||||
const string REPO = "xamarin-forms-book-samples";
|
||||
const string CATEGORY = "xamarinformsbookssamples"; // categories can't contain dashes
|
||||
const string HASH = "79f51213ed742878333e072fdc74c2ee894c0130";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
// Build solution instead of csproj,
|
||||
{ "Chapter20/TextFileAsync/TextFileAsync/TextFileAsync.iOS/TextFileAsync.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "Chapter20/TextFileAsync/TextFileAsync.sln" } },
|
||||
{ "Chapter24/NoteTaker/NoteTaker/NoteTaker.iOS/NoteTaker.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "Chapter24/NoteTaker/NoteTaker.sln" } },
|
||||
{ "Chapter27/BouncingBall/BouncingBall/BouncingBall.iOS/BouncingBall.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "Chapter27/BouncingBall/BouncingBall.sln" } },
|
||||
{ "Chapter27/EllipseDemo/EllipseDemo/EllipseDemo.iOS/EllipseDemo.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "Chapter27/EllipseDemo/EllipseDemo.sln" } },
|
||||
{ "Chapter27/StepSliderDemo/StepSliderDemo/StepSliderDemo.iOS/StepSliderDemo.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "Chapter27/StepSliderDemo/StepSliderDemo.sln" } },
|
||||
{ "Chapter28/MapDemos/MapDemos/MapDemos.iOS/MapDemos.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "Chapter28/MapDemos/MapDemos.sln" } },
|
||||
{ "Chapter28/WhereAmI/WhereAmI/WhereAmI.iOS/WhereAmI.iOS.csproj", new SampleTest { BuildSolution = true, Solution = "Chapter28/WhereAmI/WhereAmI.sln" } },
|
||||
|
||||
// Known failures
|
||||
{ "Chapter02/FS/Greetings/Greetings.iOS/Greetings.iOS.fsproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter02/FS/Hello/Hello.iOS/Hello.iOS.fsproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter03/FS/Baskervilles/Baskervilles/Baskervilles.iOS/Baskervilles.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter03/FS/NamedFontSizes/NamedFontSizes/NamedFontSizes.iOS/NamedFontSizes.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter03/FS/VariableFormPara/VariableFormattedParagraph/VariableFormattedParagraph.iOS/VariableFormattedParagraph.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter03/FS/VariableFormText/VariableFormattedText/VariableFormattedText.iOS/VariableFormattedText.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/BlackCat/BlackCat/BlackCat.iOS/BlackCat.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/ColorBlocks/ColorBlocks/ColorBlocks.iOS/ColorBlocks.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/ColorList/ColorList/ColorList.iOS/ColorList.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/ColorLoop/ColorLoop/ColorLoop.iOS/ColorLoop.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/FramedText/FramedText/FramedText.iOS/FramedText.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/ReflectedColors/ReflectedColors/ReflectedColors.iOS/ReflectedColors.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/SizedBoxView/SizedBoxView/SizedBoxView.iOS/SizedBoxView.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter04/FS/VerticalOptionsDemo/VerticalOptionsDemo/VerticalOptionsDemo.iOS/VerticalOptionsDemo.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter05/FS/EmpiricalFontSize/EmpiricalFontSize/EmpiricalFontSize.iOS/EmpiricalFontSize.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter05/FS/EstimatedFontSize/EstimatedFontSize/EstimatedFontSize.iOS/EstimatedFontSize.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter05/FS/FitToSizeClock/FitToSizeClock/FitToSizeClock.iOS/FitToSizeClock.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter05/FS/FontSizes/FontSizes/FontSizes.iOS/FontSizes.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter05/FS/MetricalBoxView/MetricalBoxView/MetricalBoxView.iOS/MetricalBoxView.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter05/FS/WhatSize/WhatSize/WhatSize.iOS/WhatSize.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter06/FS/ButtonLambdas/ButtonLambdas/ButtonLambdas.iOS/ButtonLambdas.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter06/FS/ButtonLogger/ButtonLogger/ButtonLogger.iOS/ButtonLogger.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter06/FS/PersistentKeypad/PersistentKeypad/PersistentKeypad.iOS/PersistentKeypad.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter06/FS/SimplestKeypad/SimplestKeypad/SimplestKeypad.iOS/SimplestKeypad.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter06/FS/TwoButtons/TwoButtons/TwoButtons.iOS/TwoButtons.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter07/FS/CodePlusXaml/CodePlusXaml/CodePlusXaml.iOS/CodePlusXaml.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
{ "Chapter08/FS/XamlKeypad/XamlKeypad/XamlKeypad.iOS/XamlKeypad.iOS.csproj", new SampleTest { KnownFailure = "https://github.com/xamarin/xamarin-forms-book-samples/pull/55 and https://github.com/xamarin/xamarin-forms-book-samples/pull/56" } },
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
[Category (CATEGORY)]
|
||||
public class EmbeddedFrameworksTester : SampleTester {
|
||||
const string ORG = "rolfbjarne";
|
||||
const string REPO = "embedded-frameworks";
|
||||
const string CATEGORY = "embeddedframeworks"; // categories can't contain dashes
|
||||
const string HASH = "faaad6f9dcda53b2c49cec567eca769cb534307f";
|
||||
|
||||
static Dictionary<string, SampleTest> test_data = new Dictionary<string, SampleTest> {
|
||||
// Known failures
|
||||
{ "simpleapp-with-framework/simpleapp-with-framework/simpleapp-with-framework.csproj", new SampleTest { BuildSolution = true, Solution = "simpleapp-with-framework/simpleapp-with-framework.sln" } },
|
||||
};
|
||||
|
||||
static IEnumerable<SampleTestData> GetSampleData ()
|
||||
{
|
||||
return GetSampleTestData (test_data, ORG, REPO, HASH);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
public enum TestPlatform {
|
||||
None,
|
||||
iOS,
|
||||
macOS,
|
||||
watchOS,
|
||||
tvOS,
|
||||
}
|
||||
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 98 KiB |
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit" version="3.11.0" targetFramework="net45" />
|
||||
<package id="NUnit.ConsoleRunner" version="3.9.0" targetFramework="net45" />
|
||||
<package id="NUnit.Extension.NUnitV2ResultWriter" version="3.6.0" targetFramework="net45" />
|
||||
</packages>
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\packages\NUnit.3.11.0\build\NUnit.props" Condition="Exists('..\..\packages\NUnit.3.11.0\build\NUnit.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>sampletester</RootNamespace>
|
||||
<AssemblyName>sampletester</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="nunit.framework">
|
||||
<HintPath>..\..\packages\NUnit.3.11.0\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="SampleTester.cs" />
|
||||
<Compile Include="GitHub.cs" />
|
||||
<Compile Include="Configuration.cs" />
|
||||
<Compile Include="ProcessHelper.cs" />
|
||||
<Compile Include="BaseTester.cs" />
|
||||
<Compile Include="TestPlatform.cs" />
|
||||
<Compile Include="..\mtouch\Cache.cs">
|
||||
<Link>external\Cache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\StringUtils.cs">
|
||||
<Link>external\StringUtils.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\common\Configuration.cs">
|
||||
<Link>external\Configuration.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\common\Profile.cs">
|
||||
<Link>external\Profile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\common\ExecutionHelper.cs">
|
||||
<Link>external\ExecutionHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Samples.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="external\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sampletester", "sampletester.csproj", "{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -119,6 +119,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CorlibXunit", "bcl-test\BCL
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CorlibTests", "bcl-test\BCLTests\CorlibTests.csproj", "{3D440BA8-29F8-EB17-6858-209114E79FD3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sampletester", "sampletester\sampletester.csproj", "{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|iPhoneSimulator = Debug|iPhoneSimulator
|
||||
|
@ -1522,5 +1524,31 @@ Global
|
|||
{3D440BA8-29F8-EB17-6858-209114E79FD3}.Release32|iPhone.Build.0 = Release32|iPhone
|
||||
{3D440BA8-29F8-EB17-6858-209114E79FD3}.Release64|iPhone.ActiveCfg = Release64|iPhone
|
||||
{3D440BA8-29F8-EB17-6858-209114E79FD3}.Release64|iPhone.Build.0 = Release64|iPhone
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release-bitcode|iPhone.ActiveCfg = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release-bitcode|iPhone.Build.0 = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release-bitcode|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release-bitcode|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release-bitcode|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release-bitcode|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug32|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug32|iPhone.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug64|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Debug64|iPhone.Build.0 = Debug|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release32|iPhone.ActiveCfg = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release32|iPhone.Build.0 = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release64|iPhone.ActiveCfg = Release|Any CPU
|
||||
{7340A1C6-61A5-42D2-9DBC-6688D2E70F62}.Release64|iPhone.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -963,6 +963,24 @@ namespace xharness
|
|||
};
|
||||
Tasks.Add (runDocsTests);
|
||||
|
||||
var buildSampleTests = new XBuildTask {
|
||||
Jenkins = this,
|
||||
TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "sampletester", "sampletester.sln"))),
|
||||
SpecifyPlatform = false,
|
||||
Platform = TestPlatform.All,
|
||||
ProjectConfiguration = "Debug",
|
||||
};
|
||||
var runSampleTests = new NUnitExecuteTask (buildSampleTests) {
|
||||
TestLibrary = Path.Combine (Harness.RootDirectory, "sampletester", "bin", "Debug", "sampletester.dll"),
|
||||
TestProject = new TestProject (Path.GetFullPath (Path.Combine (Harness.RootDirectory, "sampletester", "sampletester.csproj"))),
|
||||
Platform = TestPlatform.All,
|
||||
TestName = "Sample tests",
|
||||
Timeout = TimeSpan.FromDays (1), // These can take quite a while to execute.
|
||||
InProcess = true,
|
||||
Ignored = true, // Ignored by default, can be run manually. On CI will execute if the label 'run-sample-tests' is present on a PR (but in Azure Devops on a different bot).
|
||||
};
|
||||
Tasks.Add (runSampleTests);
|
||||
|
||||
Tasks.AddRange (CreateRunDeviceTasks ());
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
# This script takes
|
||||
# First argument: the github token to authenticate with
|
||||
# Subsequent arguments: the names of each test variation in the test matrix.
|
||||
# There is a corresponding environment variable with the result from each test variation.
|
||||
|
||||
|
||||
# Print environment for debugging
|
||||
env | sort
|
||||
|
||||
TOKEN=$1
|
||||
shift 1
|
||||
STEPS="$*"
|
||||
|
||||
EMOJII="✅"
|
||||
GH_STATE=success
|
||||
FILE=commit-comment.md
|
||||
|
||||
for STEP in $STEPS; do
|
||||
# The environment variable's name is the variation name in uppercase, and special symbols removed (|-)
|
||||
STEPNAME=JOBRESULT$(echo "$STEP" | tr '[:lower:]' '[:upper:]' | sed -e 's/|//g' -e 's/-//g')
|
||||
STEPSTATUS=${!STEPNAME}
|
||||
if [[ "$STEPSTATUS" == "Succeeded" ]]; then
|
||||
STEPEMOJII="✅"
|
||||
else
|
||||
STEPEMOJII="❌"
|
||||
EMOJII="❌"
|
||||
GH_STATE=failure
|
||||
fi
|
||||
echo "* $STEPEMOJII $STEP: $STEPSTATUS" >> "$FILE"
|
||||
done
|
||||
|
||||
printf "%s\n\n" "$EMOJII Status for '$BUILD_DEFINITIONNAME': [$GH_STATE]($AZURE_BUILD_URL)." | cat - "$FILE" > "$FILE.tmp"
|
||||
mv "$FILE.tmp" "$FILE"
|
||||
|
||||
./jenkins/add-commit-comment.sh "--token=$TOKEN" "--hash=$BUILD_SOURCEVERSION" "--file=$FILE"
|
||||
./jenkins/add-commit-status.sh "--token=$TOKEN" "--hash=$BUILD_SOURCEVERSION" "--state=$GH_STATE" --target-url="$AZURE_BUILD_URL" --description="$BUILD_DEFINITIONNAME" --context="$BUILD_DEFINITIONNAME"
|
||||
rm -f "$FILE"
|
|
@ -0,0 +1,67 @@
|
|||
#load "utils.csx"
|
||||
#load "../../../maccore/tools/devops/external-deps.csx" // this will map the Xcode locations from Azure into our expected locations using symlinks.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Xamarin.Provisioning;
|
||||
using Xamarin.Provisioning.Model;
|
||||
|
||||
// Provision Mono, XI, XM, Mono, Objective-Sharpie, Xcode, provisioning profiles.
|
||||
//
|
||||
// We get Mono from the current commit's MIN_MONO_URL value in Make.config
|
||||
// We get XI and XM from the current commit's manifest from GitHub's statuses
|
||||
//
|
||||
// Overrides:
|
||||
// * Each download URL can be overriden by setting an environment variable (MIN_MONO_URL, XI_PACKAGE and/or XM_PACKAGE).
|
||||
// * The current commit can be overridden by setting the PROVISION_FROM_COMMIT variable. This is usually easier than overriding each url.
|
||||
|
||||
var commit = Environment.GetEnvironmentVariable ("BUILD_SOURCEVERSION");
|
||||
var provision_from_commit = Environment.GetEnvironmentVariable ("PROVISION_FROM_COMMIT") ?? commit;
|
||||
|
||||
string FindVariable (string variable)
|
||||
{
|
||||
var value = FindConfigurationVariable (variable, provision_from_commit);
|
||||
if (!string.IsNullOrEmpty (value))
|
||||
return value;
|
||||
|
||||
switch (variable) {
|
||||
case "XI_PACKAGE":
|
||||
value = GetManifest (provision_from_commit).Where ((v) => v.Contains ("xamarin.ios-") && v.EndsWith (".pkg", StringComparison.Ordinal)).FirstOrDefault ();
|
||||
break;
|
||||
case "XM_PACKAGE":
|
||||
value = GetManifest (provision_from_commit).Where ((v) => v.Contains ("xamarin.mac-") && v.EndsWith (".pkg", StringComparison.Ordinal)).FirstOrDefault ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty (value))
|
||||
return value;
|
||||
|
||||
throw new Exception ($"Could not find {variable} in environment nor in the commit's ({commit}) manifest.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty (provision_from_commit)) {
|
||||
Console.Error.WriteLine ($"Either BUILD_SOURCEVERSION or PROVISION_FROM_COMMIT must be set.");
|
||||
Environment.Exit (1);
|
||||
return 1;
|
||||
}
|
||||
Console.WriteLine ($"Provisioning from {provision_from_commit}...");
|
||||
|
||||
InstallPackage ("Mono", FindVariable ("MIN_MONO_URL"));
|
||||
InstallPackage ("Xamarin.iOS", FindVariable ("XI_PACKAGE"));
|
||||
InstallPackage ("Xamarin.Mac", FindVariable ("XM_PACKAGE"));
|
||||
InstallPackage ("Objective-Sharpie", FindVariable ("MIN_SHARPIE_URL"));
|
||||
|
||||
// Xcode
|
||||
var xcode_path = Path.GetDirectoryName (Path.GetDirectoryName (FindVariable ("XCODE_DEVELOPER_ROOT")));
|
||||
Console.WriteLine ($"ln -Fhs {xcode_path} /Applications/Xcode.app");
|
||||
Exec ("ln", "-Fhs", xcode_path, "/Applications/Xcode.app");
|
||||
|
||||
// Provisioning profiles
|
||||
Exec ($"../../../maccore/tools/install-qa-provisioning-profiles.sh");
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash -eux
|
||||
|
||||
# we want verbose output from mtouch and mlaunch
|
||||
echo 123456789 > ~/.mtouch-verbosity
|
||||
echo 123456789 > ~/.mlaunch-verbosity
|
||||
|
||||
make -C tests test-system.config
|
||||
make -C tests/sampletester TESTS_USE_SYSTEM=1
|
|
@ -0,0 +1,137 @@
|
|||
# Xamarin
|
||||
# Build samples
|
||||
|
||||
variables:
|
||||
azure_build_url: '$(System.CollectionUri)/$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)'
|
||||
macOSVersion: 'Hosted Mac Internal Mojave'
|
||||
debug_filter: '^.*Debug.*$'
|
||||
release_filter: '^.*Release.*$'
|
||||
iphone_filter: '^iPhone$'
|
||||
iphonesimulator_filter: '^iPhoneSimulator$'
|
||||
mac_platform_filter: '^$'
|
||||
name_filter_af: '^[A-Fa-f].*$'
|
||||
name_filter_gr: '^[G-Rg-r].*$'
|
||||
name_filter_rest: '^[^A-Ra-r].*$'
|
||||
|
||||
jobs:
|
||||
- job: ReportStartToGitHub
|
||||
displayName: Set pending GitHub status
|
||||
pool:
|
||||
name: '$(macOSVersion)'
|
||||
steps:
|
||||
- bash: ./jenkins/add-commit-status.sh "--token=$(github-pat)" "--hash=$BUILD_SOURCEVERSION" "--state=pending" --target-url="$AZURE_BUILD_URL" --description="$BUILD_DEFINITIONNAME" --context="$BUILD_DEFINITIONNAME"
|
||||
displayName: Set pending GitHub status
|
||||
|
||||
- job: macOS
|
||||
dependsOn: ReportStartToGitHub
|
||||
displayName: Build samples
|
||||
timeoutInMinutes: 360
|
||||
strategy:
|
||||
matrix:
|
||||
# We have rougly 900 tests, which take a while to build for device.
|
||||
# So in that case, we split them in 3 buckets of roughly 300 tests each,
|
||||
# based on the first letter of the project's filename.
|
||||
Debug|iPhone|A-F:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphone_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(debug_filter)
|
||||
TEST_NAME_FILTER_EXPRESSION: $(name_filter_af)
|
||||
Debug|iPhone|G-R:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphone_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(debug_filter)
|
||||
TEST_NAME_FILTER_EXPRESSION: $(name_filter_gr)
|
||||
Debug|iPhone|S-Z:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphone_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(debug_filter)
|
||||
TEST_NAME_FILTER_EXPRESSION: $(name_filter_rest)
|
||||
Debug|iPhoneSimulator:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphonesimulator_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(debug_filter)
|
||||
Release|iPhone|A-F:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphone_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(release_filter)
|
||||
TEST_NAME_FILTER_EXPRESSION: $(name_filter_af)
|
||||
Release|iPhone|G-R:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphone_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(release_filter)
|
||||
TEST_NAME_FILTER_EXPRESSION: $(name_filter_gr)
|
||||
Release|iPhone|S-Z:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphone_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(release_filter)
|
||||
TEST_NAME_FILTER_EXPRESSION: $(name_filter_rest)
|
||||
Release|iPhoneSimulator:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(iphonesimulator_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(release_filter)
|
||||
Debug|Mac:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(mac_platform_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(debug_filter)
|
||||
Release|Mac:
|
||||
TEST_PLATFORM_FILTER_EXPRESSION: $(mac_platform_filter)
|
||||
TEST_CONFIG_FILTER_EXPRESSION: $(release_filter)
|
||||
|
||||
pool:
|
||||
name: '$(macOSVersion)'
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
persistCredentials: true
|
||||
|
||||
- bash: ./tools/devops/system-info.sh
|
||||
displayName: System Info
|
||||
|
||||
- bash: ./tools/devops/fetch-maccore.sh
|
||||
displayName: Fetch maccore
|
||||
|
||||
- task: xamops.azdevex.provisionator-task.provisionator@1
|
||||
displayName: Provision XI/XM/Mono/Xcode/Objective-Sharpie
|
||||
inputs:
|
||||
provisioning_script: $(System.DefaultWorkingDirectory)/tools/devops/build-samples.csx
|
||||
|
||||
- bash: ./tools/devops/system-info.sh
|
||||
displayName: System Info post provisioning
|
||||
|
||||
- bash: ./tools/devops/build-samples.sh
|
||||
displayName: Run tests
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish test results
|
||||
condition: always()
|
||||
inputs:
|
||||
testResultsFormat: NUnit
|
||||
testResultsFiles: '**/TestResult*.xml'
|
||||
testRunTitle: Sample tests (build)
|
||||
publishRunAttachments: true
|
||||
mergeTestResults: true
|
||||
|
||||
- bash: echo "##vso[task.setvariable variable=JobStatus;isOutput=true]$AGENT_JOBSTATUS"
|
||||
name: ExportedVariables
|
||||
displayName: Export status
|
||||
condition: always()
|
||||
|
||||
- job: ReportResultsToGitHub
|
||||
displayName: Report status/results to GitHub
|
||||
dependsOn: macOS
|
||||
condition: always()
|
||||
pool:
|
||||
name: '$(macOSVersion)'
|
||||
variables:
|
||||
jobResultDebugiPhoneAF: $[ dependencies.macOS.outputs['Debug|iPhone|A-F.ExportedVariables.JobStatus'] ]
|
||||
jobResultDebugiPhoneGR: $[ dependencies.macOS.outputs['Debug|iPhone|G-R.ExportedVariables.JobStatus'] ]
|
||||
jobResultDebugiPhoneSZ: $[ dependencies.macOS.outputs['Debug|iPhone|S-Z.ExportedVariables.JobStatus'] ]
|
||||
jobResultDebugiPhoneSimulator: $[ dependencies.macOS.outputs['Debug|iPhoneSimulator.ExportedVariables.JobStatus'] ]
|
||||
jobResultReleaseiPhoneAF: $[ dependencies.macOS.outputs['Release|iPhone|A-F.ExportedVariables.JobStatus'] ]
|
||||
jobResultReleaseiPhoneGR: $[ dependencies.macOS.outputs['Release|iPhone|G-R.ExportedVariables.JobStatus'] ]
|
||||
jobResultReleaseiPhoneSZ: $[ dependencies.macOS.outputs['Release|iPhone|S-Z.ExportedVariables.JobStatus'] ]
|
||||
jobResultReleaseiPhoneSimulator: $[ dependencies.macOS.outputs['Release|iPhoneSimulator.ExportedVariables.JobStatus'] ]
|
||||
jobResultDebugMac: $[ dependencies.macOS.outputs['Debug|Mac.ExportedVariables.JobStatus'] ]
|
||||
jobResultReleaseMac: $[ dependencies.macOS.outputs['Release|Mac.ExportedVariables.JobStatus'] ]
|
||||
steps:
|
||||
- bash: |
|
||||
./tools/devops/build-samples-report-to-github.sh "$(github-pat)" \
|
||||
"Debug|iPhone|A-F" "Debug|iPhone|G-R" "Debug|iPhone|S-Z" \
|
||||
"Debug|iPhoneSimulator" \
|
||||
"Release|iPhone|A-F" "Release|iPhone|G-R" "Release|iPhone|S-Z" \
|
||||
"Release|iPhoneSimulator" \
|
||||
"Debug|Mac" \
|
||||
"Release|Mac"
|
||||
displayName: Report results to GitHub as comment / status
|
||||
condition: always()
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash -eux
|
||||
|
||||
# Make sure we've enabled our xamarin bits
|
||||
./configure --enable-xamarin
|
||||
|
||||
# grab Azure Devop's authorization token from the current repo, and use add it to the global git configuration
|
||||
AUTH=$(git config -l | grep AUTHORIZATION | sed 's/.*AUTHORIZATION: //')
|
||||
git config --global http.extraheader "AUTHORIZATION: $AUTH"
|
||||
|
||||
# fetch maccore
|
||||
# the github auth we use only works with https, so change maccore's url to be https:// instead of git@
|
||||
make reset-maccore MACCORE_MODULE="$(grep ^MACCORE_MODULE mk/xamarin.mk | sed -e 's/.*:= //' -e 's_git@github.com:_https://github.com/_' -e 's/[.]git//')" V=1
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash -x
|
||||
|
||||
# we do not want errors to fail the script, we want to print as much info as possible, so we don't pass -e to bash
|
||||
|
||||
uname -a
|
||||
ls -la /Library/Frameworks/Xamarin.iOS.framework/Versions
|
||||
ls -la /Library/Frameworks/Xamarin.Mac.framework/Versions
|
||||
ls -la /Library/Frameworks/ObjectiveSharpie.framework/Versions/
|
||||
ls -lad /Applications/Xcode*
|
||||
xcode-select -p
|
||||
mono --version
|
||||
env | sort
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,80 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
string DownloadWithGithubAuth (string uri)
|
||||
{
|
||||
var downloader = new Downloader ();
|
||||
var path = Path.GetTempFileName ();
|
||||
var headers = new List<(string, string)> ();
|
||||
var authToken = AuthToken ("github.com");
|
||||
if (!string.IsNullOrEmpty (authToken))
|
||||
headers.Add (("Authorization", $"token {authToken}"));
|
||||
path = downloader
|
||||
.DownloadItemAsync (
|
||||
uri,
|
||||
headers.ToArray (),
|
||||
Path.GetDirectoryName (path),
|
||||
Path.GetFileName (path),
|
||||
options: Downloader.Options.Default.WithUseCache (false))
|
||||
.GetAwaiter ()
|
||||
.GetResult ();
|
||||
try {
|
||||
return File.ReadAllText (path);
|
||||
} finally {
|
||||
File.Delete (path);
|
||||
}
|
||||
}
|
||||
|
||||
string manifest_url = null;
|
||||
string GetManifestUrl (string hash)
|
||||
{
|
||||
if (manifest_url == null) {
|
||||
var url = $"https://api.github.com/repos/xamarin/xamarin-macios/statuses/{hash}";
|
||||
var json = JToken.Parse (DownloadWithGithubAuth (url));
|
||||
var value = (JValue) ((JArray) json).Where ((v) => v ["context"].ToString () == "manifest").Select ((v) => v ["target_url"]).FirstOrDefault ();
|
||||
manifest_url = (string) value?.Value;
|
||||
if (manifest_url == null)
|
||||
throw new Exception ($"Could not find the manifest for {hash}. Is the commit already built by CI?");
|
||||
}
|
||||
return manifest_url;
|
||||
}
|
||||
|
||||
string[] manifest = null;
|
||||
string[] GetManifest (string hash)
|
||||
{
|
||||
if (manifest == null)
|
||||
manifest = ReadAllText (GetManifestUrl (hash)).Split ('\n');
|
||||
return manifest;
|
||||
}
|
||||
|
||||
// Looks for a variable either in the environment, or in current repo's Make.config.
|
||||
// Returns null if the variable couldn't be found.
|
||||
IEnumerable<string> make_config = null;
|
||||
string FindConfigurationVariable (string variable, string hash = "HEAD")
|
||||
{
|
||||
var value = Environment.GetEnvironmentVariable (variable);
|
||||
if (!string.IsNullOrEmpty (value))
|
||||
return value;
|
||||
|
||||
if (make_config == null)
|
||||
make_config = Exec ("git", "show", $"{hash}:Make.config");
|
||||
foreach (var line in make_config) {
|
||||
if (line.StartsWith (variable + "=", StringComparison.Ordinal))
|
||||
return line.Substring (variable.Length + 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void InstallPackage (string name, string url)
|
||||
{
|
||||
Console.WriteLine ($"Installing {name} from {url}");
|
||||
var version = Regex.Match (url, "[0-9]+[.][0-9]+[.][0-9]+([.][0-9]+)?").Value;
|
||||
Item (name, version).Source (url);
|
||||
}
|
Загрузка…
Ссылка в новой задаче