Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] First Pass at adding Unit tests for the…
Browse files Browse the repository at this point in the history
… MSBuild Tasks

This commit adds basic support for the MSBuild Unit Tests. The goal
of these tests is to ensure that the build of an android applicaition
works consistently on all the supported platforms.

The basic layout of a Unit test is as follows

	[Test]
	public void BuildReleaseApplication ()
	{
		var proj = new XamarinAndroidApplicationProject () {
			IsRelease = true,
		};
		using (var b = CreateApkBuilder (Path.Combine ("temp", TestContext.CurrentContext.Test.Name))) {
			Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
		}
	}

It is a standard Unit test. First we create a XamarinAndroidApplicatonProject
and set its properties. You can use the proj to add new source files,
references, assets, resources and nuget packages. By default you will get
a standard "HelloWorld" android application.
Once you have the project you will need to create either a Apk or Dll builder
via the helper methods

	CreateApkBuilder
	CreateDllBuilder

CreateApkBuilder will create an apk and by default will execute the
`SignAndroidPackage` build target. the CreateDllBuilder will produce
a dll. The source files are created in a temp directory relative to
the build output of the unit tests. By default this is

	src/Xamarin.Android.Build.Tasks/UnitTests/Xamarin.Android.Build.Tests/bin/$(Configuraiton)/temp

Once you have a builder you can then call Build passing in the project. There are
also methods available for Clean and Save. Running any of these will cause
the Unit test to shell out to xbuild/msbuild and attempt to build the project.
Test results are written to a build.log file.
If a unit test passes then the test directory is removed. If a test fails
the directory and the build.log will be left in place for investigation.
  • Loading branch information
dellis1972 committed May 12, 2016
1 parent 4821159 commit 4efc24a
Show file tree
Hide file tree
Showing 60 changed files with 19,379 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ Configuration.Override.props
obj
packages
.DS_Store
.nuget
TestResult.xml
22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
V ?= 0
CONFIGURATION = Debug
MSBUILD = xbuild /p:Configuration=$(CONFIGURATION) $(MSBUILD_ARGS)
RUNTIME := $(shell if [ -f `which mono64` ] ; then echo mono64 ; else echo mono; fi) --debug=casts

NUNIT_TESTS = \
bin/Test$(CONFIGURATION)/Xamarin.Android.Build.Tests.dll

NUNIT_CONSOLE = packages/NUnit.ConsoleRunner.3.2.1/tools/nunit3-console.exe

ifneq ($(V),0)
MONO_OPTIONS += --debug
Expand All @@ -19,5 +25,21 @@ prepare:
nuget restore
(cd external/Java.Interop && nuget restore)


run-all-tests: run-nunit-tests

clean:
$(MSBUILD) /t:Clean


# $(call RUN_NUNIT_TEST,filename,log-lref?)
define RUN_NUNIT_TEST
MONO_TRACE_LISTENER=Console.Out \
$(RUNTIME) --runtime=v4.0.0 \
$(NUNIT_CONSOLE) $(NUNIT_EXTRA) $(1) \
$(if $(RUN),-run:$(RUN)) \
-output=bin/Test$(CONFIGURATION)/TestOutput-$(basename $(notdir $(1))).txt ;
endef

