From fb81f6a1dd57ab8544de1978d6e36ffa63d9e506 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 20 Mar 2019 13:02:00 +0000 Subject: [PATCH 1/6] [Xamarin.Android.Build.Tests] Add support for testing the UI This commit adds support for testing that the app launches on device AND can be interacted with. It does this by making use of a few features of `adb`. First is `adb input`. This allow us to send taps and clicks to the device. It simulates user input. So we can use this to say click a button. Next up is the `uiautomator` this allows use to get the complete ui layout for the active activity. Using exec-out uiautomator dump /dev/tty we can send this data to the console. As it is xml we can then parse it looking for our ui element we want. We can then get the bounds of the control and pass that information to `adb input tap`. This commit also adds support for testing that the timezone set on a device is correctly picked up by mono. And finally we add support for testing that the debugger can actaully connect to the application. These tests are only run if the commerial parts of the product are available. Seeing as all of these new tests REQUIRE the emulator or device they have been split out into a new `MSBuildDeviceIntegration` project. This is built as part of the main solution. However it is run as part of the `make run-apk-tests` call. This is so we can be sure the emulator is running. In future any MSBuild related tests that REQUIRE a device should be placed in this new project. --- .gitmodules | 6 + Configuration.props | 6 + Xamarin.Android.Build.Tasks.sln | 45 ++++ Xamarin.Android.sln | 44 ++++ build-tools/scripts/RunTests.targets | 2 - external/debugger-libs | 1 + external/nrefactory | 1 + .../Utilities/BaseTest.cs | 8 +- .../Utilities/DeviceTest.cs | 146 +++++++++++++ ...marin.Android.Build.Tests.Shared.projitems | 1 + .../Xamarin.Android.Build.Tests.csproj | 2 + .../Common/ProjectBuilder.cs | 10 + .../Resources/Forms/MainPage.xaml | 3 +- .../Resources/Forms/MainPage.xaml.cs | 5 + .../Xamarin.Android.Common.targets | 13 ++ .../MSBuildDeviceIntegration.csproj | 84 ++++++++ .../MSBuildDeviceIntegration.targets | 24 +++ .../Tests/DebuggingTest.cs | 199 ++++++++++++++++++ .../Tests/DeploymentTest.cs | 105 +++++++++ .../MSBuildDeviceIntegration/packages.config | 5 + tests/RunApkTests.targets | 36 ++++ 21 files changed, 741 insertions(+), 5 deletions(-) create mode 160000 external/debugger-libs create mode 160000 external/nrefactory create mode 100644 src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs create mode 100644 tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj create mode 100644 tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.targets create mode 100644 tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs create mode 100644 tests/MSBuildDeviceIntegration/Tests/DeploymentTest.cs create mode 100644 tests/MSBuildDeviceIntegration/packages.config diff --git a/.gitmodules b/.gitmodules index 13463abc5dd..ec3438d9f69 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ +[submodule "external/debugger-libs"] + path = external/debugger-libs + url = git://github.com/mono/debugger-libs [submodule "external/dlfcn-win32"] path = external/dlfcn-win32 url = https://github.com/dlfcn-win32/dlfcn-win32.git @@ -30,6 +33,9 @@ path = external/mxe url = https://github.com/xamarin/mxe.git branch = xamarin +[submodule "external/nrefactory"] + path = external/nrefactory + url = git://github.com/icsharpcode/NRefactory.git [submodule "external/opentk"] path = external/opentk url = https://github.com/mono/opentk.git diff --git a/Configuration.props b/Configuration.props index a92d4f50b82..36c2cb1e652 100644 --- a/Configuration.props +++ b/Configuration.props @@ -176,5 +176,11 @@ $(ManagedRuntime) $(ManagedRuntimeArgs) "$(MSBuildThisFileDirectory)bin\Build$(Configuration)\remap-assembly-ref.exe" + + + <_Runtime Condition=" '$(HostOS)' != 'Windows' ">$(ManagedRuntime) + <_NUnit>$(_Runtime) packages\NUnit.ConsoleRunner.3.9.0\tools\nunit3-console.exe + + diff --git a/Xamarin.Android.Build.Tasks.sln b/Xamarin.Android.Build.Tasks.sln index e778058faa7..bb2e95c7ab5 100644 --- a/Xamarin.Android.Build.Tasks.sln +++ b/Xamarin.Android.Build.Tasks.sln @@ -11,6 +11,20 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Xamarin.Android.Build.Tests 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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{385E71CC-BAE5-488B-805E-ACAE55F01DF5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuildDeviceIntegration", "tests\MSBuildDeviceIntegration\MSBuildDeviceIntegration.csproj", "{16DB2680-399B-4111-AA26-6CDBBFA334D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "external\nrefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.CSharp", "external\nrefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj", "{53DCA265-3C3C-42F9-B647-F72BA678122B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Debugging", "external\debugger-libs\Mono.Debugging\Mono.Debugging.csproj", "{90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Debugger.Soft", "external\debugger-libs\Mono.Debugger.Soft\Mono.Debugger.Soft.csproj", "{372E8E3E-29D5-4B4D-88A2-4711CD628C4E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Debugging.Soft", "external\debugger-libs\Mono.Debugging.Soft\Mono.Debugging.Soft.csproj", "{DE40756E-57F6-4AF2-B155-55E3A88CCED8}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Xamarin.Android.NamingCustomAttributes\Xamarin.Android.NamingCustomAttributes.projitems*{3f1f2f50-af1a-4a5a-bedb-193372f068d7}*SharedItemsImports = 4 @@ -34,6 +48,30 @@ Global {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 + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Release|Any CPU.Build.0 = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.Build.0 = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|Any CPU.Build.0 = Release|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Release|Any CPU.Build.0 = Release|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Release|Any CPU.Build.0 = Release|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -41,4 +79,11 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F32556C5-6FD4-4F1D-884A-DEDF2EE865F6} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} = {385E71CC-BAE5-488B-805E-ACAE55F01DF5} + {53DCA265-3C3C-42F9-B647-F72BA678122B} = {385E71CC-BAE5-488B-805E-ACAE55F01DF5} + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2} = {385E71CC-BAE5-488B-805E-ACAE55F01DF5} + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E} = {385E71CC-BAE5-488B-805E-ACAE55F01DF5} + {DE40756E-57F6-4AF2-B155-55E3A88CCED8} = {385E71CC-BAE5-488B-805E-ACAE55F01DF5} + EndGlobalSection EndGlobal diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln index 3389d812e39..6ad4ebd033a 100644 --- a/Xamarin.Android.sln +++ b/Xamarin.Android.sln @@ -133,6 +133,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "create-pkg", "build-tools\c EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vswhere", "tools\vswhere\vswhere.csproj", "{DBDC804F-8406-4F5E-83C6-720CB0CB6C6F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuildDeviceIntegration", "tests\MSBuildDeviceIntegration\MSBuildDeviceIntegration.csproj", "{16DB2680-399B-4111-AA26-6CDBBFA334D8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{05C3B1D6-A4CE-4534-A9E4-E9117591ADF7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "external\nrefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.CSharp", "external\nrefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj", "{53DCA265-3C3C-42F9-B647-F72BA678122B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Debugger.Soft", "external\debugger-libs\Mono.Debugger.Soft\Mono.Debugger.Soft.csproj", "{372E8E3E-29D5-4B4D-88A2-4711CD628C4E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Debugging", "external\debugger-libs\Mono.Debugging\Mono.Debugging.csproj", "{90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Debugging.Soft", "external\debugger-libs\Mono.Debugging.Soft\Mono.Debugging.Soft.csproj", "{DE40756E-57F6-4AF2-B155-55E3A88CCED8}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Xamarin.Android.NamingCustomAttributes\Xamarin.Android.NamingCustomAttributes.projitems*{3f1f2f50-af1a-4a5a-bedb-193372f068d7}*SharedItemsImports = 4 @@ -383,6 +397,30 @@ Global {DBDC804F-8406-4F5E-83C6-720CB0CB6C6F}.Debug|AnyCPU.Build.0 = Debug|Any CPU {DBDC804F-8406-4F5E-83C6-720CB0CB6C6F}.Release|AnyCPU.ActiveCfg = Release|Any CPU {DBDC804F-8406-4F5E-83C6-720CB0CB6C6F}.Release|AnyCPU.Build.0 = Release|Any CPU + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {16DB2680-399B-4111-AA26-6CDBBFA334D8}.Release|AnyCPU.Build.0 = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|AnyCPU.Build.0 = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|AnyCPU.Build.0 = Release|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E}.Release|AnyCPU.Build.0 = Release|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2}.Release|AnyCPU.Build.0 = Release|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {DE40756E-57F6-4AF2-B155-55E3A88CCED8}.Release|AnyCPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -446,6 +484,12 @@ Global {0C31DE30-F9DF-4312-BFFE-DCAD558CCF08} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {A0AEF446-3368-4591-9DE6-BC3B2B33337D} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {DBDC804F-8406-4F5E-83C6-720CB0CB6C6F} = {864062D3-A415-4A6F-9324-5820237BA058} + {16DB2680-399B-4111-AA26-6CDBBFA334D8} = {CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483} + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} = {05C3B1D6-A4CE-4534-A9E4-E9117591ADF7} + {53DCA265-3C3C-42F9-B647-F72BA678122B} = {05C3B1D6-A4CE-4534-A9E4-E9117591ADF7} + {372E8E3E-29D5-4B4D-88A2-4711CD628C4E} = {05C3B1D6-A4CE-4534-A9E4-E9117591ADF7} + {90C99ADB-7D4B-4EB4-98C2-40BD1B14C7D2} = {05C3B1D6-A4CE-4534-A9E4-E9117591ADF7} + {DE40756E-57F6-4AF2-B155-55E3A88CCED8} = {05C3B1D6-A4CE-4534-A9E4-E9117591ADF7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {53A1F287-EFB2-4D97-A4BB-4A5E145613F6} diff --git a/build-tools/scripts/RunTests.targets b/build-tools/scripts/RunTests.targets index 66c352041c3..2fa51ee5c48 100644 --- a/build-tools/scripts/RunTests.targets +++ b/build-tools/scripts/RunTests.targets @@ -8,8 +8,6 @@ - <_Runtime Condition=" '$(HostOS)' != 'Windows' ">$(ManagedRuntime) $(ManagedRuntimeArgs) - <_NUnit>$(_Runtime) packages\NUnit.ConsoleRunner.3.9.0\tools\nunit3-console.exe <_Test Condition=" '$(TEST)' != '' ">--test="$(TEST)" <_XABuild>$(_TopDir)\bin\$(Configuration)\bin\xabuild <_XABinLogPrefix>/v:normal /binaryLogger:"$(MSBuildThisFileDirectory)\..\..\bin\Test$(Configuration)\msbuild diff --git a/external/debugger-libs b/external/debugger-libs new file mode 160000 index 00000000000..b45303f650e --- /dev/null +++ b/external/debugger-libs @@ -0,0 +1 @@ +Subproject commit b45303f650e827b9364fbcc95a7bcb5e2e99f3cc diff --git a/external/nrefactory b/external/nrefactory new file mode 160000 index 00000000000..0607a4ad96e --- /dev/null +++ b/external/nrefactory @@ -0,0 +1 @@ +Subproject commit 0607a4ad96ebdd16817e47dcae85b1cfcb5b5bf5 diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs index 1c5f4d5e116..30130fe589c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs @@ -8,7 +8,11 @@ using System.Linq; using System.Security.Cryptography; using System.Text; +using System.Text.RegularExpressions; using System.Threading; +using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; using Xamarin.ProjectTools; using XABuildPaths = Xamarin.Android.Build.Paths; @@ -154,13 +158,13 @@ public static string AndroidNdkPath { } } - protected void WaitFor(int milliseconds) + protected static void WaitFor(int milliseconds) { var pause = new ManualResetEvent(false); pause.WaitOne(milliseconds); } - protected static string RunAdbCommand (string command, bool ignoreErrors = true) + protected static string RunAdbCommand (string command, bool ignoreErrors = true, int timeout = 30) { string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".exe" : ""; string adb = Path.Combine (AndroidSdkPath, "platform-tools", "adb" + ext); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs new file mode 100644 index 00000000000..96ef7ad5d50 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs @@ -0,0 +1,146 @@ +using NUnit.Framework; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; +using Xamarin.ProjectTools; +using XABuildPaths = Xamarin.Android.Build.Paths; + +namespace Xamarin.Android.Build.Tests +{ + public class DeviceTest: BaseTest + { + protected static void RunAdbInput (string command, params object [] args) + { + RunAdbCommand ($"shell {command} {string.Join (" ", args)}"); + } + + protected static string ClearAdbLogcat () + { + return RunAdbCommand ("logcat -c"); + } + + protected static string ClearDebugProperty () + { + return RunAdbCommand ("shell setprop debug.mono.extra \"\""); + } + + protected static void AdbStartActivity (string activity) + { + RunAdbCommand ($"shell am start -S -n \"{activity}\""); + } + + protected void WaitFor (TimeSpan timeSpan, Func func) + { + var pause = new ManualResetEvent (false); + TimeSpan total = timeSpan; + TimeSpan interval = TimeSpan.FromMilliseconds (10); + while (total.TotalMilliseconds > 0) { + pause.WaitOne (interval); + total = total.Subtract (interval); + if (func ()) { + break; + } + } + } + + protected static bool MonitorAdbLogcat (Func action, int timeout = 10) + { + string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".exe" : ""; + string adb = Path.Combine (AndroidSdkPath, "platform-tools", "adb" + ext); + var info = new ProcessStartInfo (adb, "logcat") { + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + }; + using (var proc = Process.Start (info)) { + var sw = new Stopwatch (); + try { + TimeSpan time = TimeSpan.FromSeconds (timeout); + while (time.TotalMilliseconds > 0) { + sw.Start (); + if (action (proc.StandardOutput.ReadLine ())) + return true; + time = time.Subtract (TimeSpan.FromMilliseconds (sw.ElapsedMilliseconds)); + sw.Reset (); + } + } finally { + sw.Stop (); + proc.Kill (); + proc.WaitForExit (); + } + return false; + } + } + + protected static bool WaitForDebuggerToStart (out string output, int timeout = 60) + { + var sb = new StringBuilder (); + bool result = MonitorAdbLogcat ((line) => { + sb.AppendLine (line); + return line.IndexOf ("monodroid-debug: Trying to initialize the debugger with options", StringComparison.OrdinalIgnoreCase) > 0; + }, timeout: timeout); + output = sb.ToString (); + return result; + } + + protected static bool WaitForActivityToStart (string activityNamespace, string activityName, out string output, int timeout = 60) + { + var sb = new StringBuilder (); + bool result = MonitorAdbLogcat ((line) => { + sb.AppendLine (line); + return line.IndexOf ("ActivityManager: Displayed", StringComparison.OrdinalIgnoreCase) > 0 && line.Contains (activityNamespace) && line.Contains (activityName); + }, timeout: timeout); + output = sb.ToString (); + return result; + } + + protected static (int x, int y, int w, int h) GetControlBounds (string packageName, string uiElement, string text) + { + var regex = new Regex (@"[(0-9)]\d*", RegexOptions.Compiled); + var result = (x: 0, y: 0, w: 0, h: 0); + var ui = RunAdbCommand ("exec-out uiautomator dump /dev/tty"); + while (ui.Contains ("ERROR:")) { + ui = RunAdbCommand ("exec-out uiautomator dump /dev/tty"); + WaitFor (1); + } + ui = ui.Replace ("UI hierchary dumped to: /dev/tty", string.Empty).Trim (); + try { + var uiDoc = XDocument.Parse (ui); + var node = uiDoc.XPathSelectElement ($"//node[contains(@resource-id,'{uiElement}')]"); + if (node == null) + node = uiDoc.XPathSelectElement ($"//node[contains(@content-desc,'{uiElement}')]"); + if (node == null) + node = uiDoc.XPathSelectElement ($"//node[contains(@text,'{text}')]"); + if (node == null) + return result; + var bounds = node.Attribute ("bounds"); + var matches = regex.Matches (bounds.Value); + int.TryParse (matches [0].Value, out int x); + int.TryParse (matches [1].Value, out int y); + int.TryParse (matches [2].Value, out int w); + int.TryParse (matches [3].Value, out int h); + return (x: x, y: y, w: w, h: h); + } catch (Exception ex) { + // Ignore any error and return and empty + throw new InvalidOperationException ($"uiautomator returned invalid xml {ui}", ex); + } + } + + protected static void ClickButton (string packageName, string buttonName, string buttonText) + { + var bounds = GetControlBounds (packageName, buttonName, buttonText); + RunAdbInput ("input tap", bounds.x + ((bounds.w - bounds.x) / 2), bounds.y + ((bounds.h - bounds.y) / 2)); + } + } +} \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems index 948b6bef0d8..735508b6090 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems @@ -19,6 +19,7 @@ + diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj index e71eb1f8c31..ca5f90ea85d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj @@ -58,6 +58,8 @@ ..\..\..\..\packages\Xamarin.Build.AsyncTask.0.3.4\lib\netstandard2.0\Xamarin.Build.AsyncTask.dll + + diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs index b2ae958df8b..8be46e30b26 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/ProjectBuilder.cs @@ -82,6 +82,16 @@ public bool Build (XamarinProject project, bool doNotCleanupOnUpdate = false, st return result; } + public bool Install (XamarinProject project, bool doNotCleanupOnUpdate = false) + { + return RunTarget (project, "Install", doNotCleanupOnUpdate); + } + + public bool Uninstall (XamarinProject project, bool doNotCleanupOnUpdate = false) + { + return RunTarget (project, "Uninstall", doNotCleanupOnUpdate); + } + public bool Restore (XamarinProject project, bool doNotCleanupOnUpdate = false) { return RunTarget (project, "Restore", doNotCleanupOnUpdate); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainPage.xaml b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainPage.xaml index eedb53ff2bc..2a75ede1a6c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainPage.xaml +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainPage.xaml @@ -1,4 +1,4 @@ - + @@ -6,5 +6,6 @@