Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1969 from cpraehaus/fix/gh-1960_version-track-dow…
Browse files Browse the repository at this point in the history
…ngrades

Correctly handle version downgrades (e.g. via TestFlight)
  • Loading branch information
jfversluis authored Apr 6, 2022
2 parents 70ae013 + ca7f366 commit ce57d0d
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 3 deletions.
146 changes: 146 additions & 0 deletions DeviceTests/DeviceTests.Shared/VersionTracking_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System;
using System.Diagnostics;
using Xamarin.Essentials;
using Xunit;
using Xunit.Abstractions;

namespace DeviceTests
{
public class VersionTracking_Tests
{
/// <summary>
/// We cannot mock the app version but it should be constant value
/// </summary>
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);
}
}
}
25 changes: 22 additions & 3 deletions Xamarin.Essentials/VersionTracking/VersionTracking.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,20 @@ public static class VersionTracking

static readonly string sharedName = Preferences.GetPrivatePreferencesSharedName("versiontracking");

static readonly Dictionary<string, List<string>> versionTrail;
static Dictionary<string, List<string>> versionTrail;

static VersionTracking()
{
InitVersionTracking();
}

/// <summary>
/// Initialize VersionTracking module, load data and track current version
/// </summary>
/// <remarks>
/// For internal use. Usually only called once in production code, but multiple times in unit tests
/// </remarks>
internal static void InitVersionTracking()
{
IsFirstLaunchEver = !Preferences.ContainsKey(versionsKey, sharedName) || !Preferences.ContainsKey(buildsKey, sharedName);
if (IsFirstLaunchEver)
Expand All @@ -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);
}

Expand Down Expand Up @@ -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();
}
}

0 comments on commit ce57d0d

Please sign in to comment.