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

Commit ce57d0d

Browse files
authored
Merge pull request #1969 from cpraehaus/fix/gh-1960_version-track-downgrades
Correctly handle version downgrades (e.g. via TestFlight)
2 parents 70ae013 + ca7f366 commit ce57d0d

File tree

2 files changed

+168
-3
lines changed

2 files changed

+168
-3
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Diagnostics;
3+
using Xamarin.Essentials;
4+
using Xunit;
5+
using Xunit.Abstractions;
6+
7+
namespace DeviceTests
8+
{
9+
public class VersionTracking_Tests
10+
{
11+
/// <summary>
12+
/// We cannot mock the app version but it should be constant value
13+
/// </summary>
14+
const string currentVersion = "1.0.1.0";
15+
const string currentBuild = "1";
16+
17+
const string versionTrailKey = "VersionTracking.Trail";
18+
const string versionsKey = "VersionTracking.Versions";
19+
const string buildsKey = "VersionTracking.Builds";
20+
static readonly string sharedName = Preferences.GetPrivatePreferencesSharedName("versiontracking");
21+
22+
readonly ITestOutputHelper output;
23+
24+
public VersionTracking_Tests(ITestOutputHelper output)
25+
{
26+
this.output = output;
27+
}
28+
29+
[Fact]
30+
public void First_Launch_Ever()
31+
{
32+
VersionTracking.Track();
33+
Preferences.Clear(sharedName);
34+
35+
VersionTracking.InitVersionTracking();
36+
37+
Assert.Equal(currentVersion, VersionTracking.CurrentVersion);
38+
Assert.True(VersionTracking.IsFirstLaunchEver);
39+
Assert.True(VersionTracking.IsFirstLaunchForCurrentVersion);
40+
Assert.True(VersionTracking.IsFirstLaunchForCurrentBuild);
41+
}
42+
43+
[Fact]
44+
public void First_Launch_For_Version()
45+
{
46+
VersionTracking.Track();
47+
Preferences.Set(versionsKey, string.Join("|", new string[] { "0.8.0", "0.9.0", "1.0.0" }), sharedName);
48+
Preferences.Set(buildsKey, string.Join("|", new string[] { currentBuild }), sharedName);
49+
50+
VersionTracking.InitVersionTracking();
51+
52+
Assert.Equal(currentVersion, VersionTracking.CurrentVersion);
53+
Assert.Equal("1.0.0", VersionTracking.PreviousVersion);
54+
Assert.Equal("0.8.0", VersionTracking.FirstInstalledVersion);
55+
Assert.False(VersionTracking.IsFirstLaunchEver);
56+
Assert.True(VersionTracking.IsFirstLaunchForCurrentVersion);
57+
Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild);
58+
59+
VersionTracking.InitVersionTracking();
60+
61+
Assert.Equal(currentVersion, VersionTracking.CurrentVersion);
62+
Assert.Equal("1.0.0", VersionTracking.PreviousVersion);
63+
Assert.Equal("0.8.0", VersionTracking.FirstInstalledVersion);
64+
Assert.False(VersionTracking.IsFirstLaunchEver);
65+
Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion);
66+
Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild);
67+
}
68+
69+
[Fact]
70+
public void First_Launch_For_Build()
71+
{
72+
VersionTracking.Track();
73+
Preferences.Set(versionsKey, string.Join("|", new string[] { currentVersion }), sharedName);
74+
Preferences.Set(buildsKey, string.Join("|", new string[] { "10", "20" }), sharedName);
75+
76+
VersionTracking.InitVersionTracking();
77+
78+
Assert.Equal(currentVersion, VersionTracking.CurrentVersion);
79+
Assert.Equal("20", VersionTracking.PreviousBuild);
80+
Assert.Equal("10", VersionTracking.FirstInstalledBuild);
81+
Assert.False(VersionTracking.IsFirstLaunchEver);
82+
Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion);
83+
Assert.True(VersionTracking.IsFirstLaunchForCurrentBuild);
84+
85+
VersionTracking.InitVersionTracking();
86+
87+
Assert.Equal(currentVersion, VersionTracking.CurrentVersion);
88+
Assert.Equal("20", VersionTracking.PreviousBuild);
89+
Assert.Equal("10", VersionTracking.FirstInstalledBuild);
90+
Assert.False(VersionTracking.IsFirstLaunchEver);
91+
Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion);
92+
Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild);
93+
}
94+
95+
[Fact]
96+
public void First_Launch_After_Downgrade()
97+
{
98+
VersionTracking.Track();
99+
Preferences.Set(versionsKey, string.Join("|", new string[] { currentVersion, "1.0.2", "1.0.3" }), sharedName);
100+
101+
VersionTracking.InitVersionTracking();
102+
output.WriteLine(VersionTracking.GetStatus());
103+
104+
Assert.Equal(currentVersion, VersionTracking.CurrentVersion);
105+
Assert.Equal("1.0.3", VersionTracking.PreviousVersion);
106+
Assert.Equal("1.0.2", VersionTracking.FirstInstalledVersion);
107+
Assert.False(VersionTracking.IsFirstLaunchEver);
108+
Assert.True(VersionTracking.IsFirstLaunchForCurrentVersion);
109+
110+
VersionTracking.InitVersionTracking();
111+
112+
Assert.Equal(currentVersion, VersionTracking.CurrentVersion);
113+
Assert.Equal("1.0.3", VersionTracking.PreviousVersion);
114+
Assert.Equal("1.0.2", VersionTracking.FirstInstalledVersion);
115+
Assert.False(VersionTracking.IsFirstLaunchEver);
116+
Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion);
117+
}
118+
119+
[Fact]
120+
public void First_Launch_After_Build_Downgrade()
121+
{
122+
VersionTracking.Track();
123+
Preferences.Set(versionsKey, string.Join("|", new string[] { currentVersion }), sharedName);
124+
Preferences.Set(buildsKey, string.Join("|", new string[] { currentBuild, "10", "20" }), sharedName);
125+
126+
VersionTracking.InitVersionTracking();
127+
output.WriteLine(VersionTracking.GetStatus());
128+
129+
Assert.Equal(currentBuild, VersionTracking.CurrentBuild);
130+
Assert.Equal("20", VersionTracking.PreviousBuild);
131+
Assert.Equal("10", VersionTracking.FirstInstalledBuild);
132+
Assert.False(VersionTracking.IsFirstLaunchEver);
133+
Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion);
134+
Assert.True(VersionTracking.IsFirstLaunchForCurrentBuild);
135+
136+
VersionTracking.InitVersionTracking();
137+
138+
Assert.Equal(currentBuild, VersionTracking.CurrentBuild);
139+
Assert.Equal("20", VersionTracking.PreviousBuild);
140+
Assert.Equal("10", VersionTracking.FirstInstalledBuild);
141+
Assert.False(VersionTracking.IsFirstLaunchEver);
142+
Assert.False(VersionTracking.IsFirstLaunchForCurrentVersion);
143+
Assert.False(VersionTracking.IsFirstLaunchForCurrentBuild);
144+
}
145+
}
146+
}

