Viewing is open to everyone. Downloading and starring need a GitHub sign-in. Sign in with GitHub

TelemetryTests.cs

v0.1.0-build.85 Latest Commit ede38f5

Size
7.9 KB
Lines
217
Language
csharp
SHA-256
fd5c81ba3b21…
View on GitHub

Sign in with GitHub to download. Each download is recorded with your GitHub username.

1using System;
2using System.IO;
3using System.Net;
4using System.Net.Sockets;
5using System.Text;
6using System.Threading.Tasks;
7using Xunit;
8
9namespace OpenCKey.Tests
10{
11 // Telemetry state is process-global: every test touching it lives in this
12 // collection, which xUnit never runs in parallel with itself.
13 [CollectionDefinition("telemetry", DisableParallelization = true)]
14 public class TelemetryCollection
15 {
16 }
17
18 [Collection("telemetry")]
19 public class TelemetryTests : IDisposable
20 {
21 public TelemetryTests()
22 {
23 TelemetrySender.AutoSend = false; // the tests call FlushAsync themselves
24 CKeyTelemetry.ResetForTesting();
25 }
26
27 public void Dispose()
28 {
29 TelemetrySender.Endpoint = new Uri(CKeyTelemetry.DefaultEndpoint);
30 CKeyTelemetry.ResetForTesting();
31 CKey.Telemetry = CKey.TelemetryLocal; // nothing from the test run is ever sent
32 }
33
34 [Fact]
35 public void DefaultsToOne()
36 {
37 Assert.True(CKeyTelemetry.IsAvailable);
38 Assert.Equal(1, CKey.Telemetry);
39 }
40
41 [Theory]
42 [InlineData(0)]
43 [InlineData(1)]
44 [InlineData(2)]
45 public void AcceptsZeroOneTwo(int mode)
46 {
47 CKey.Telemetry = mode;
48 Assert.Equal(mode, CKey.Telemetry);
49 }
50
51 [Theory]
52 [InlineData(-1)]
53 [InlineData(3)]
54 [InlineData(int.MaxValue)]
55 public void RejectsOtherValuesAndKeepsTheMode(int mode)
56 {
57 CKey.Telemetry = 2;
58 Assert.Throws<ArgumentOutOfRangeException>(() => CKey.Telemetry = mode);
59 Assert.Equal(2, CKey.Telemetry); // never silently reset to 1
60 }
61
62 [Fact]
63 public void ScanSessionCountsASuccess()
64 {
65 CKey.Telemetry = 2;
66 var symbol = CKeySymbol.Encode("session");
67 byte[] frame = Frames.Frame(symbol, out int w, out int h, out int stride);
68 using (var session = new CKeyScanSession())
69 {
70 Assert.Equal("session", session.ProcessFrame(frame, w, h, stride)?.Text);
71 Assert.True(session.HasDecoded);
72 Assert.NotNull(session.ProcessFrame(frame, w, h, stride)); // later decodes still returned
73 }
74 CKeyTelemetryCounters c = CKeyTelemetry.Snapshot();
75 Assert.Equal(1UL, c.SessionsStarted);
76 Assert.Equal(1UL, c.SessionsSucceeded);
77 Assert.Equal(0UL, c.SessionsFailed);
78 Assert.Equal(2UL, c.Frames);
79 Assert.Equal(1UL, c.Decoded[0]); // p0
80 Assert.Equal(1UL, c.TimeToDecodeMs.Count);
81 Assert.Equal(2UL, c.FrameMs.Count);
82 }
83
84 [Fact]
85 public void PrintLayoutCountsAsP0_1()
86 {
87 CKey.Telemetry = 2;
88 var symbol = CKeySymbol.Encode("print", layout: Layout.Print);
89 byte[] frame = Frames.Frame(symbol, out int w, out int h, out int stride);
90 using (var session = new CKeyScanSession()) Assert.NotNull(session.ProcessFrame(frame, w, h, stride));
91 Assert.Equal(1UL, CKeyTelemetry.Snapshot().Decoded[1]);
92 }
93
94 [Fact]
95 public void SessionWithoutDecodeFails()
96 {
97 CKey.Telemetry = 2;
98 var blank = new byte[64 * 64];
99 using (var session = new CKeyScanSession())
100 {
101 Assert.Null(session.ProcessFrame(blank, 64, 64, 64));
102 session.Restart(); // ends the first session as failed
103 }
104 CKeyTelemetryCounters c = CKeyTelemetry.Snapshot();
105 Assert.Equal(2UL, c.SessionsStarted);
106 Assert.Equal(2UL, c.SessionsFailed);
107 Assert.Equal(0UL, c.SessionsSucceeded);
108 }
109
110 [Fact]
111 public void ModeZeroCountsNothingAndPurges()
112 {
113 CKey.Telemetry = 2;
114 using (new CKeyScanSession())
115 {
116 }
117 Assert.Equal(1UL, CKeyTelemetry.Snapshot().SessionsStarted);
118 CKey.Telemetry = 0;
119 using (new CKeyScanSession())
120 {
121 }
122 Assert.Equal(0UL, CKeyTelemetry.Snapshot().SessionsStarted);
123 }
124
125 [Fact]
126 public void ExportImportRoundTrip()
127 {
128 CKey.Telemetry = 2;
129 using (new CKeyScanSession())
130 {
131 }
132 string state = CKeyTelemetry.ExportState();
133 Assert.StartsWith("{\"state\":1", state);
134 CKeyTelemetry.ResetForTesting();
135 CKey.Telemetry = 2;
136 CKeyTelemetry.ImportState(state);
137 Assert.Equal(1UL, CKeyTelemetry.Snapshot().SessionsStarted);
138 Assert.Throws<CKeyException>(() => CKeyTelemetry.ImportState("{\"nope\":1}"));
139 }
140
141 [Fact]
142 public async Task FlushSendsOneBatchToTheEndpoint()
143 {
144 // Build an unsent delta that is due immediately: deltas restored by
145 // import are exempt from the minimum age, and a fresh process has no
146 // hourly wait.
147 CKey.Telemetry = 1;
148 using (new CKeyScanSession())
149 {
150 }
151 string state = CKeyTelemetry.ExportState();
152 CKeyTelemetry.ResetForTesting();
153 CKeyTelemetry.ImportState(state);
154
155 int port = FreePort();
156 using var listener = new HttpListener();
157 listener.Prefixes.Add($"http://localhost:{port}/");
158 listener.Start();
159 TelemetrySender.Endpoint = new Uri($"http://localhost:{port}/api/telemetry/v1/batch");
160
161 Task<string> server = Task.Run(async () =>
162 {
163 HttpListenerContext context = await listener.GetContextAsync();
164 string body;
165 using (var reader = new StreamReader(context.Request.InputStream, Encoding.UTF8)) body = await reader.ReadToEndAsync();
166 string contentType = context.Request.ContentType ?? string.Empty;
167 context.Response.StatusCode = 204;
168 context.Response.Close();
169 return contentType + "\n" + body;
170 });
171
172 bool accepted = await CKeyTelemetry.FlushAsync().WaitAsync(TimeSpan.FromSeconds(30));
173 Assert.True(accepted);
174 string[] request = (await server.WaitAsync(TimeSpan.FromSeconds(30))).Split('\n');
175 Assert.StartsWith("application/json", request[0]);
176 Assert.Contains("\"schema\":1", request[1]);
177 Assert.Contains("\"sdk\":\"dotnet\"", request[1]);
178 Assert.Contains($"\"sdk_version\":\"{CKey.Version}\"", request[1]);
179 Assert.Contains("\"sessions\":{\"started\":1,\"succeeded\":0,\"failed\":1}", request[1]);
180
181 // Accepted: nothing more is due (and the hourly cadence has started).
182 Assert.False(await CKeyTelemetry.FlushAsync());
183 }
184
185 [Fact]
186 public async Task NetworkFailureKeepsTheBatch()
187 {
188 CKey.Telemetry = 1;
189 using (new CKeyScanSession())
190 {
191 }
192 string state = CKeyTelemetry.ExportState();
193 CKeyTelemetry.ResetForTesting();
194 CKeyTelemetry.ImportState(state);
195 TelemetrySender.Endpoint = new Uri($"http://127.0.0.1:{FreePort()}/"); // nothing listens
196 Assert.False(await CKeyTelemetry.FlushAsync());
197 // Kept: the delta still holds the session (it was merged back).
198 Assert.Contains("\"unsent\":{\"sessions\":{\"started\":1", CKeyTelemetry.ExportState());
199 }
200
201 [Fact]
202 public void VersionMatchesTheTelemetrySchema()
203 {
204 Assert.Matches(@"^0\.[0-9]+\.[0-9]+(-(build|dev)\.[0-9]{1,6})?$", CKey.Version);
205 Assert.False(string.IsNullOrEmpty(CKey.CoreVersion));
206 }
207
208 private static int FreePort()
209 {
210 var l = new TcpListener(IPAddress.Loopback, 0);
211 l.Start();
212 int port = ((IPEndPoint)l.LocalEndpoint).Port;
213 l.Stop();
214 return port;
215 }
216 }
217}