From ae271c4b3ef9269ed99270af4cd6135bef6c659f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Pr=C3=A4hauser?= Date: Sun, 20 Feb 2022 15:19:38 +0100 Subject: [PATCH 1/2] Add device unit tests for VersionTracking module --- .../VersionTracking_Tests.cs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 DeviceTests/DeviceTests.Shared/VersionTracking_Tests.cs diff --git a/DeviceTests/DeviceTests.Shared/VersionTracking_Tests.cs b/DeviceTests/DeviceTests.Shared/VersionTracking_Tests.cs new file mode 100644 index 000000000..bc646ad08 --- /dev/null +++ b/DeviceTests/DeviceTests.Shared/VersionTracking_Tests.cs @@ -0,0 +1,146 @@ +using System; +using System.Diagnostics; +using Xamarin.Essentials; +using Xunit; +using Xunit.Abstractions; + +namespace DeviceTests +{ + public class VersionTracking_Tests + { + /// + /// We cannot mock the app version but it should be constant value + /// + const string currentVersion = "1.0.1.0"; + const string currentBuild = "1"; + + const string versionTrailKey = "VersionTracking.Trail"; + const string versionsKey = "VersionTracking.Versions"; + const string buildsKey = "VersionTracking.Builds"; + static readonly string sharedName = Preferences.GetPrivatePreferencesSharedName("versiontracking"); + + readonly ITestOutputHelper output; + + public VersionTracking_Tests(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void First_Launch_Ever() + { + VersionTracking.Track(); + Preferences.Clear(sharedName); + + VersionTracking.InitVersionTracking(); + + Assert.Equal(currentVersion, VersionTracking.CurrentVersion); + Assert.True(VersionTracking.IsFirstLaunchEver); + Assert.True(VersionTracking.IsFirstLaunchForCurrentVersion); + Assert.True(VersionTracking.IsFirstLaunchForCurrentBuild); + } + + [Fact] + public void First_Launch_For_Version() + { + VersionTracking.Track(); + Preferences.Set(versionsKey, string.Join("|", new string[] { "0.8.0", "0.9.0", "1.0.0" }), sharedName); + Preferences.Set(buildsKey, string.Join("|", new string[] { currentBuild }), sharedName); + + VersionTracking.InitVersionTracking(); + + Assert.Equal(currentVersion, VersionTracking.CurrentVersion); + Assert.Equal("1.0.0", VersionTracking.PreviousVersion); + Assert.Equal("0.8.0", VersionTracking.FirstInstalledVersion); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.True(VersionTracking.IsFirstLaunchForCurrentVersion); + Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild); + + VersionTracking.InitVersionTracking(); + + Assert.Equal(currentVersion, VersionTracking.CurrentVersion); + Assert.Equal("1.0.0", VersionTracking.PreviousVersion); + Assert.Equal("0.8.0", VersionTracking.FirstInstalledVersion); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion); + Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild); + } + + [Fact] + public void First_Launch_For_Build() + { + VersionTracking.Track(); + Preferences.Set(versionsKey, string.Join("|", new string[] { currentVersion }), sharedName); + Preferences.Set(buildsKey, string.Join("|", new string[] { "10", "20" }), sharedName); + + VersionTracking.InitVersionTracking(); + + Assert.Equal(currentVersion, VersionTracking.CurrentVersion); + Assert.Equal("20", VersionTracking.PreviousBuild); + Assert.Equal("10", VersionTracking.FirstInstalledBuild); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion); + Assert.True(VersionTracking.IsFirstLaunchForCurrentBuild); + + VersionTracking.InitVersionTracking(); + + Assert.Equal(currentVersion, VersionTracking.CurrentVersion); + Assert.Equal("20", VersionTracking.PreviousBuild); + Assert.Equal("10", VersionTracking.FirstInstalledBuild); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion); + Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild); + } + + [Fact] + public void First_Launch_After_Downgrade() + { + VersionTracking.Track(); + Preferences.Set(versionsKey, string.Join("|", new string[] { currentVersion, "1.0.2", "1.0.3" }), sharedName); + + VersionTracking.InitVersionTracking(); + output.WriteLine(VersionTracking.GetStatus()); + + Assert.Equal(currentVersion, VersionTracking.CurrentVersion); + Assert.Equal("1.0.3", VersionTracking.PreviousVersion); + Assert.Equal("1.0.2", VersionTracking.FirstInstalledVersion); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.True(VersionTracking.IsFirstLaunchForCurrentVersion); + + VersionTracking.InitVersionTracking(); + + Assert.Equal(currentVersion, VersionTracking.CurrentVersion); + Assert.Equal("1.0.3", VersionTracking.PreviousVersion); + Assert.Equal("1.0.2", VersionTracking.FirstInstalledVersion); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion); + } + + [Fact] + public void First_Launch_After_Build_Downgrade() + { + VersionTracking.Track(); + Preferences.Set(versionsKey, string.Join("|", new string[] { currentVersion }), sharedName); + Preferences.Set(buildsKey, string.Join("|", new string[] { currentBuild, "10", "20" }), sharedName); + + VersionTracking.InitVersionTracking(); + output.WriteLine(VersionTracking.GetStatus()); + + Assert.Equal(currentBuild, VersionTracking.CurrentBuild); + Assert.Equal("20", VersionTracking.PreviousBuild); + Assert.Equal("10", VersionTracking.FirstInstalledBuild); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion); + Assert.True(VersionTracking.IsFirstLaunchForCurrentBuild); + + VersionTracking.InitVersionTracking(); + + Assert.Equal(currentBuild, VersionTracking.CurrentBuild); + Assert.Equal("20", VersionTracking.PreviousBuild); + Assert.Equal("10", VersionTracking.FirstInstalledBuild); + Assert.False(VersionTracking.IsFirstLaunchEver); + Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion); + Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild); + } + } +} From 0495f8640bddfebabb79856dabfdf79acc33207a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Pr=C3=A4hauser?= Date: Sun, 20 Feb 2022 15:21:21 +0100 Subject: [PATCH 2/2] Correctly handle version/build downgrades, fixes #1960 --- .../VersionTracking/VersionTracking.shared.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Xamarin.Essentials/VersionTracking/VersionTracking.shared.cs b/Xamarin.Essentials/VersionTracking/VersionTracking.shared.cs index 1c56d5dd5..85d460446 100644 --- a/Xamarin.Essentials/VersionTracking/VersionTracking.shared.cs +++ b/Xamarin.Essentials/VersionTracking/VersionTracking.shared.cs @@ -13,9 +13,20 @@ public static class VersionTracking static readonly string sharedName = Preferences.GetPrivatePreferencesSharedName("versiontracking"); - static readonly Dictionary> versionTrail; + static Dictionary> versionTrail; static VersionTracking() + { + InitVersionTracking(); + } + + /// + /// Initialize VersionTracking module, load data and track current version + /// + /// + /// For internal use. Usually only called once in production code, but multiple times in unit tests + /// + internal static void InitVersionTracking() { IsFirstLaunchEver = !Preferences.ContainsKey(versionsKey, sharedName) || !Preferences.ContainsKey(buildsKey, sharedName); if (IsFirstLaunchEver) @@ -35,15 +46,19 @@ static VersionTracking() }; } - IsFirstLaunchForCurrentVersion = !versionTrail[versionsKey].Contains(CurrentVersion); + IsFirstLaunchForCurrentVersion = !versionTrail[versionsKey].Contains(CurrentVersion) || CurrentVersion != LastInstalledVersion; if (IsFirstLaunchForCurrentVersion) { + // Avoid duplicates and move current version to end of list if already present + versionTrail[versionsKey].RemoveAll(v => v == CurrentVersion); versionTrail[versionsKey].Add(CurrentVersion); } - IsFirstLaunchForCurrentBuild = !versionTrail[buildsKey].Contains(CurrentBuild); + IsFirstLaunchForCurrentBuild = !versionTrail[buildsKey].Contains(CurrentBuild) || CurrentBuild != LastInstalledBuild; if (IsFirstLaunchForCurrentBuild) { + // Avoid duplicates and move current build to end of list if already present + versionTrail[buildsKey].RemoveAll(b => b == CurrentBuild); versionTrail[buildsKey].Add(CurrentBuild); } @@ -119,5 +134,9 @@ static string GetPrevious(string key) var trail = versionTrail[key]; return (trail.Count >= 2) ? trail[trail.Count - 2] : null; } + + static string LastInstalledVersion => versionTrail[versionsKey].LastOrDefault(); + + static string LastInstalledBuild => versionTrail[buildsKey].LastOrDefault(); } }