Xamarin.Essentials/VersionTracking/VersionTracking.shared.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,20 @@ public static class VersionTracking
1313

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

16-
static readonly Dictionary<string, List<string>> versionTrail;
16+
static Dictionary<string, List<string>> versionTrail;
1717

1818
static VersionTracking()
19+
{
20+
InitVersionTracking();
21+
}
22+
23+
/// <summary>
24+
/// Initialize VersionTracking module, load data and track current version
25+
/// </summary>
26+
/// <remarks>
27+
/// For internal use. Usually only called once in production code, but multiple times in unit tests
28+
/// </remarks>
29+
internal static void InitVersionTracking()
1930
{
2031
IsFirstLaunchEver = !Preferences.ContainsKey(versionsKey, sharedName) || !Preferences.ContainsKey(buildsKey, sharedName);
2132
if (IsFirstLaunchEver)
@@ -35,15 +46,19 @@ static VersionTracking()
3546
};
3647
}
3748

38-
IsFirstLaunchForCurrentVersion = !versionTrail[versionsKey].Contains(CurrentVersion);
49+
IsFirstLaunchForCurrentVersion = !versionTrail[versionsKey].Contains(CurrentVersion) || CurrentVersion != LastInstalledVersion;
3950
if (IsFirstLaunchForCurrentVersion)
4051
{
52+
// Avoid duplicates and move current version to end of list if already present
53+
versionTrail[versionsKey].RemoveAll(v => v == CurrentVersion);
4154
versionTrail[versionsKey].Add(CurrentVersion);
4255
}
4356

44-
IsFirstLaunchForCurrentBuild = !versionTrail[buildsKey].Contains(CurrentBuild);
57+
IsFirstLaunchForCurrentBuild = !versionTrail[buildsKey].Contains(CurrentBuild) || CurrentBuild != LastInstalledBuild;
4558
if (IsFirstLaunchForCurrentBuild)
4659
{
60+
// Avoid duplicates and move current build to end of list if already present
61+
versionTrail[buildsKey].RemoveAll(b => b == CurrentBuild);
4762
versionTrail[buildsKey].Add(CurrentBuild);
4863
}
4964

@@ -119,5 +134,9 @@ static string GetPrevious(string key)
119134
var trail = versionTrail[key];
120135
return (trail.Count >= 2) ? trail[trail.Count - 2] : null;
121136
}
137+
138+
static string LastInstalledVersion => versionTrail[versionsKey].LastOrDefault();
139+
140+
static string LastInstalledBuild => versionTrail[buildsKey].LastOrDefault();
122141
}
123142
}

0 commit comments

Comments
 (0)