run-nunit-tests: $(NUNIT_TESTS)
$(foreach t,$(NUNIT_TESTS), $(call RUN_NUNIT_TEST,$(t),1))
32 changes: 32 additions & 0 deletions Xamarin.Android.sln
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api-xml-adjuster", "tools\a
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.NUnitLite", "src\Xamarin.Android.NUnitLite\Xamarin.Android.NUnitLite.csproj", "{4D603AA3-3BFD-43C8-8050-0CD6C2601126}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.ProjectTools", "src\Xamarin.Android.Build.Tasks\Tests\Xamarin.ProjectTools\Xamarin.ProjectTools.csproj", "{2DD1EE75-6D8D-4653-A800-0A24367F7F38}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Build.Tests", "src\Xamarin.Android.Build.Tasks\Tests\Xamarin.Android.Build.Tests\Xamarin.Android.Build.Tests.csproj", "{53E4ABF0-1085-45F9-B964-DCAE4B819998}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AnyCPU = Debug|AnyCPU
Expand Down Expand Up @@ -208,6 +214,30 @@ Global
{4D603AA3-3BFD-43C8-8050-0CD6C2601126}.XAIntegrationDebug|AnyCPU.Build.0 = Debug|Any CPU
{4D603AA3-3BFD-43C8-8050-0CD6C2601126}.XAIntegrationRelease|AnyCPU.ActiveCfg = Debug|Any CPU
{4D603AA3-3BFD-43C8-8050-0CD6C2601126}.XAIntegrationRelease|AnyCPU.Build.0 = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Release|AnyCPU.Build.0 = Release|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationRelease|Any CPU.ActiveCfg = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationRelease|Any CPU.Build.0 = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationDebug|AnyCPU.ActiveCfg = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationDebug|AnyCPU.Build.0 = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationRelease|AnyCPU.ActiveCfg = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.XAIntegrationRelease|AnyCPU.Build.0 = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.Release|AnyCPU.Build.0 = Release|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationRelease|Any CPU.ActiveCfg = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationRelease|Any CPU.Build.0 = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationDebug|AnyCPU.ActiveCfg = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationDebug|AnyCPU.Build.0 = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationRelease|AnyCPU.ActiveCfg = Debug|Any CPU
{53E4ABF0-1085-45F9-B964-DCAE4B819998}.XAIntegrationRelease|AnyCPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62}
Expand All @@ -234,6 +264,8 @@ Global
{53EE4C57-1C03-405A-8243-8DA539546C88} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{8A6CB07C-E493-4A4F-AB94-038645A27118} = {864062D3-A415-4A6F-9324-5820237BA058}
{4D603AA3-3BFD-43C8-8050-0CD6C2601126} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{2DD1EE75-6D8D-4653-A800-0A24367F7F38} = {CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}
{53E4ABF0-1085-45F9-B964-DCAE4B819998} = {CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.IO;
using NUnit.Framework;
using Xamarin.ProjectTools;

namespace Xamarin.Android.Build.Tests
{
[Parallelizable (ParallelScope.Fixtures)]
public class BuildTest : BaseTest
{
[Test]
public void BuildReleaseApplication ()
{
var proj = new XamarinAndroidApplicationProject () {
IsRelease = true,
};
using (var b = CreateApkBuilder (Path.Combine ("temp", TestContext.CurrentContext.Test.Name))) {
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
}
}

[Test]
public void BuildReleaseApplicationWithNugetPackages ()
{
var proj = new XamarinAndroidApplicationProject () {
IsRelease = true,
Packages = {
KnownPackages.AndroidSupportV4_21_0_3_0,
},
};
using (var b = CreateApkBuilder (Path.Combine ("temp", TestContext.CurrentContext.Test.Name))) {
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
DirectoryAssert.Exists (Path.Combine (Root, "temp","packages", "Xamarin.Android.Support.v4.21.0.3.0"),
"Nuget Package Xamarin.Android.Support.v4.21.0.3.0 should have been restored.");
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.IO;
using System.Collections.Generic;
using Xamarin.ProjectTools;
using NUnit.Framework;
using System.Linq;

namespace Xamarin.Android.Build.Tests
{
public class BaseTest
{
static BaseTest ()
{
try {
string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".exe" : "";
string adb = Path.Combine (Environment.GetEnvironmentVariable ("ANDROID_SDK_PATH"), "platform-tools", "adb" + ext);
var proc = System.Diagnostics.Process.Start (new System.Diagnostics.ProcessStartInfo (adb, "devices") { RedirectStandardOutput = true, UseShellExecute = false });
proc.WaitForExit ();
var output = proc.StandardOutput.ReadToEnd ().Trim ();
// We wouldn't like to unexpectedly deploy to connected devices while running tests, so filter by target name for now...
HasDevices = output.Split ('\n').Where (s => s.Contains ("emulator-")).Count () > 0;
}
catch (Exception ex) {
Console.Error.WriteLine ("Failed to determine whether there is Android target emulator or not" + ex);
}
}

public static readonly bool HasDevices;

protected bool IsWindows {
get { return Environment.OSVersion.Platform == PlatformID.Win32NT; }
}

public string CacheRootPath {
get {
return IsWindows ? Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData)
: Environment.GetFolderPath (Environment.SpecialFolder.Personal);
}
}

public string CachePath {
get {
return IsWindows ? Path.Combine (CacheRootPath, "Xamarin")
: Path.Combine (CacheRootPath, ".local", "share", "Xamarin");
}
}

public string StagingPath {
get { return Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments); }
}

public string Root {
get {
return Path.GetDirectoryName (new Uri (typeof (XamarinProject).Assembly.CodeBase).LocalPath);
}
}

protected ProjectBuilder CreateApkBuilder (string directory, bool cleanupAfterSuccessfulBuild = false, bool cleanupOnDispose = true)
{
TestContext.CurrentContext.Test.Properties ["Output"] = new string [] { Path.Combine (Root, directory) };
return BuildHelper.CreateApkBuilder (directory, cleanupAfterSuccessfulBuild, cleanupOnDispose);
}

protected ProjectBuilder CreateDllBuilder (string directory, bool cleanupAfterSuccessfulBuild = false, bool cleanupOnDispose = true)
{
TestContext.CurrentContext.Test.Properties ["Output"] = new string [] { Path.Combine (Root, directory) };
return BuildHelper.CreateDllBuilder (directory, cleanupAfterSuccessfulBuild, cleanupOnDispose);
}

[OneTimeSetUp]
public void FixtureSetup ()
{
// Clean the Resource Cache.
if (string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("BUILD_HOST")))
return;
if (Directory.Exists (CachePath)) {
foreach (var subDir in Directory.GetDirectories (CachePath, "*", SearchOption.TopDirectoryOnly)) {
// ignore known useful directories.
if (subDir.EndsWith ("Mono for Android", StringComparison.OrdinalIgnoreCase) ||
subDir.EndsWith ("Cache", StringComparison.OrdinalIgnoreCase) ||
subDir.EndsWith ("Log", StringComparison.OrdinalIgnoreCase)
|| subDir.EndsWith ("Logs", StringComparison.OrdinalIgnoreCase))
continue;
Console.WriteLine ("[FixtureSetup] Removing Resource Cache Directory {0}", subDir);
Directory.Delete (subDir, recursive: true);
}
}
}

[TearDown]
protected virtual void CleanupTest ()
{
if (TestContext.CurrentContext.Result.Outcome.Status == NUnit.Framework.Interfaces.TestStatus.Passed) {
if (TestContext.CurrentContext.Test.Properties ["Output"] == null)
return;
// find the "root" directory just below "temp" and clean from there because
// some tests create multiple subdirectories
var output = Path.GetFullPath (((string [])TestContext.CurrentContext.Test.Properties ["Output"]) [0]);
while (!Directory.GetParent (output).Name.EndsWith ("temp", StringComparison.OrdinalIgnoreCase)) {
output = Directory.GetParent (output).FullName;
}
if (Directory.Exists (output)) {
FileSystemUtils.SetDirectoryWriteable (output);
Directory.Delete (output, recursive: true);
}
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using Microsoft.Build.Framework;
using Xamarin.ProjectTools;

namespace Xamarin.Android.Build.Tests
{
public static class BuildHelper
{
// In general here I don't make use of IDisposable pattern for ProjectBuilder because
// in case it failed I rather want to preserve the files to investigate.

public static ProjectBuilder CreateApkBuilder (string directory, bool cleanupAfterSuccessfulBuild = false, bool cleanupOnDispose = true)
{
var ret = CreateDllBuilder (directory, cleanupAfterSuccessfulBuild, cleanupOnDispose);
ret.Target = "SignAndroidPackage";
return ret;
}

public static ProjectBuilder CreateDllBuilder (string directory, bool cleanupAfterSuccessfulBuild = false, bool cleanupOnDispose = true)
{
return new ProjectBuilder (directory) {
CleanupAfterSuccessfulBuild = cleanupAfterSuccessfulBuild,
CleanupOnDispose = cleanupOnDispose,
Verbosity = LoggerVerbosity.Diagnostic
};
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\Configuration.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{53E4ABF0-1085-45F9-B964-DCAE4B819998}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Xamarin.Android.Build.Tests</RootNamespace>
<AssemblyName>Xamarin.Android.Build.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\bin\TestDebug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\..\..\bin\TestRelease</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Microsoft.Build" />
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="nunit.framework">
<HintPath>..\..\..\..\packages\NUnit.3.2.1\lib\net45\nunit.framework.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Utilities\BaseTest.cs" />
<Compile Include="Utilities\BuildHelper.cs" />
<Compile Include="BuildTest.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\Xamarin.ProjectTools\Xamarin.ProjectTools.csproj">
<Project>{2DD1EE75-6D8D-4653-A800-0A24367F7F38}</Project>
<Name>Xamarin.ProjectTools</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
<Folder Include="Utilities\" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="3.2.1" targetFramework="net45" />
<package id="Unofficial.Ionic.Zip" version="1.9.1.8" targetFramework="net45" />
<package id="NUnit.ConsoleRunner" version="3.2.1" />
</packages>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Microsoft.Build.Construction;

namespace Xamarin.ProjectTools
{
public static class AndroidBuildActions
{
public const string AndroidResource = "AndroidResource";
public const string AndroidAsset = "AndroidAsset";
public const string AndroidEnvironment = "AndroidEnvironment";
public const string AndroidInterfaceDescription = "AndroidInterfaceDescription";
public const string AndroidJavaSource = "AndroidJavaSource";
public const string AndroidJavaLibrary = "AndroidJavaLibrary";
public const string AndroidLintConfig = "AndroidLintConfig";
public const string AndroidNativeLibrary = "AndroidNativeLibrary";
public const string ProguardConfiguration = "ProguardConfiguration";
public const string TransformFile = "TransformFile";
public const string InputJar = "InputJar";
public const string ReferenceJar = "ReferenceJar";
public const string EmbeddedJar = "EmbeddedJar";
public const string EmbeddedNativeLibrary = "EmbeddedNativeLibrary";
public const string EmbeddedReferenceJar = "EmbeddedReferenceJar";
public const string LibraryProjectZip = "LibraryProjectZip";
public const string LibraryProjectProperties = "LibraryProjectProperties";
}
}
Loading

0 comments on commit 4efc24a

Please sign in to comment.