diff --git a/Documentation/DevelopmentTips.md b/Documentation/DevelopmentTips.md index 4c20dafff0a..05557888972 100644 --- a/Documentation/DevelopmentTips.md +++ b/Documentation/DevelopmentTips.md @@ -148,15 +148,15 @@ make variable: ## Running Individual `.apk` Projects See also the [`tests/RunApkTests.targets`](../tests/RunApkTests.targets) and -[`build-tools/scripts/UnitTestApks.targets`](../build-tools/scripts/UnitTestApks.targets) +[`build-tools/scripts/TestApks.targets`](../build-tools/scripts/TestApks.targets) files. All `.apk`-based unit test projects provide the following targets: -* `DeployUnitTestApks`: Installs the associated `.apk` to an Android device. -* `UndeployUnitTestApks`: Uninstalls the associated `.apk` from an Android device. -* `RunUnitTestApks`: Executes the unit tests contained within a `.apk`. - Must be executed *after* the `DeployUnitTestApks` target. +* `DeployTestApks`: Installs the associated `.apk` to an Android device. +* `UndeployTestApks`: Uninstalls the associated `.apk` from an Android device. +* `RunTestApks`: Executes the unit tests contained within a `.apk`. + Must be executed *after* the `DeployTestApks` target. To run an individual `.apk`-based test project, a package must be built, using the `SignAndroidPackage` target, installed, and executed. @@ -164,23 +164,23 @@ To run an individual `.apk`-based test project, a package must be built, using t For example: $ tools/scripts/xabuild /t:SignAndroidPackage tests/locales/Xamarin.Android.Locale-Tests/Xamarin.Android.Locale-Tests.csproj - $ tools/scripts/xabuild /t:DeployUnitTestApks tests/locales/Xamarin.Android.Locale-Tests/Xamarin.Android.Locale-Tests.csproj - $ tools/scripts/xabuild /t:RunUnitTestApks tests/locales/Xamarin.Android.Locale-Tests/Xamarin.Android.Locale-Tests.csproj + $ tools/scripts/xabuild /t:DeployTestApks tests/locales/Xamarin.Android.Locale-Tests/Xamarin.Android.Locale-Tests.csproj + $ tools/scripts/xabuild /t:RunTestApks tests/locales/Xamarin.Android.Locale-Tests/Xamarin.Android.Locale-Tests.csproj ### Running A Single Test Fixture A single NUnit *Test Fixture* -- a class with the `[TestFixture]` custom attribute -- may be executed instead of executing *all* test fixtures. -The `RunUnitTestApks` target accepts a `TestFixture` MSBuild property +The `RunTestApks` target accepts a `TestFixture` MSBuild property to specify the test fixture class to execute: - $ tools/scripts/xabuild /t:RunUnitTestApks \ + $ tools/scripts/xabuild /t:RunTestApks \ /p:TestFixture=Xamarin.Android.LocaleTests.SatelliteAssemblyTests \ tests/locales/Xamarin.Android.Locale-Tests/Xamarin.Android.Locale-Tests.csproj If using `Xamarin.Android.NUnitLite` for projects outside the `xamarin-android` -repository, such as NUnit tests for a custom app, the `RunUnitTestApks` target +repository, such as NUnit tests for a custom app, the `RunTestApks` target will not exist. In such scenarios, the [`adb shell am`][adb-shell-am] `instrument` command can be used instead. It follows the format: diff --git a/Xamarin.Android-Tests.sln b/Xamarin.Android-Tests.sln index 8aa0169dced..0c5521095a8 100644 --- a/Xamarin.Android-Tests.sln +++ b/Xamarin.Android-Tests.sln @@ -45,6 +45,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.BindingReso EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.BindingResolveImports-Tests", "tests\ResolveImports\Xamarin.Android.BindingResolveImports-Tests\Xamarin.Android.BindingResolveImports-Tests.csproj", "{B297008B-C313-455E-B230-E119589D2D79}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Xamarin.Forms.Performance.Integration", "tests\Xamarin.Forms-Performance-Integration\Xamarin.Forms.Performance.Integration.shproj", "{195BE9C2-1F91-40DC-BD6D-DE860BF083FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Performance.Integration.Droid", "tests\Xamarin.Forms-Performance-Integration\Droid\Xamarin.Forms.Performance.Integration.Droid.csproj", "{576312CC-83FF-48B1-A473-488CDC7121AD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -115,6 +119,10 @@ Global {B297008B-C313-455E-B230-E119589D2D79}.Debug|Any CPU.Build.0 = Debug|Any CPU {B297008B-C313-455E-B230-E119589D2D79}.Release|Any CPU.ActiveCfg = Release|Any CPU {B297008B-C313-455E-B230-E119589D2D79}.Release|Any CPU.Build.0 = Release|Any CPU + {576312CC-83FF-48B1-A473-488CDC7121AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {576312CC-83FF-48B1-A473-488CDC7121AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {576312CC-83FF-48B1-A473-488CDC7121AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {576312CC-83FF-48B1-A473-488CDC7121AD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {2305B00D-DE81-4744-B0DA-357835CAFE5A} = {43A4FB09-279A-4138-8027-EC1E1CED2E8A} diff --git a/build-tools/scripts/UnitTestApks.targets b/build-tools/scripts/TestApks.targets similarity index 83% rename from build-tools/scripts/UnitTestApks.targets rename to build-tools/scripts/TestApks.targets index 0bc00ec9c33..a5ecf7d2d04 100644 --- a/build-tools/scripts/UnitTestApks.targets +++ b/build-tools/scripts/TestApks.targets @@ -4,13 +4,14 @@ + - <_TestImageName>XamarinAndroidUnitTestRunner + <_TestImageName>XamarinAndroidTestRunner <_AdbEmulatorPort>5570 @@ -107,54 +108,62 @@ - + - + - + + + <_LogcatFilename>logcat-$(Configuration)$(_AotName).txt + ApplicationPackageName="%(TestApk.Package)" + ResultsFilename="%(TestApk.ResultsPath)" + DefinitionsFilename="%(TestApk.TimingDefinitionsFilename)" + AddResults="true" + Activity="%(TestApk.Activity)" /> diff --git a/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ProcessLogcatTiming.cs b/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ProcessLogcatTiming.cs index 195e9238b23..d40e34aa357 100644 --- a/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ProcessLogcatTiming.cs +++ b/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ProcessLogcatTiming.cs @@ -10,13 +10,16 @@ namespace Xamarin.Android.BuildTools.PrepTasks { public class ProcessLogcatTiming : ProcessPlotInput { + public string Activity { get; set; } + public override bool Execute () { LoadDefinitions (); using (var reader = new StreamReader (InputFilename)) { string line; int pid = -1; - var procStartRegex = new Regex ($@"^(?\d+-\d+\s+[\d:\.]+)\s+.*ActivityManager: Start proc.*for added application {ApplicationPackageName}: pid=(?\d+)"); + var procIdentification = string.IsNullOrEmpty (Activity) ? $"added application {ApplicationPackageName}" : $"activity {Activity}"; + var procStartRegex = new Regex ($@"^(?\d+-\d+\s+[\d:\.]+)\s+.*ActivityManager: Start proc.*for {procIdentification}: pid=(?\d+)"); Regex timingRegex = null; DateTime start = DateTime.Now; DateTime last = start; diff --git a/src/Mono.Android/Test/Mono.Android-Tests.projitems b/src/Mono.Android/Test/Mono.Android-Tests.projitems index 65b62e182ce..13bc4d032f6 100644 --- a/src/Mono.Android/Test/Mono.Android-Tests.projitems +++ b/src/Mono.Android/Test/Mono.Android-Tests.projitems @@ -8,14 +8,14 @@ <_MonoAndroidTestApkSizesInput>apk-sizes-$(_MonoAndroidTestPackage)-$(Configuration)$(_AotName).txt - + $(_MonoAndroidTestPackage) xamarin.android.runtimetests.TestInstrumentation $(_MonoAndroidTestResultsPath) $(MSBuildThisFileDirectory)timing-definitions-$(Configuration)$(_AotName).txt - + - + diff --git a/src/Mono.Android/Test/Mono.Android-Tests.targets b/src/Mono.Android/Test/Mono.Android-Tests.targets index 00d0be5a551..d1068ece991 100644 --- a/src/Mono.Android/Test/Mono.Android-Tests.targets +++ b/src/Mono.Android/Test/Mono.Android-Tests.targets @@ -8,7 +8,7 @@ - + - + + diff --git a/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CreateAndroidEmulator.cs b/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CreateAndroidEmulator.cs index 04e9453de95..463ba533070 100644 --- a/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CreateAndroidEmulator.cs +++ b/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CreateAndroidEmulator.cs @@ -21,7 +21,7 @@ public class CreateAndroidEmulator : Task public string TargetId {get; set;} - public string ImageName {get; set;} = "XamarinAndroidUnitTestRunner"; + public string ImageName {get; set;} = "XamarinAndroidTestRunner"; public override bool Execute () { diff --git a/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/RunUITests.cs b/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/RunUITests.cs new file mode 100644 index 00000000000..9841efb6542 --- /dev/null +++ b/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/RunUITests.cs @@ -0,0 +1,37 @@ +using Microsoft.Build.Framework; + +namespace Xamarin.Android.Tools.BootstrapTasks +{ + public class RunUITests : Adb + { + public string AdbTarget { get; set; } + public string AdbOptions { get; set; } + + [Required] + public string Activity { get; set; } + + protected override bool LogTaskMessages { + get { return false; } + } + + public override bool Execute () + { + Log.LogMessage (MessageImportance.Low, $"Task {nameof (RunUITests)}"); + Log.LogMessage (MessageImportance.Low, $" {nameof (AdbTarget)}: {AdbTarget}"); + Log.LogMessage (MessageImportance.Low, $" {nameof (AdbOptions)}: {AdbOptions}"); + Log.LogMessage (MessageImportance.Low, $" {nameof (Activity)}: {Activity}"); + + base.Execute (); + + Log.LogMessage(MessageImportance.Low, $" going to wait for 15 seconds"); + System.Threading.Thread.Sleep (15000); + + return !Log.HasLoggedErrors; + } + + protected override string GenerateCommandLineCommands () + { + return $"{AdbTarget} {AdbOptions} shell am start -n \"{Activity}\""; + } + } +} diff --git a/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/StartAndroidEmulator.cs b/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/StartAndroidEmulator.cs index 2689ef45199..3fd4142b8bf 100644 --- a/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/StartAndroidEmulator.cs +++ b/src/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/StartAndroidEmulator.cs @@ -19,7 +19,7 @@ public class StartAndroidEmulator : Task public string AndroidSdkHome {get; set;} public string Port {get; set;} - public string ImageName {get; set;} = "XamarinAndroidUnitTestRunner"; + public string ImageName {get; set;} = "XamarinAndroidTestRunner"; public string ToolPath {get; set;} public string ToolExe {get; set;} diff --git a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.projitems b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.projitems index 22e17d970c1..ea258a6242c 100644 --- a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.projitems +++ b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.projitems @@ -1,11 +1,11 @@ - + Xamarin.Android.JcwGen_Tests xamarin.android.jcwgentests.TestInstrumentation $(MSBuildThisFileDirectory)..\..\..\TestResult-Xamarin.Android.JcwGen_Tests.xml $(MSBuildThisFileDirectory)..\..\..\build-tools\scripts\TimingDefinitions.txt - + diff --git a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.targets b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.targets index 97813d56aea..616a3a8e166 100644 --- a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.targets +++ b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.targets @@ -1,7 +1,7 @@ - + -Aot - + + AcquireAndroidTarget; - UndeployUnitTestApks; - DeployUnitTestApks; - RunUnitTestApks; + UndeployTestApks; + DeployTestApks; + RunTestApks; ReleaseAndroidTarget diff --git a/tests/Xamarin.Forms-Performance-Integration/App.xaml b/tests/Xamarin.Forms-Performance-Integration/App.xaml new file mode 100644 index 00000000000..15677dfdfe9 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/App.xaml @@ -0,0 +1,18 @@ + + + + + #2196F3 + #1976D2 + #96d1ff + #FAFAFA + #C0C0C0 + #4d4d4d + #999999 + + + + diff --git a/tests/Xamarin.Forms-Performance-Integration/App.xaml.cs b/tests/Xamarin.Forms-Performance-Integration/App.xaml.cs new file mode 100644 index 00000000000..d90dd6d63bc --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/App.xaml.cs @@ -0,0 +1,27 @@ +using System; + +using Xamarin.Forms; + +namespace Xamarin.Forms.Performance.Integration +{ + public partial class App : Application + { + public static bool UseMockDataStore = true; + public static string BackendUrl = "https://localhost:5000"; + + public App () + { + InitializeComponent (); + + if (UseMockDataStore) + DependencyService.Register (); + else + DependencyService.Register (); + + if (Device.RuntimePlatform == Device.iOS) + MainPage = new MainPage (); + else + MainPage = new NavigationPage (new MainPage ()); + } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Assets/AboutAssets.txt b/tests/Xamarin.Forms-Performance-Integration/Droid/Assets/AboutAssets.txt new file mode 100644 index 00000000000..a9b0638eb1b --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with your package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/MainActivity.cs b/tests/Xamarin.Forms-Performance-Integration/Droid/MainActivity.cs new file mode 100644 index 00000000000..6397bf0f32d --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/MainActivity.cs @@ -0,0 +1,71 @@ +using System; + +using Android.App; +using Android.Content; +using Android.Content.PM; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using Android.OS; + +namespace Xamarin.Forms.Performance.Integration.Droid +{ + [Activity (Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] + public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity + { + bool firstOnCreate = true; + bool firstOnStart = true; + bool firstOnResume = true; + + protected override void OnCreate (Bundle bundle) + { + if (firstOnCreate) + Console.WriteLine ("startup-timing: OnCreate reached"); + + TabLayoutResource = Resource.Layout.Tabbar; + ToolbarResource = Resource.Layout.Toolbar; + + base.OnCreate (bundle); + + global::Xamarin.Forms.Forms.Init (this, bundle); + + LoadApplication (new App ()); + + if (firstOnCreate) { + Console.WriteLine ("startup-timing: OnCreate end reached"); + firstOnCreate = false; + } + } + + protected override void OnActivityResult (int requestCode, Result resultCode, Intent data) + { + base.OnActivityResult (requestCode, resultCode, data); + } + + protected override void OnStart () + { + if (firstOnStart) + Console.WriteLine ("startup-timing: OnStart reached"); + + base.OnStart (); + + if (firstOnStart) { + Console.WriteLine ("startup-timing: OnStart end reached"); + firstOnStart = false; + } + } + + protected override void OnResume () + { + if (firstOnResume) + Console.WriteLine ("startup-timing: OnResume reached"); + + base.OnResume (); + + if (firstOnResume) { + Console.WriteLine ("startup-timing: OnResume end reached"); + firstOnResume = false; + } + } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Properties/AndroidManifest.xml b/tests/Xamarin.Forms-Performance-Integration/Droid/Properties/AndroidManifest.xml new file mode 100644 index 00000000000..e9bec0891f9 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Properties/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Properties/AssemblyInfo.cs b/tests/Xamarin.Forms-Performance-Integration/Droid/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..691e0b192ee --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using Android.App; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("Xamarin.Forms.Performance.Integration.Droid")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("${AuthorCopyright}")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.0")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/AboutResources.txt b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/AboutResources.txt new file mode 100644 index 00000000000..10f52d46021 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-hdpi/icon.png b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-hdpi/icon.png new file mode 100644 index 00000000000..964f110abb6 Binary files /dev/null and b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-hdpi/icon.png differ diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-xhdpi/icon.png b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-xhdpi/icon.png new file mode 100644 index 00000000000..3c01e60ced0 Binary files /dev/null and b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-xhdpi/icon.png differ diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-xxhdpi/icon.png b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-xxhdpi/icon.png new file mode 100644 index 00000000000..0d8c1c57dc5 Binary files /dev/null and b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable-xxhdpi/icon.png differ diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable/icon.png b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable/icon.png new file mode 100644 index 00000000000..b0ba7150f4d Binary files /dev/null and b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable/icon.png differ diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable/xamarin_logo.png b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable/xamarin_logo.png new file mode 100644 index 00000000000..b36d00ed697 Binary files /dev/null and b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/drawable/xamarin_logo.png differ diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/layout/Tabbar.axml b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/layout/Tabbar.axml new file mode 100644 index 00000000000..0bc7e9db671 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/layout/Tabbar.axml @@ -0,0 +1,2 @@ + + diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/layout/Toolbar.axml b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/layout/Toolbar.axml new file mode 100644 index 00000000000..d685cbadba2 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/layout/Toolbar.axml @@ -0,0 +1,2 @@ + + diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/values/styles.xml b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/values/styles.xml new file mode 100644 index 00000000000..7d6f9e578b4 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Resources/values/styles.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Xamarin.Forms.Performance.Integration.Droid.csproj b/tests/Xamarin.Forms-Performance-Integration/Droid/Xamarin.Forms.Performance.Integration.Droid.csproj new file mode 100644 index 00000000000..49b90451ba8 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Xamarin.Forms.Performance.Integration.Droid.csproj @@ -0,0 +1,121 @@ + + + + Debug + AnyCPU + {576312CC-83FF-48B1-A473-488CDC7121AD} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Xamarin.Forms.Performance.Integration.Droid + Xamarin.Forms.Performance.Integration.Droid + v7.1 + True + Resources\Resource.designer.cs + Resource + Properties\AndroidManifest.xml + Resources + Assets + true + + + true + full + false + ..\..\..\bin\TestDebug + DEBUG; + prompt + 4 + None + + + true + pdbonly + true + ..\..\..\bin\TestRelease + prompt + 4 + true + false + armeabi-v7a;x86 + + + + + + + + + ..\..\..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll + + + ..\..\..\packages\Xam.Plugin.Connectivity.2.3.0\lib\MonoAndroid10\Plugin.Connectivity.Abstractions.dll + + + ..\..\..\packages\Xam.Plugin.Connectivity.2.3.0\lib\MonoAndroid10\Plugin.Connectivity.dll + + + ..\..\..\packages\Xamarin.Android.Support.v4.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll + + + ..\..\..\packages\Xamarin.Android.Support.Vector.Drawable.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.Vector.Drawable.dll + + + ..\..\..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.Animated.Vector.Drawable.dll + + + ..\..\..\packages\Xamarin.Android.Support.v7.RecyclerView.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.RecyclerView.dll + + + ..\..\..\packages\Xamarin.Android.Support.v7.AppCompat.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll + + + ..\..\..\packages\Xamarin.Android.Support.Design.23.3.0\lib\MonoAndroid43\Xamarin.Android.Support.Design.dll + + + ..\..\..\packages\Xamarin.Android.Support.v7.CardView.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.CardView.dll + + + ..\..\..\packages\Xamarin.Android.Support.v7.MediaRouter.23.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.MediaRouter.dll + + + ..\..\..\packages\Xamarin.Forms.2.3.4.247\lib\MonoAndroid10\FormsViewGroup.dll + + + ..\..\..\packages\Xamarin.Forms.2.3.4.247\lib\MonoAndroid10\Xamarin.Forms.Core.dll + + + ..\..\..\packages\Xamarin.Forms.2.3.4.247\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll + + + ..\..\..\packages\Xamarin.Forms.2.3.4.247\lib\MonoAndroid10\Xamarin.Forms.Platform.dll + + + ..\..\..\packages\Xamarin.Forms.2.3.4.247\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/Xamarin.Forms.Performance.Integration.Droid.projitems b/tests/Xamarin.Forms-Performance-Integration/Droid/Xamarin.Forms.Performance.Integration.Droid.projitems new file mode 100644 index 00000000000..24015c3e338 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/Xamarin.Forms.Performance.Integration.Droid.projitems @@ -0,0 +1,11 @@ + + + + + Xamarin.Forms_Performance_Integration + Xamarin.Forms_Performance_Integration/md52b709e14dec302485bbcaeac0bd817ce.MainActivity + $(MSBuildThisFileDirectory)..\..\..\TestResult-Xamarin.Forms_Test.xml + $(MSBuildThisFileDirectory)timing-definitions.txt + + + diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/packages.config b/tests/Xamarin.Forms-Performance-Integration/Droid/packages.config new file mode 100644 index 00000000000..dcda8dbc8f6 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/packages.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Xamarin.Forms-Performance-Integration/Droid/timing-definitions.txt b/tests/Xamarin.Forms-Performance-Integration/Droid/timing-definitions.txt new file mode 100644 index 00000000000..5305a2f82ba --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Droid/timing-definitions.txt @@ -0,0 +1,14 @@ +# measure time of last monodroid-timing message appearance +last=monodroid-timing:\s+(?.*)$ + +# measure time of runtime and JNIEnv initialization end +init=monodroid-timing:\s+(?Runtime\.init: end native-to-managed.*)$ +JNI.init=monodroid-timing:\s+(?JNIEnv\.Initialize end:.*)$ + +# measure UI startup +OnCreateBegin=mono-stdout: startup-timing: (?OnCreate reached)$ +OnCreateEnd=mono-stdout: startup-timing: (?OnCreate end reached)$ +OnStartBegin=mono-stdout: startup-timing: (?OnStart reached)$ +OnStartEnd=mono-stdout: startup-timing: (?OnStart end reached)$ +OnResumeBegin=mono-stdout: startup-timing: (?OnResume reached)$ +OnResumeEnd=mono-stdout: startup-timing: (?OnResume end reached)$ diff --git a/tests/Xamarin.Forms-Performance-Integration/Models/Item.cs b/tests/Xamarin.Forms-Performance-Integration/Models/Item.cs new file mode 100644 index 00000000000..d21a2759fc9 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Models/Item.cs @@ -0,0 +1,11 @@ +using System; + +namespace Xamarin.Forms.Performance.Integration +{ + public class Item + { + public string Id { get; set; } + public string Text { get; set; } + public string Description { get; set; } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/Services/CloudDataStore.cs b/tests/Xamarin.Forms-Performance-Integration/Services/CloudDataStore.cs new file mode 100644 index 00000000000..455557d5063 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Services/CloudDataStore.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +using Newtonsoft.Json; +using Plugin.Connectivity; + +namespace Xamarin.Forms.Performance.Integration +{ + public class CloudDataStore : IDataStore + { + HttpClient client; + IEnumerable items; + + public CloudDataStore () + { + client = new HttpClient (); + client.BaseAddress = new Uri ($"{App.BackendUrl}/"); + + items = new List (); + } + + public async Task> GetItemsAsync (bool forceRefresh = false) + { + if (forceRefresh && CrossConnectivity.Current.IsConnected) { + var json = await client.GetStringAsync ($"api/item"); + items = await Task.Run (() => JsonConvert.DeserializeObject> (json)); + } + + return items; + } + + public async Task GetItemAsync (string id) + { + if (id != null && CrossConnectivity.Current.IsConnected) { + var json = await client.GetStringAsync ($"api/item/{id}"); + return await Task.Run (() => JsonConvert.DeserializeObject (json)); + } + + return null; + } + + public async Task AddItemAsync (Item item) + { + if (item == null || !CrossConnectivity.Current.IsConnected) + return false; + + var serializedItem = JsonConvert.SerializeObject (item); + + var response = await client.PostAsync ($"api/item", new StringContent (serializedItem, Encoding.UTF8, "application/json")); + + return response.IsSuccessStatusCode; + } + + public async Task UpdateItemAsync (Item item) + { + if (item == null || item.Id == null || !CrossConnectivity.Current.IsConnected) + return false; + + var serializedItem = JsonConvert.SerializeObject (item); + var buffer = Encoding.UTF8.GetBytes (serializedItem); + var byteContent = new ByteArrayContent (buffer); + + var response = await client.PutAsync (new Uri ($"api/item/{item.Id}"), byteContent); + + return response.IsSuccessStatusCode; + } + + public async Task DeleteItemAsync (string id) + { + if (string.IsNullOrEmpty (id) && !CrossConnectivity.Current.IsConnected) + return false; + + var response = await client.DeleteAsync ($"api/item/{id}"); + + return response.IsSuccessStatusCode; + } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/Services/IDataStore.cs b/tests/Xamarin.Forms-Performance-Integration/Services/IDataStore.cs new file mode 100644 index 00000000000..3857596c717 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Services/IDataStore.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Xamarin.Forms.Performance.Integration +{ + public interface IDataStore + { + Task AddItemAsync (T item); + Task UpdateItemAsync (T item); + Task DeleteItemAsync (string id); + Task GetItemAsync (string id); + Task> GetItemsAsync (bool forceRefresh = false); + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/Services/MockDataStore.cs b/tests/Xamarin.Forms-Performance-Integration/Services/MockDataStore.cs new file mode 100644 index 00000000000..cd790ea261d --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Services/MockDataStore.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Xamarin.Forms.Performance.Integration +{ + public class MockDataStore : IDataStore + { + List items; + + public MockDataStore () + { + items = new List (); + var mockItems = new List + { + new Item { Id = Guid.NewGuid().ToString(), Text = "First item", Description="This is an item description." }, + new Item { Id = Guid.NewGuid().ToString(), Text = "Second item", Description="This is an item description." }, + new Item { Id = Guid.NewGuid().ToString(), Text = "Third item", Description="This is an item description." }, + new Item { Id = Guid.NewGuid().ToString(), Text = "Fourth item", Description="This is an item description." }, + new Item { Id = Guid.NewGuid().ToString(), Text = "Fifth item", Description="This is an item description." }, + new Item { Id = Guid.NewGuid().ToString(), Text = "Sixth item", Description="This is an item description." }, + }; + + foreach (var item in mockItems) { + items.Add (item); + } + } + + public async Task AddItemAsync (Item item) + { + items.Add (item); + + return await Task.FromResult (true); + } + + public async Task UpdateItemAsync (Item item) + { + var _item = items.Where ((Item arg) => arg.Id == item.Id).FirstOrDefault (); + items.Remove (_item); + items.Add (item); + + return await Task.FromResult (true); + } + + public async Task DeleteItemAsync (string id) + { + var _item = items.Where ((Item arg) => arg.Id == id).FirstOrDefault (); + items.Remove (_item); + + return await Task.FromResult (true); + } + + public async Task GetItemAsync (string id) + { + return await Task.FromResult (items.FirstOrDefault (s => s.Id == id)); + } + + public async Task> GetItemsAsync (bool forceRefresh = false) + { + return await Task.FromResult (items); + } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/ViewModels/AboutViewModel.cs b/tests/Xamarin.Forms-Performance-Integration/ViewModels/AboutViewModel.cs new file mode 100644 index 00000000000..2a18cfd48d5 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/ViewModels/AboutViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Windows.Input; + +using Xamarin.Forms; + +namespace Xamarin.Forms.Performance.Integration +{ + public class AboutViewModel : BaseViewModel + { + public AboutViewModel () + { + Title = "About"; + + OpenWebCommand = new Command (() => Device.OpenUri (new Uri ("https://xamarin.com/platform"))); + } + + public ICommand OpenWebCommand { get; } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/ViewModels/BaseViewModel.cs b/tests/Xamarin.Forms-Performance-Integration/ViewModels/BaseViewModel.cs new file mode 100644 index 00000000000..0a2a3323679 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/ViewModels/BaseViewModel.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +using Xamarin.Forms; + +namespace Xamarin.Forms.Performance.Integration +{ + public class BaseViewModel : INotifyPropertyChanged + { + public IDataStore DataStore => DependencyService.Get> () ?? new MockDataStore (); + + bool isBusy = false; + public bool IsBusy { + get { return isBusy; } + set { SetProperty (ref isBusy, value); } + } + + string title = string.Empty; + public string Title { + get { return title; } + set { SetProperty (ref title, value); } + } + + protected bool SetProperty (ref T backingStore, T value, + [CallerMemberName]string propertyName = "", + Action onChanged = null) + { + if (EqualityComparer.Default.Equals (backingStore, value)) + return false; + + backingStore = value; + onChanged?.Invoke (); + OnPropertyChanged (propertyName); + return true; + } + + #region INotifyPropertyChanged + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged ([CallerMemberName] string propertyName = "") + { + var changed = PropertyChanged; + if (changed == null) + return; + + changed.Invoke (this, new PropertyChangedEventArgs (propertyName)); + } + #endregion + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/ViewModels/ItemDetailViewModel.cs b/tests/Xamarin.Forms-Performance-Integration/ViewModels/ItemDetailViewModel.cs new file mode 100644 index 00000000000..4239d93a849 --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/ViewModels/ItemDetailViewModel.cs @@ -0,0 +1,14 @@ +using System; + +namespace Xamarin.Forms.Performance.Integration +{ + public class ItemDetailViewModel : BaseViewModel + { + public Item Item { get; set; } + public ItemDetailViewModel (Item item = null) + { + Title = item?.Text; + Item = item; + } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/ViewModels/ItemsViewModel.cs b/tests/Xamarin.Forms-Performance-Integration/ViewModels/ItemsViewModel.cs new file mode 100644 index 00000000000..a0ebba2fa6d --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/ViewModels/ItemsViewModel.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Threading.Tasks; + +using Xamarin.Forms; + +namespace Xamarin.Forms.Performance.Integration +{ + public class ItemsViewModel : BaseViewModel + { + public ObservableCollection Items { get; set; } + public Command LoadItemsCommand { get; set; } + + public ItemsViewModel () + { + Title = "Browse"; + Items = new ObservableCollection (); + LoadItemsCommand = new Command (async () => await ExecuteLoadItemsCommand ()); + + MessagingCenter.Subscribe (this, "AddItem", async (obj, item) => { + var _item = item as Item; + Items.Add (_item); + await DataStore.AddItemAsync (_item); + }); + } + + async Task ExecuteLoadItemsCommand () + { + if (IsBusy) + return; + + IsBusy = true; + + try { + Items.Clear (); + var items = await DataStore.GetItemsAsync (true); + foreach (var item in items) { + Items.Add (item); + } + } catch (Exception ex) { + Debug.WriteLine (ex); + } finally { + IsBusy = false; + } + } + } +} diff --git a/tests/Xamarin.Forms-Performance-Integration/Views/AboutPage.xaml b/tests/Xamarin.Forms-Performance-Integration/Views/AboutPage.xaml new file mode 100644 index 00000000000..35ae053100c --- /dev/null +++ b/tests/Xamarin.Forms-Performance-Integration/Views/AboutPage.xaml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + +