* [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:
Rolf Bjarne Kvinge 2019-04-25 17:18:43 +02:00 коммит произвёл GitHub
Родитель d988124684
Коммит bd457212d5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
30 изменённых файлов: 1728 добавлений и 11 удалений

23
jenkins/Jenkinsfile поставляемый
Просмотреть файл

@ -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));

3
tests/sampletester/.gitignore поставляемый Normal file
Просмотреть файл

@ -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">&lt;!DOCTYPE html&gt;&#10;</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[&nbsp;]]></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,
}

Двоичные данные
tests/sampletester/images/provision_from_commit.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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");

8
tools/devops/build-samples.sh Executable file
Просмотреть файл

@ -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()

12
tools/devops/fetch-maccore.sh Executable file
Просмотреть файл

@ -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

14
tools/devops/system-info.sh Executable file
Просмотреть файл

@ -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

80
tools/devops/utils.csx Normal file
Просмотреть файл

@ -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);
}