Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ protected override void OnCreate (Bundle bundle)
button.Click += delegate {
button.Text = string.Format ("{0} clicks!", count++);
};

//${AFTER_ONCREATE}
}
}
}
Expand Down
327 changes: 327 additions & 0 deletions tools/msbuild-fuzzer/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Xamarin.ProjectTools;

namespace MSBuild.Fuzzer
{
class Program
{
static readonly Random random = new Random ();
static readonly List<string> installedPackages = new List<string> ();
static ProjectBuilder builder;
static XamarinFormsAndroidApplicationProject application;
static bool needsInstall = true;
static string directory;

static void Main ()
{
var temp = Path.Combine (Path.GetDirectoryName (typeof (Program).Assembly.Location));
using (builder = new ProjectBuilder (Path.Combine ("temp", "Fuzzer")) {
AutomaticNuGetRestore = false,
CleanupAfterSuccessfulBuild = false,
CleanupOnDispose = true,
Root = Xamarin.Android.Build.Paths.TestOutputDirectory,
}) {
directory = Path.GetFullPath (Path.Combine (builder.Root, builder.ProjectDirectory));
if (Directory.Exists (directory))
Directory.Delete (directory, recursive: true);

application = new XamarinFormsAndroidApplicationProject ();
application.AndroidManifest = application.AndroidManifest.Replace ("<uses-sdk />", "<uses-sdk android:targetSdkVersion=\"28\" />");
application.MainActivity = application.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}", "Android.Util.Log.Debug (\"FUZZER\", \"App started!\");");
var abis = new string [] { "armeabi-v7a", "arm64-v8a", "x86" };
application.SetProperty (KnownProperties.AndroidSupportedAbis, string.Join (";", abis));

if (!NuGetRestore ()) {
Console.WriteLine ("Initial NuGet restore failed!");
return;
}

Func<bool> [] operations = {
AddClass,
AddResource,
Build,
ChangePackageName,
Clean,
DesignerBuild,
DesignTimeBuild,
Install,
NuGetRestore,
RemoveClass,
RemoveResource,
RenameClass,
RenameResource,
Run,
TouchRandomFile,
Uninstall,
};

while (true) {
var operation = operations [random.Next (operations.Length)];
if (!operation ()) {
break;
}
}
}

Console.WriteLine ("Press enter to exit...");
Console.ReadLine ();
}

static bool NuGetRestore ()
{
Console.WriteLine (nameof (NuGetRestore));
return builder.RunTarget (application, "Restore", doNotCleanupOnUpdate: true);
}

static bool Build ()
{
Console.WriteLine (nameof (Build));
return builder.Build (application, doNotCleanupOnUpdate: true);
}

static bool DesignTimeBuild ()
{
Console.WriteLine (nameof (DesignTimeBuild));
return builder.DesignTimeBuild (application, doNotCleanupOnUpdate: true);
}

static readonly string [] DesignerParameters = new [] { "DesignTimeBuild=True", "AndroidUseManagedDesignTimeResourceGenerator=False" };

static bool DesignerBuild ()
{
Console.WriteLine (nameof (DesignerBuild));
return builder.RunTarget (application, "SetupDependenciesForDesigner", doNotCleanupOnUpdate: true, parameters: DesignerParameters);
}

static bool Clean ()
{
Console.WriteLine (nameof (Clean));
return builder.Clean (application, doNotCleanupOnUpdate: true);
}

static bool Install ()
{
Console.WriteLine (nameof (Install));
if (!builder.Install (application, doNotCleanupOnUpdate: true)) {
return false;
}
if (!installedPackages.Contains (application.PackageName))
installedPackages.Add (application.PackageName);
needsInstall = false;
return true;
}

static bool Uninstall ()
{
Console.WriteLine (nameof (Uninstall));
foreach (var packageName in installedPackages) {
Adb ($"uninstall {packageName}");
}
installedPackages.Clear ();
needsInstall = true;
return true;
}

static bool Run ()
{
if (needsInstall && !Install ())
return false;
Console.WriteLine (nameof (Run));
string stop = Adb ($"shell am force-stop {application.PackageName}", ignoreExitCode: true);
Adb ("logcat -c");
string activity = $"{application.PackageName}/md52d9cf6333b8e95e8683a477bc589eda5.MainActivity";
string start = Adb ($"shell am start -n {activity}");
Console.WriteLine (start.Trim ());
//Wait for the app to start
Thread.Sleep (3000);
string logcat = Adb ("logcat -d");
if (!logcat.Contains ("App started!")) {
Console.WriteLine (logcat);
throw new Exception ($"Activity {activity} did not start!");
}
return true;
}

static string Adb (string arguments, bool ignoreExitCode = false)
{
var info = new ProcessStartInfo {
FileName = "adb",
Arguments = arguments,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
};
var builder = new StringBuilder ();
using (var process = new Process ()) {
var stdout_done = new ManualResetEventSlim (false);
var stderr_done = new ManualResetEventSlim (false);
process.StartInfo = info;
process.OutputDataReceived += (sender, e) => {
if (e.Data != null) {
builder.AppendLine (e.Data);
} else {
stdout_done.Set ();
}
};
process.ErrorDataReceived += (sender, e) => {
if (e.Data != null) {
builder.AppendLine (e.Data);
} else {
stderr_done.Set ();
}
};
process.Start ();
process.BeginErrorReadLine ();
process.BeginOutputReadLine ();
process.WaitForExit ();
stderr_done.Wait ();
stdout_done.Wait ();
if (!ignoreExitCode && process.ExitCode != 0) {
Console.WriteLine (builder);
throw new Exception ($"Adb exited with code: {process.ExitCode}");
}
}
return builder.ToString ();
}

static readonly string [] extensions = {
".cs",
".csproj",
".png",
".xaml",
".xml",
};

static bool TouchRandomFile ()
{
Console.WriteLine (nameof (TouchRandomFile));
var files = (from f in Directory.EnumerateFiles (directory, "*", SearchOption.AllDirectories)
let relative = f.Substring (directory.Length + 1)
where !relative.StartsWith ("obj") && !relative.StartsWith ("bin")
let ext = Path.GetExtension (f)
where extensions.Contains (ext)
select f).ToArray ();
var file = files [random.Next (0, files.Length)];
File.SetLastWriteTimeUtc (file, DateTime.UtcNow);
File.SetLastAccessTimeUtc (file, DateTime.UtcNow);
return true;
}

static bool ChangePackageName ()
{
Console.WriteLine (nameof (ChangePackageName));
application.PackageName = "com.foo.a" + RandomName ();
application.Touch ("Properties\\AndroidManifest.xml");
needsInstall = true;
return true;
}

static bool AddClass ()
{
Console.WriteLine (nameof (AddClass));
application.Sources.Add (new Class ());
return true;
}

static bool RemoveClass ()
{
Console.WriteLine (nameof (RemoveClass));
for (int i = application.Sources.Count - 1; i >= 0; i--) {
if (application.Sources [i] is Class) {
application.Sources.RemoveAt (i);
break;
}
}
return true;
}

static bool RenameClass ()
{
Console.WriteLine (nameof (RemoveClass));
var clazz = application.Sources.OfType<Class> ().FirstOrDefault ();
if (clazz != null) {
clazz.Rename ();
}
return true;
}

static bool AddResource ()
{
Console.WriteLine (nameof (AddResource));
application.Sources.Add (new AndroidResource ());
return true;
}

static bool RemoveResource ()
{
Console.WriteLine (nameof (RemoveResource));
for (int i = application.Sources.Count - 1; i >= 0; i--) {
if (application.Sources [i] is AndroidResource) {
application.Sources.RemoveAt (i);
break;
}
}
return true;
}

static bool RenameResource ()
{
Console.WriteLine (nameof (RenameResource));
var resource = application.Sources.OfType<AndroidResource> ().FirstOrDefault ();
if (resource != null) {
resource.Rename ();
}
return true;
}

static string RandomName () => Guid.NewGuid ().ToString ("N");

class Class : BuildItem.Source
{
public string TypeName { get; set; }

public Class () : base (RandomName () + ".cs")
{
Rename ();
TextContent = () => $"public class Foo{TypeName} : Java.Lang.Object {{ }}";
}

public void Rename ()
{
TypeName = RandomName ();
Timestamp = null;
}
}

class AndroidResource : BuildItem
{
public string ResourceId { get; set; }

public string StringValue { get; set; }

public AndroidResource () : base ("AndroidResource", RandomName () + ".xml")
{
Rename ();
TextContent = () => $@"<?xml version=""1.0"" encoding=""utf-8""?>
<resources>
<string name=""foo_{ResourceId}"">{StringValue}</string>
</resources>";
}

public void Rename ()
{
ResourceId = RandomName ();
StringValue = RandomName ();
Timestamp = null;
}
}
}
}
14 changes: 14 additions & 0 deletions tools/msbuild-fuzzer/msbuild-fuzzer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472</TargetFrameworks>
<ProjectGuid>{A3671983-ABC2-4693-8872-463427B57117}</ProjectGuid>
<OutputType>Exe</OutputType>
</PropertyGroup>
<Import Project="..\..\Configuration.props" />
<ItemGroup>
<ProjectReference Include="..\..\src\Xamarin.Android.Build.Tasks\Tests\Xamarin.ProjectTools\Xamarin.ProjectTools.csproj">
<Project>{2dd1ee75-6d8d-4653-a800-0a24367f7f38}</Project>
<Name>Xamarin.ProjectTools</Name>
</ProjectReference>
</ItemGroup>
</Project>
31 changes: 31 additions & 0 deletions tools/msbuild-fuzzer/msbuild-fuzzer.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.352
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "msbuild-fuzzer", "msbuild-fuzzer.csproj", "{A3671983-ABC2-4693-8872-463427B57117}"
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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A3671983-ABC2-4693-8872-463427B57117}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3671983-ABC2-4693-8872-463427B57117}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3671983-ABC2-4693-8872-463427B57117}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3671983-ABC2-4693-8872-463427B57117}.Release|Any CPU.Build.0 = Release|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2DD1EE75-6D8D-4653-A800-0A24367F7F38}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E88EED5D-9DFA-405D-9495-4EC9A146B249}
EndGlobalSection
EndGlobal