Beispiel #1
0
        public void ApplicationRunsWithDebuggerAndBreaks(bool useSharedRuntime, bool embedAssemblies, string fastDevType, bool allowDeltaInstall)
        {
            if (!CommercialBuildAvailable)
            {
                Assert.Ignore("Test does not run on the Open Source Builds.");
                return;
            }
            if (!HasDevices)
            {
                Assert.Ignore("Test needs a device attached.");
                return;
            }
            var proj = new XamarinFormsAndroidApplicationProject()
            {
                IsRelease = false,
                AndroidUseSharedRuntime   = useSharedRuntime,
                EmbedAssembliesIntoApk    = embedAssemblies,
                AndroidFastDeploymentType = fastDevType
            };
            var abis = new string [] { "armeabi-v7a", "x86" };

            proj.SetProperty(KnownProperties.AndroidSupportedAbis, string.Join(";", abis));
            if (allowDeltaInstall)
            {
                proj.SetProperty(KnownProperties._AndroidAllowDeltaInstall, "true");
            }
            proj.SetDefaultTargetDevice();
            using (var b = CreateApkBuilder(Path.Combine("temp", TestName))) {
                SetTargetFrameworkAndManifest(proj, b);
                Assert.True(b.Install(proj), "Project should have installed.");

                int breakcountHitCount      = 0;
                ManualResetEvent resetEvent = new ManualResetEvent(false);
                var sw = new Stopwatch();
                // setup the debugger
                var session = new SoftDebuggerSession();
                session.Breakpoints = new BreakpointStore {
                    { Path.Combine(Root, b.ProjectDirectory, "MainActivity.cs"), 19 },
                    { Path.Combine(Root, b.ProjectDirectory, "MainPage.xaml.cs"), 14 },
                    { Path.Combine(Root, b.ProjectDirectory, "MainPage.xaml.cs"), 19 },
                    { Path.Combine(Root, b.ProjectDirectory, "App.xaml.cs"), 12 },
                };
                session.TargetHitBreakpoint += (sender, e) => {
                    Console.WriteLine($"BREAK {e.Type}");
                    breakcountHitCount++;
                    session.Continue();
                };
                var rnd  = new Random();
                int port = rnd.Next(10000, 20000);
                TestContext.Out.WriteLine($"{port}");
                var args = new SoftDebuggerConnectArgs("", IPAddress.Loopback, port)
                {
                    MaxConnectionAttempts = 10,
                };
                var startInfo = new SoftDebuggerStartInfo(args)
                {
                    WorkingDirectory = Path.Combine(b.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets"),
                };
                var options = new DebuggerSessionOptions()
                {
                    EvaluationOptions = EvaluationOptions.DefaultOptions,
                };
                options.EvaluationOptions.UseExternalTypeResolver = true;
                ClearAdbLogcat();
                Assert.True(b.RunTarget(proj, "_Run", parameters: new string [] {
                    $"AndroidSdbTargetPort={port}",
                    $"AndroidSdbHostPort={port}",
                    "AndroidAttachDebugger=True",
                }), "Project should have run.");

                Assert.IsTrue(WaitForDebuggerToStart(Path.Combine(Root, b.ProjectDirectory, "logcat.log")), "Activity should have started");
                // we need to give a bit of time for the debug server to start up.
                WaitFor(2000);
                session.LogWriter    += (isStderr, text) => { Console.WriteLine(text); };
                session.OutputWriter += (isStderr, text) => { Console.WriteLine(text); };
                session.DebugWriter  += (level, category, message) => { Console.WriteLine(message); };
                session.Run(startInfo, options);
                WaitFor(TimeSpan.FromSeconds(30), () => session.IsConnected);
                Assert.True(session.IsConnected, "Debugger should have connected but it did not.");
                // we need to wait here for a while to allow the breakpoints to hit
                // but we need to timeout
                TimeSpan timeout  = TimeSpan.FromSeconds(60);
                int      expected = 3;
                while (session.IsConnected && breakcountHitCount < 3 && timeout >= TimeSpan.Zero)
                {
                    Thread.Sleep(10);
                    timeout = timeout.Subtract(TimeSpan.FromMilliseconds(10));
                }
                WaitFor(2000);
                Assert.AreEqual(expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
                breakcountHitCount = 0;
                ClearAdbLogcat();
                ClickButton(proj.PackageName, "myXFButton", "CLICK ME");
                while (session.IsConnected && breakcountHitCount < 1 && timeout >= TimeSpan.Zero)
                {
                    Thread.Sleep(10);
                    timeout = timeout.Subtract(TimeSpan.FromMilliseconds(10));
                }
                expected = 1;
                Assert.AreEqual(expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
                Assert.True(b.Uninstall(proj), "Project should have uninstalled.");
                session.Exit();
            }
        }
Beispiel #2
0
        public void CustomApplicationRunsWithDebuggerAndBreaks(bool useSharedRuntime, bool embedAssemblies, string fastDevType, bool activityStarts)
        {
            AssertCommercialBuild();
            AssertHasDevices();
            var proj = new XamarinAndroidApplicationProject()
            {
                IsRelease = false,
                AndroidFastDeploymentType = fastDevType,
            };

            proj.SetAndroidSupportedAbis("armeabi-v7a", "x86");
            proj.SetProperty(KnownProperties.AndroidUseSharedRuntime, useSharedRuntime.ToString());
            proj.SetProperty("EmbedAssembliesIntoApk", embedAssemblies.ToString());
            proj.SetDefaultTargetDevice();
            proj.Sources.Add(new BuildItem.Source("MyApplication.cs")
            {
                TextContent = () => proj.ProcessSourceTemplate(@"using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Widget;

namespace ${ROOT_NAMESPACE} {
	[Application]
	public class MyApplication : Application {
		public MyApplication (IntPtr handle, JniHandleOwnership jniHandle)
			: base (handle, jniHandle)
		{
		}

		public override void OnCreate ()
		{
			base.OnCreate ();
	    }
	}
}
"),
            });
            using (var b = CreateApkBuilder(Path.Combine("temp", TestName))) {
                SetTargetFrameworkAndManifest(proj, b);
                Assert.True(b.Install(proj), "Project should have installed.");

                int breakcountHitCount      = 0;
                ManualResetEvent resetEvent = new ManualResetEvent(false);
                var sw = new Stopwatch();
                // setup the debugger
                var session = new SoftDebuggerSession();
                session.Breakpoints = new BreakpointStore {
                    { Path.Combine(Root, b.ProjectDirectory, "MainActivity.cs"), 19 },
                    { Path.Combine(Root, b.ProjectDirectory, "MyApplication.cs"), 17 },
                };
                session.TargetHitBreakpoint += (sender, e) => {
                    TestContext.WriteLine($"BREAK {e.Type}, {e.Backtrace.GetFrame (0)}");
                    breakcountHitCount++;
                    session.Continue();
                };
                var rnd  = new Random();
                int port = rnd.Next(10000, 20000);
                TestContext.Out.WriteLine($"{port}");
                var args = new SoftDebuggerConnectArgs("", IPAddress.Loopback, port)
                {
                    MaxConnectionAttempts = 10,
                };
                var startInfo = new SoftDebuggerStartInfo(args)
                {
                    WorkingDirectory = Path.Combine(b.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets"),
                };
                var options = new DebuggerSessionOptions()
                {
                    EvaluationOptions = EvaluationOptions.DefaultOptions,
                };
                options.EvaluationOptions.UseExternalTypeResolver = true;
                ClearAdbLogcat();
                Assert.True(b.RunTarget(proj, "_Run", parameters: new string [] {
                    $"AndroidSdbTargetPort={port}",
                    $"AndroidSdbHostPort={port}",
                    "AndroidAttachDebugger=True",
                }), "Project should have run.");

                // do we expect the app to start?
                Assert.AreEqual(activityStarts, WaitForDebuggerToStart(Path.Combine(Root, b.ProjectDirectory, "logcat.log")), "Activity should have started");
                if (!activityStarts)
                {
                    return;
                }
                // we need to give a bit of time for the debug server to start up.
                WaitFor(2000);
                session.LogWriter    += (isStderr, text) => { Console.WriteLine(text); };
                session.OutputWriter += (isStderr, text) => { Console.WriteLine(text); };
                session.DebugWriter  += (level, category, message) => { Console.WriteLine(message); };
                session.Run(startInfo, options);
                var expectedTime = TimeSpan.FromSeconds(1);
                var actualTime   = ProfileFor(() => session.IsConnected);
                TestContext.Out.WriteLine($"Debugger connected in {actualTime}");
                Assert.LessOrEqual(actualTime, expectedTime, $"Debugger should have connected within {expectedTime} but it took {actualTime}.");
                // we need to wait here for a while to allow the breakpoints to hit
                // but we need to timeout
                TimeSpan timeout = TimeSpan.FromSeconds(60);
                while (session.IsConnected && breakcountHitCount < 2 && timeout >= TimeSpan.Zero)
                {
                    Thread.Sleep(10);
                    timeout = timeout.Subtract(TimeSpan.FromMilliseconds(10));
                }
                WaitFor(2000);
                int expected = 2;
                Assert.AreEqual(expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
                Assert.True(b.Uninstall(proj), "Project should have uninstalled.");
                session.Exit();
            }
        }
        public void CustomApplicationRunsWithDebuggerAndBreaks(bool useSharedRuntime, bool embedAssemblies, string fastDevType, bool activityStarts)
        {
            if (!CommercialBuildAvailable)
            {
                Assert.Ignore("Test does not run on the Open Source Builds.");
                return;
            }
            if (!HasDevices)
            {
                Assert.Ignore("Test needs a device attached.");
                return;
            }
            var proj = new XamarinAndroidApplicationProject()
            {
                IsRelease = false,
                AndroidFastDeploymentType = fastDevType,
            };
            var abis = new string [] { "armeabi-v7a", "x86" };

            proj.SetProperty(KnownProperties.AndroidSupportedAbis, string.Join(";", abis));
            proj.SetProperty(KnownProperties.AndroidUseSharedRuntime, useSharedRuntime.ToString());
            proj.SetProperty("EmbedAssembliesIntoApk", embedAssemblies.ToString());
            proj.SetDefaultTargetDevice();
            proj.Sources.Add(new BuildItem.Source("MyApplication.cs")
            {
                TextContent = () => proj.ProcessSourceTemplate(@"using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Widget;

namespace ${ROOT_NAMESPACE} {
	[Application]
	public class MyApplication : Application {
		public MyApplication (IntPtr handle, JniHandleOwnership jniHandle)
			: base (handle, jniHandle)
		{
		}

		public override void OnCreate ()
		{
			base.OnCreate ();
	    }
	}
}
"),
            });
            using (var b = CreateApkBuilder(Path.Combine("temp", TestName))) {
                string apiLevel;
                proj.TargetFrameworkVersion = b.LatestTargetFrameworkVersion(out apiLevel);
                proj.AndroidManifest        = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<manifest xmlns:android=""http://schemas.android.com/apk/res/android"" android:versionCode=""1"" android:versionName=""1.0"" package=""UnnamedProject.UnnamedProject"">
	<uses-sdk android:minSdkVersion=""24"" android:targetSdkVersion=""{apiLevel}"" />
	<application android:label=""${{PROJECT_NAME}}"">
	</application >
</manifest>";
                b.Save(proj, saveProject: true);
                proj.NuGetRestore(Path.Combine(Root, b.ProjectDirectory), b.PackagesDirectory);
                Assert.True(b.Build(proj), "Project should have built.");
                Assert.True(b.Install(proj), "Project should have installed.");

                int breakcountHitCount      = 0;
                ManualResetEvent resetEvent = new ManualResetEvent(false);
                var sw = new Stopwatch();
                // setup the debugger
                var session = new SoftDebuggerSession();
                session.Breakpoints = new BreakpointStore {
                    { Path.Combine(Root, b.ProjectDirectory, "MainActivity.cs"), 19 },
                    { Path.Combine(Root, b.ProjectDirectory, "MyApplication.cs"), 17 },
                };
                session.TargetHitBreakpoint += (sender, e) => {
                    Console.WriteLine($"BREAK {e.Type}");
                    breakcountHitCount++;
                    session.Continue();
                };
                var rnd  = new Random();
                int port = rnd.Next(10000, 20000);
                TestContext.Out.WriteLine($"{port}");
                var args = new SoftDebuggerConnectArgs("", IPAddress.Loopback, port)
                {
                    MaxConnectionAttempts = 10,
                };
                var startInfo = new SoftDebuggerStartInfo(args)
                {
                    WorkingDirectory = Path.Combine(b.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets"),
                };
                var options = new DebuggerSessionOptions()
                {
                    EvaluationOptions = EvaluationOptions.DefaultOptions,
                };
                options.EvaluationOptions.UseExternalTypeResolver = true;
                ClearAdbLogcat();
                Assert.True(b.RunTarget(proj, "_Run", parameters: new string [] {
                    $"AndroidSdbTargetPort={port}",
                    $"AndroidSdbHostPort={port}",
                    "AndroidAttachDebugger=True",
                }), "Project should have run.");

                // do we expect the app to start?
                Assert.AreEqual(activityStarts, WaitForDebuggerToStart(Path.Combine(Root, b.ProjectDirectory, "logcat.log")), "Activity should have started");
                if (!activityStarts)
                {
                    return;
                }
                // we need to give a bit of time for the debug server to start up.
                WaitFor(2000);
                session.LogWriter    += (isStderr, text) => { Console.WriteLine(text); };
                session.OutputWriter += (isStderr, text) => { Console.WriteLine(text); };
                session.DebugWriter  += (level, category, message) => { Console.WriteLine(message); };
                session.Run(startInfo, options);
                WaitFor(TimeSpan.FromSeconds(30), () => session.IsConnected);
                Assert.True(session.IsConnected, "Debugger should have connected but it did not.");
                // we need to wait here for a while to allow the breakpoints to hit
                // but we need to timeout
                TimeSpan timeout = TimeSpan.FromSeconds(60);
                while (session.IsConnected && breakcountHitCount < 2 && timeout >= TimeSpan.Zero)
                {
                    Thread.Sleep(10);
                    timeout = timeout.Subtract(TimeSpan.FromMilliseconds(10));
                }
                WaitFor(2000);
                int expected = 2;
                Assert.AreEqual(expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
                Assert.True(b.Uninstall(proj), "Project should have uninstalled.");
                session.Exit();
            }
        }
        public void DotNetDebug()
        {
            AssertCommercialBuild();
            AssertHasDevices();

            XASdkProject proj;

            proj = new XASdkProject();
            proj.SetRuntimeIdentifier(DeviceAbi);

            var relativeProjDir = Path.Combine("temp", TestName);
            var fullProjDir     = Path.Combine(Root, relativeProjDir);

            TestOutputDirectories [TestContext.CurrentContext.Test.ID] = fullProjDir;
            var files = proj.Save();

            proj.Populate(relativeProjDir, files);
            proj.CopyNuGetConfig(relativeProjDir);
            var dotnet = new DotNetCLI(proj, Path.Combine(fullProjDir, proj.ProjectFilePath));

            Assert.IsTrue(dotnet.Build("Install"), "`dotnet build` should succeed");

            bool             breakpointHit = false;
            ManualResetEvent resetEvent    = new ManualResetEvent(false);
            var sw = new Stopwatch();
            // setup the debugger
            var session = new SoftDebuggerSession();

            session.Breakpoints = new BreakpointStore {
                { Path.Combine(Root, dotnet.ProjectDirectory, "MainActivity.cs"), 19 },
            };
            session.TargetHitBreakpoint += (sender, e) => {
                Console.WriteLine($"BREAK {e.Type}");
                breakpointHit = true;
                session.Continue();
            };
            var rnd  = new Random();
            int port = rnd.Next(10000, 20000);

            TestContext.Out.WriteLine($"{port}");
            var args = new SoftDebuggerConnectArgs("", IPAddress.Loopback, port)
            {
                MaxConnectionAttempts = 10,
            };
            var startInfo = new SoftDebuggerStartInfo(args)
            {
                WorkingDirectory = Path.Combine(dotnet.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets"),
            };
            var options = new DebuggerSessionOptions()
            {
                EvaluationOptions = EvaluationOptions.DefaultOptions,
            };

            options.EvaluationOptions.UseExternalTypeResolver = true;
            ClearAdbLogcat();
            Assert.True(dotnet.Build("Run", new string [] {
                $"AndroidSdbTargetPort={port}",
                $"AndroidSdbHostPort={port}",
                "AndroidAttachDebugger=True",
            }), "Project should have run.");

            Assert.IsTrue(WaitForDebuggerToStart(Path.Combine(Root, dotnet.ProjectDirectory, "logcat.log")), "Activity should have started");
            // we need to give a bit of time for the debug server to start up.
            WaitFor(2000);
            session.LogWriter    += (isStderr, text) => { Console.WriteLine(text); };
            session.OutputWriter += (isStderr, text) => { Console.WriteLine(text); };
            session.DebugWriter  += (level, category, message) => { Console.WriteLine(message); };
            session.Run(startInfo, options);
            WaitFor(TimeSpan.FromSeconds(30), () => session.IsConnected);
            Assert.True(session.IsConnected, "Debugger should have connected but it did not.");
            // we need to wait here for a while to allow the breakpoints to hit
            // but we need to timeout
            TimeSpan timeout = TimeSpan.FromSeconds(60);

            while (session.IsConnected && !breakpointHit && timeout >= TimeSpan.Zero)
            {
                Thread.Sleep(10);
                timeout = timeout.Subtract(TimeSpan.FromMilliseconds(10));
            }
            WaitFor(2000);
            Assert.IsTrue(breakpointHit, "Should have a breakpoint");
        }
        private string SerializeDebuggerOptions(string jsonDebugOptions)
        {
            try
            {
                NLogService.TraceEnteringMethod(Logger);
                var debugOptions = DebugOptions.DeserializeFromJson(jsonDebugOptions);

                _session = new SoftDebuggerSession();

                LogMonoDebuggerAssemblyPaths();

                if (debugOptions.UserSettings.EnableVerboseDebugLogging)
                {
                    Debug.WriteLine("TargetReady!");
                    CreateAndRegisterXamarinThread(eventArgs.Thread);
                }
                ;
                _session.ExceptionHandler = exception => true;
                _session.TargetExited    += (sender, x) =>
                {
                    Debug.WriteLine("TargetExited!");
                };
                _session.TargetUnhandledException += (sender, x) =>
                {
                    Debug.WriteLine("TargetUnhandledException!");
                };
                _session.LogWriter            = (stderr, text) => Debug.WriteLine(text);
                _session.OutputWriter         = (stderr, text) => Debug.WriteLine(text);
                _session.TargetThreadStarted += (sender, x) =>
                {
                    Debug.WriteLine("TargetThreadStarted!");
                    CreateAndRegisterXamarinThread(x.Thread);
                };
                _session.TargetThreadStopped += (sender, x) =>
                {
                    Debug.WriteLine("TargetThreadStopped!");
                };
                _session.TargetStopped += (sender, x) =>
                {
                    Debug.WriteLine(x.Type);
                };
                _session.TargetStarted     += (sender, x) => Debug.WriteLine("TargetStarted");
                _session.TargetSignaled    += (sender, x) => Debug.WriteLine(x.Type);
                _session.TargetInterrupted += (sender, x) =>
                {
                    Debug.WriteLine(x.Type);
                };
                _session.TargetExceptionThrown += (sender, x) =>
                {
                    Debug.WriteLine("TargetExceptionThrown!");
                };
                _session.TargetHitBreakpoint += (sender, x) =>
                {
                    Debug.WriteLine("TargetHitBreakpoint!");
                };
                _session.TargetEvent += _session_TargetEvent;

                var connectionTimeout       = 30000;
                var evaluationTimeout       = 30000;
                var startupProject          = StartupProject;
                var softDebuggerConnectArgs = new SoftDebuggerConnectArgs(debugOptions.TargetExeFileName, debugOptions.GetHostIP(), debugOptions.GetMonoDebugPort());

                // TODO implement programm output via stream
                //softDebuggerConnectArgs.RedirectOutput = true;
                //softDebuggerConnectArgs.OutputPort = ???;
                //_session.VirtualMachine.StandardOutput ???

                softDebuggerConnectArgs.TimeBetweenConnectionAttempts = (int)debugOptions.UserSettings.TimeBetweenConnectionAttemptsInMs;
                softDebuggerConnectArgs.MaxConnectionAttempts         = (int)debugOptions.UserSettings.MaxConnectionAttempts;

                _startInfo = new StartInfo(
                    softDebuggerConnectArgs,
                    new DebuggingOptions()
                {
                    EvaluationTimeout       = evaluationTimeout,
                    MemberEvaluationTimeout = evaluationTimeout,
                    ModificationTimeout     = evaluationTimeout,
                    SocketTimeout           = connectionTimeout
                },
                    startupProject
                    );

                SessionMarshalling sessionMarshalling = new SessionMarshalling(_session, _startInfo);
                using (MemoryStream ms = new MemoryStream())
                {
                    BinaryFormatter bf   = new BinaryFormatter();
                    ObjRef          oref = RemotingServices.Marshal(sessionMarshalling);
                    bf.Serialize(ms, oref);
                    return(Convert.ToBase64String(ms.ToArray()));
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex);
                throw;
            }
        }
        private void RunImpl(Response response, dynamic args, ExecutionType executionType)
        {
            lock (_lock)
            {
                _attachMode = executionType == ExecutionType.Attach;

                SetExceptionBreakpoints(args.__exceptionOptions);

                SoftDebuggerRemoteArgs listenArgs;

                if (executionType == ExecutionType.Attach)
                {
                    // validate argument 'address'
                    string host = getString(args, "address");
                    if (host == null)
                    {
                        SendErrorResponse(response, 3007, "Property 'address' is missing or empty.");
                        return;
                    }

                    // validate argument 'port'
                    int port = getInt(args, "port", -1);
                    if (port == -1)
                    {
                        SendErrorResponse(response, 3008, "Property 'port' is missing.");
                        return;
                    }

                    IPAddress address = Utilities.ResolveIPAddress(host);
                    if (address == null)
                    {
                        SendErrorResponse(response, 3013, "Invalid address '{host}'.", new { host });
                        return;
                    }

                    listenArgs = new SoftDebuggerConnectArgs("Godot", IPAddress.Loopback, port);
                }
                else
                {
                    listenArgs = new SoftDebuggerListenArgs("Godot", IPAddress.Loopback, 0);
                }

                // ------

                _debuggeeKilled = false;

                string godotExecutablePath = (string)args.executable;

                string godotProjectDir = (string)args.godotProjectDir;

                var startInfo = new GodotDebuggerStartInfo(executionType, godotExecutablePath,
                                                           processOutputListener: this, listenArgs)
                {
                    WorkingDirectory = godotProjectDir
                };

                _session.Run(startInfo, _debuggerSessionOptions);

                _debuggeeExecuting = true;
            }
        }
Beispiel #7
0
        public void ApplicationRunsWithDebuggerAndBreaks(bool embedAssemblies, string fastDevType, bool allowDeltaInstall, string username)
        {
            AssertCommercialBuild();
            AssertHasDevices();

            var           path       = Path.Combine("temp", TestName);
            int           userId     = GetUserId(username);
            List <string> parameters = new List <string> ();

            if (userId >= 0)
            {
                parameters.Add($"AndroidDeviceUserId={userId}");
            }
            if (SwitchUser(username))
            {
                WaitFor(5);
                ClickButton("", "android:id/button1", "Yes continue");
            }

            var lib = new XamarinAndroidLibraryProject {
                ProjectName = "Library1",
                Sources     =
                {
                    new BuildItem.Source("Foo.cs")
                    {
                        TextContent = () =>
                                      @"public class Foo
{
	public Foo ()
	{
	}
}"
                    },
                },
            };

            var app = new XamarinFormsAndroidApplicationProject {
                ProjectName               = "App",
                IsRelease                 = false,
                EmbedAssembliesIntoApk    = embedAssemblies,
                AndroidFastDeploymentType = fastDevType
            };

            app.MainPage = app.MainPage.Replace("InitializeComponent ();", "InitializeComponent (); new Foo ();");
            app.AddReference(lib);
            app.SetAndroidSupportedAbis("armeabi-v7a", "x86");
            app.SetProperty(KnownProperties._AndroidAllowDeltaInstall, allowDeltaInstall.ToString());
            app.SetDefaultTargetDevice();
            using (var libBuilder = CreateDllBuilder(Path.Combine(path, lib.ProjectName)))
                using (var appBuilder = CreateApkBuilder(Path.Combine(path, app.ProjectName))) {
                    Assert.True(libBuilder.Build(lib), "Library should have built.");

                    SetTargetFrameworkAndManifest(app, appBuilder);
                    Assert.True(appBuilder.Install(app, parameters: parameters.ToArray()), "App should have installed.");

                    if (!embedAssemblies)
                    {
                        // Check that we deployed .pdb files
                        StringAssertEx.ContainsRegex($@"NotifySync CopyFile.+{app.ProjectName}\.pdb", appBuilder.LastBuildOutput,
                                                     $"{app.ProjectName}.pdb should be deployed!");
                        StringAssertEx.ContainsRegex($@"NotifySync CopyFile.+{lib.ProjectName}\.pdb", appBuilder.LastBuildOutput,
                                                     $"{lib.ProjectName}.pdb should be deployed!");
                    }

                    int breakcountHitCount      = 0;
                    ManualResetEvent resetEvent = new ManualResetEvent(false);
                    var sw = new Stopwatch();
                    // setup the debugger
                    var session = new SoftDebuggerSession();
                    session.Breakpoints = new BreakpointStore {
                        { Path.Combine(Root, appBuilder.ProjectDirectory, "MainActivity.cs"), 20 },
                        { Path.Combine(Root, appBuilder.ProjectDirectory, "MainPage.xaml.cs"), 14 },
                        { Path.Combine(Root, appBuilder.ProjectDirectory, "MainPage.xaml.cs"), 19 },
                        { Path.Combine(Root, appBuilder.ProjectDirectory, "App.xaml.cs"), 12 },
                        { Path.Combine(Root, libBuilder.ProjectDirectory, "Foo.cs"), 4 },
                    };
                    session.TargetHitBreakpoint += (sender, e) => {
                        TestContext.WriteLine($"BREAK {e.Type}, {e.Backtrace.GetFrame (0)}");
                        breakcountHitCount++;
                        session.Continue();
                    };
                    var rnd  = new Random();
                    int port = rnd.Next(10000, 20000);
                    TestContext.Out.WriteLine($"{port}");
                    var args = new SoftDebuggerConnectArgs("", IPAddress.Loopback, port)
                    {
                        MaxConnectionAttempts = 10,
                    };
                    var startInfo = new SoftDebuggerStartInfo(args)
                    {
                        WorkingDirectory = Path.Combine(appBuilder.ProjectDirectory, app.IntermediateOutputPath, "android", "assets"),
                    };
                    var options = new DebuggerSessionOptions()
                    {
                        EvaluationOptions = EvaluationOptions.DefaultOptions,
                    };
                    options.EvaluationOptions.UseExternalTypeResolver = true;
                    ClearAdbLogcat();
                    appBuilder.BuildLogFile = "run.log";

                    parameters.Add($"AndroidSdbTargetPort={port}");
                    parameters.Add($"AndroidSdbHostPort={port}");
                    parameters.Add("AndroidAttachDebugger=True");

                    Assert.True(appBuilder.RunTarget(app, "_Run", doNotCleanupOnUpdate: true,
                                                     parameters: parameters.ToArray()), "Project should have run.");

                    Assert.IsTrue(WaitForDebuggerToStart(Path.Combine(Root, appBuilder.ProjectDirectory, "logcat.log")), "Activity should have started");
                    // we need to give a bit of time for the debug server to start up.
                    WaitFor(2000);
                    session.LogWriter    += (isStderr, text) => { Console.WriteLine(text); };
                    session.OutputWriter += (isStderr, text) => { Console.WriteLine(text); };
                    session.DebugWriter  += (level, category, message) => { Console.WriteLine(message); };
                    session.Run(startInfo, options);
                    WaitFor(TimeSpan.FromSeconds(30), () => session.IsConnected);
                    Assert.True(session.IsConnected, "Debugger should have connected but it did not.");
                    // we need to wait here for a while to allow the breakpoints to hit
                    // but we need to timeout
                    TimeSpan timeout  = TimeSpan.FromSeconds(60);
                    int      expected = 4;
                    while (session.IsConnected && breakcountHitCount < 3 && timeout >= TimeSpan.Zero)
                    {
                        Thread.Sleep(10);
                        timeout = timeout.Subtract(TimeSpan.FromMilliseconds(10));
                    }
                    WaitFor(2000);
                    Assert.AreEqual(expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
                    breakcountHitCount = 0;
                    ClearAdbLogcat();
                    ClickButton(app.PackageName, "myXFButton", "CLICK ME");
                    while (session.IsConnected && breakcountHitCount < 1 && timeout >= TimeSpan.Zero)
                    {
                        Thread.Sleep(10);
                        timeout = timeout.Subtract(TimeSpan.FromMilliseconds(10));
                    }
                    expected = 1;
                    Assert.AreEqual(expected, breakcountHitCount, $"Should have hit {expected} breakpoints. Only hit {breakcountHitCount}");
                    appBuilder.BuildLogFile = "uninstall.log";
                    Assert.True(appBuilder.Uninstall(app), "Project should have uninstalled.");
                    session.Exit();
                }
        }