1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
6 
7 #define MOZ_USE_LAUNCHER_ERROR
8 
9 #include "mozilla/LauncherRegistryInfo.h"
10 #include "mozilla/NativeNt.h"
11 #include "mozilla/ScopeExit.h"
12 #include "mozilla/Unused.h"
13 #include "nsWindowsHelpers.h"
14 
15 #include "LauncherRegistryInfo.cpp"
16 
17 #include <string>
18 
19 static const char kMsgStart[] = "TEST-FAILED | LauncherRegistryInfo | ";
20 
21 static const wchar_t kRegKeyPath[] = L"SOFTWARE\\" EXPAND_STRING_MACRO(
22     MOZ_APP_VENDOR) L"\\" EXPAND_STRING_MACRO(MOZ_APP_BASENAME) L"\\Launcher";
23 static const wchar_t kBrowserSuffix[] = L"|Browser";
24 static const wchar_t kLauncherSuffix[] = L"|Launcher";
25 static const wchar_t kImageSuffix[] = L"|Image";
26 static const wchar_t kTelemetrySuffix[] = L"|Telemetry";
27 
28 static std::wstring gBrowserValue;
29 static std::wstring gLauncherValue;
30 static std::wstring gImageValue;
31 static std::wstring gTelemetryValue;
32 
33 static DWORD gMyImageTimestamp;
34 
35 #define RUN_TEST(result, fn)                                                 \
36   if ((result = fn()).isErr()) {                                             \
37     const mozilla::LauncherError& err = result.inspectErr();                 \
38     printf("%s%s | %08lx (%s:%d)\n", kMsgStart, #fn, err.mError.AsHResult(), \
39            err.mFile, err.mLine);                                            \
40     return 1;                                                                \
41   }
42 
43 #define EXPECT_COMMIT_IS_OK()                        \
44   do {                                               \
45     mozilla::LauncherVoidResult vr2 = info.Commit(); \
46     if (vr2.isErr()) {                               \
47       return vr2;                                    \
48     }                                                \
49   } while (0)
50 
51 #define EXPECT_CHECK_RESULT_IS(desired, expected)                       \
52   do {                                                                  \
53     mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> \
54         result = info.Check(mozilla::LauncherRegistryInfo::desired);    \
55     if (result.isErr()) {                                               \
56       return result.propagateErr();                                     \
57     }                                                                   \
58     if (result.unwrap() != mozilla::LauncherRegistryInfo::expected) {   \
59       return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL);                       \
60     }                                                                   \
61   } while (0)
62 
63 #define EXPECT_ENABLED_STATE_IS(expected)                                \
64   do {                                                                   \
65     mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> \
66         enabled = info.IsEnabled();                                      \
67     if (enabled.isErr()) {                                               \
68       return enabled.propagateErr();                                     \
69     }                                                                    \
70     if (enabled.unwrap() != mozilla::LauncherRegistryInfo::expected) {   \
71       return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);                  \
72     }                                                                    \
73   } while (0)
74 
75 #define EXPECT_TELEMETRY_IS_ENABLED(expected)                          \
76   do {                                                                 \
77     mozilla::LauncherResult<bool> enabled = info.IsTelemetryEnabled(); \
78     if (enabled.isErr()) {                                             \
79       return enabled.propagateErr();                                   \
80     }                                                                  \
81     if (enabled.unwrap() != expected) {                                \
82       return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);                \
83     }                                                                  \
84   } while (0)
85 
86 #define EXPECT_REG_DWORD_EXISTS_AND_EQ(name, expected)      \
87   do {                                                      \
88     mozilla::LauncherResult<mozilla::Maybe<DWORD>> result = \
89         ReadRegistryValueData<DWORD>(name, REG_DWORD);      \
90     if (result.isErr()) {                                   \
91       return result.propagateErr();                         \
92     }                                                       \
93     if (result.inspect().isNothing() ||                     \
94         result.inspect().value() != expected) {             \
95       return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);     \
96     }                                                       \
97   } while (0)
98 
99 #define EXPECT_REG_QWORD_EXISTS(name)                          \
100   do {                                                         \
101     mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \
102         ReadRegistryValueData<uint64_t>(name, REG_QWORD);      \
103     if (result.isErr()) {                                      \
104       return result.propagateErr();                            \
105     }                                                          \
106     if (result.inspect().isNothing()) {                        \
107       return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);        \
108     }                                                          \
109   } while (0)
110 
111 #define EXPECT_REG_QWORD_EXISTS_AND_EQ(name, expected)         \
112   do {                                                         \
113     mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \
114         ReadRegistryValueData<uint64_t>(name, REG_QWORD);      \
115     if (result.isErr()) {                                      \
116       return result.propagateErr();                            \
117     }                                                          \
118     if (result.inspect().isNothing() ||                        \
119         result.inspect().value() != expected) {                \
120       return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);        \
121     }                                                          \
122   } while (0)
123 
124 #define EXPECT_REG_DWORD_DOES_NOT_EXIST(name)               \
125   do {                                                      \
126     mozilla::LauncherResult<mozilla::Maybe<DWORD>> result = \
127         ReadRegistryValueData<DWORD>(name, REG_DWORD);      \
128     if (result.isErr()) {                                   \
129       return result.propagateErr();                         \
130     }                                                       \
131     if (result.inspect().isSome()) {                        \
132       return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);     \
133     }                                                       \
134   } while (0)
135 
136 #define EXPECT_REG_QWORD_DOES_NOT_EXIST(name)                  \
137   do {                                                         \
138     mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \
139         ReadRegistryValueData<uint64_t>(name, REG_QWORD);      \
140     if (result.isErr()) {                                      \
141       return result.propagateErr();                            \
142     }                                                          \
143     if (result.inspect().isSome()) {                           \
144       return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);        \
145     }                                                          \
146   } while (0)
147 
148 template <typename T>
ReadRegistryValueData(const std::wstring & name,DWORD expectedType)149 static mozilla::LauncherResult<mozilla::Maybe<T>> ReadRegistryValueData(
150     const std::wstring& name, DWORD expectedType) {
151   T data;
152   DWORD dataLen = sizeof(data);
153   DWORD type;
154   LSTATUS status = ::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str(),
155                                   RRF_RT_ANY, &type, &data, &dataLen);
156   if (status == ERROR_FILE_NOT_FOUND) {
157     return mozilla::Maybe<T>();
158   }
159 
160   if (status != ERROR_SUCCESS) {
161     return LAUNCHER_ERROR_FROM_WIN32(status);
162   }
163 
164   if (type != expectedType) {
165     return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
166   }
167 
168   return mozilla::Some(data);
169 }
170 
171 template <typename T>
WriteRegistryValueData(const std::wstring & name,DWORD type,T data)172 static mozilla::LauncherVoidResult WriteRegistryValueData(
173     const std::wstring& name, DWORD type, T data) {
174   LSTATUS status = ::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath,
175                                      name.c_str(), type, &data, sizeof(T));
176   if (status != ERROR_SUCCESS) {
177     return LAUNCHER_ERROR_FROM_WIN32(status);
178   }
179 
180   return mozilla::Ok();
181 }
182 
DeleteRegistryValueData(const std::wstring & name)183 static mozilla::LauncherVoidResult DeleteRegistryValueData(
184     const std::wstring& name) {
185   LSTATUS status =
186       ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str());
187   if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) {
188     return mozilla::Ok();
189   }
190 
191   return LAUNCHER_ERROR_FROM_WIN32(status);
192 }
193 
DeleteAllRegstryValues()194 static mozilla::LauncherVoidResult DeleteAllRegstryValues() {
195   // Unblock commit via ReflectPrefToRegistry
196   // (We need to set false, and then true to bypass the early return)
197   mozilla::LauncherRegistryInfo info;
198   mozilla::LauncherVoidResult vr = info.ReflectPrefToRegistry(false);
199   vr = info.ReflectPrefToRegistry(true);
200   if (vr.isErr()) {
201     return vr;
202   }
203 
204   vr = DeleteRegistryValueData(gImageValue);
205   if (vr.isErr()) {
206     return vr;
207   }
208 
209   vr = DeleteRegistryValueData(gLauncherValue);
210   if (vr.isErr()) {
211     return vr;
212   }
213 
214   vr = DeleteRegistryValueData(gBrowserValue);
215   if (vr.isErr()) {
216     return vr;
217   }
218 
219   return DeleteRegistryValueData(gTelemetryValue);
220 }
221 
SetupEnabledScenario()222 static mozilla::LauncherVoidResult SetupEnabledScenario() {
223   // Reset the registry state to an enabled state. First, we delete all existing
224   // registry values (if any).
225   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
226   if (vr.isErr()) {
227     return vr;
228   }
229 
230   // Now we run Check(Launcher)...
231   mozilla::LauncherRegistryInfo info;
232   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
233   EXPECT_COMMIT_IS_OK();
234   // ...and Check(Browser)
235   EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser);
236   EXPECT_COMMIT_IS_OK();
237 
238   // By this point we are considered to be fully enabled.
239   return mozilla::Ok();
240 }
241 
TestEmptyRegistry()242 static mozilla::LauncherVoidResult TestEmptyRegistry() {
243   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
244   if (vr.isErr()) {
245     return vr;
246   }
247 
248   mozilla::LauncherRegistryInfo info;
249   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
250   EXPECT_COMMIT_IS_OK();
251 
252   // LauncherRegistryInfo should have created Launcher and Image values
253   EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
254   EXPECT_REG_QWORD_EXISTS(gLauncherValue);
255   EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue);
256 
257   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
258 
259   return mozilla::Ok();
260 }
261 
TestNormal()262 static mozilla::LauncherVoidResult TestNormal() {
263   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
264   if (vr.isErr()) {
265     return vr;
266   }
267   vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp);
268   if (vr.isErr()) {
269     return vr;
270   }
271   vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, QPCNowRaw());
272   if (vr.isErr()) {
273     return vr;
274   }
275 
276   mozilla::LauncherRegistryInfo info;
277   EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser);
278   EXPECT_COMMIT_IS_OK();
279 
280   // Make sure the browser timestamp is newer than the launcher's
281   mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherTs =
282       ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD);
283   if (launcherTs.isErr()) {
284     return launcherTs.propagateErr();
285   }
286   mozilla::LauncherResult<mozilla::Maybe<uint64_t>> browserTs =
287       ReadRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD);
288   if (browserTs.isErr()) {
289     return browserTs.propagateErr();
290   }
291   if (launcherTs.inspect().isNothing() || browserTs.inspect().isNothing() ||
292       browserTs.inspect().value() <= launcherTs.inspect().value()) {
293     return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL);
294   }
295 
296   EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
297 
298   return mozilla::Ok();
299 }
300 
TestBrowserNoLauncher()301 static mozilla::LauncherVoidResult TestBrowserNoLauncher() {
302   mozilla::LauncherVoidResult vr = SetupEnabledScenario();
303   if (vr.isErr()) {
304     return vr;
305   }
306   vr = DeleteRegistryValueData(gLauncherValue);
307   if (vr.isErr()) {
308     return vr;
309   }
310 
311   mozilla::LauncherRegistryInfo info;
312   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
313   EXPECT_COMMIT_IS_OK();
314 
315   // Verify that we still don't have a launcher timestamp
316   EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
317   // Verify that the browser timestamp is now zero
318   EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
319 
320   EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
321 
322   return mozilla::Ok();
323 }
324 
TestLauncherNoBrowser()325 static mozilla::LauncherVoidResult TestLauncherNoBrowser() {
326   constexpr uint64_t launcherTs = 0x77777777;
327   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
328   if (vr.isErr()) {
329     return vr;
330   }
331   vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp);
332   if (vr.isErr()) {
333     return vr;
334   }
335   vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, launcherTs);
336   if (vr.isErr()) {
337     return vr;
338   }
339 
340   mozilla::LauncherRegistryInfo info;
341   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
342   EXPECT_COMMIT_IS_OK();
343 
344   // Launcher's timestamps is kept intact while browser's is set to 0.
345   EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
346   EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
347 
348   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
349 
350   return mozilla::Ok();
351 }
352 
TestBrowserLessThanLauncher()353 static mozilla::LauncherVoidResult TestBrowserLessThanLauncher() {
354   constexpr uint64_t launcherTs = 0x77777777, browserTs = 0x66666666;
355   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
356   if (vr.isErr()) {
357     return vr;
358   }
359   vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp);
360   if (vr.isErr()) {
361     return vr;
362   }
363   vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, launcherTs);
364   if (vr.isErr()) {
365     return vr;
366   }
367   vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, browserTs);
368   if (vr.isErr()) {
369     return vr;
370   }
371 
372   mozilla::LauncherRegistryInfo info;
373   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
374   EXPECT_COMMIT_IS_OK();
375 
376   // Launcher's timestamps is kept intact while browser's is set to 0.
377   EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
378   EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
379 
380   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
381 
382   return mozilla::Ok();
383 }
384 
TestImageTimestampChange()385 static mozilla::LauncherVoidResult TestImageTimestampChange() {
386   // This should reset the timestamps and then essentially run like
387   // TestEmptyRegistry
388   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
389   if (vr.isErr()) {
390     return vr;
391   }
392   vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
393   if (vr.isErr()) {
394     return vr;
395   }
396   vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, 1ULL);
397   if (vr.isErr()) {
398     return vr;
399   }
400   vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, 2ULL);
401   if (vr.isErr()) {
402     return vr;
403   }
404 
405   mozilla::LauncherRegistryInfo info;
406   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
407   EXPECT_COMMIT_IS_OK();
408 
409   EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
410   EXPECT_REG_QWORD_EXISTS(gLauncherValue);
411   EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue);
412 
413   return mozilla::Ok();
414 }
415 
TestImageTimestampChangeWhenDisabled()416 static mozilla::LauncherVoidResult TestImageTimestampChangeWhenDisabled() {
417   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
418   if (vr.isErr()) {
419     return vr;
420   }
421   vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
422   if (vr.isErr()) {
423     return vr;
424   }
425   vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, 0ULL);
426   if (vr.isErr()) {
427     return vr;
428   }
429 
430   mozilla::LauncherRegistryInfo info;
431   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
432   EXPECT_COMMIT_IS_OK();
433 
434   EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
435   EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
436   EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0);
437 
438   EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
439 
440   return mozilla::Ok();
441 }
442 
TestDisableDueToFailure()443 static mozilla::LauncherVoidResult TestDisableDueToFailure() {
444   mozilla::LauncherVoidResult vr = SetupEnabledScenario();
445   if (vr.isErr()) {
446     return vr;
447   }
448 
449   // Check that we are indeed enabled.
450   mozilla::LauncherRegistryInfo info;
451   EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
452 
453   // Now call DisableDueToFailure
454   mozilla::LauncherVoidResult lvr = info.DisableDueToFailure();
455   if (lvr.isErr()) {
456     return lvr.propagateErr();
457   }
458 
459   // We should now be FailDisabled
460   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
461 
462   // If we delete the launcher timestamp, IsEnabled should then return
463   // ForceDisabled.
464   vr = DeleteRegistryValueData(gLauncherValue);
465   if (vr.isErr()) {
466     return vr;
467   }
468   EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
469 
470   return mozilla::Ok();
471 }
472 
TestPrefReflection()473 static mozilla::LauncherVoidResult TestPrefReflection() {
474   // Reset the registry to a known good state.
475   mozilla::LauncherVoidResult vr = SetupEnabledScenario();
476   if (vr.isErr()) {
477     return vr;
478   }
479 
480   // Let's see what happens when we flip the pref to OFF.
481   mozilla::LauncherRegistryInfo info;
482   mozilla::LauncherVoidResult reflectOk = info.ReflectPrefToRegistry(false);
483   if (reflectOk.isErr()) {
484     return reflectOk.propagateErr();
485   }
486 
487   // Launcher timestamp should be non-existent.
488   EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
489   // Browser timestamp should be zero
490   EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL);
491   // IsEnabled should give us ForceDisabled
492   EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
493 
494   // Now test to see what happens when the pref is set to ON.
495   reflectOk = info.ReflectPrefToRegistry(true);
496   if (reflectOk.isErr()) {
497     return reflectOk.propagateErr();
498   }
499 
500   // Launcher and browser timestamps should be non-existent.
501   EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue);
502   EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue);
503 
504   // IsEnabled should give us Enabled.
505   EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
506 
507   return mozilla::Ok();
508 }
509 
TestTelemetryConfig()510 static mozilla::LauncherVoidResult TestTelemetryConfig() {
511   mozilla::LauncherVoidResult vr = DeleteAllRegstryValues();
512   if (vr.isErr()) {
513     return vr;
514   }
515 
516   mozilla::LauncherRegistryInfo info;
517   EXPECT_TELEMETRY_IS_ENABLED(false);
518 
519   mozilla::LauncherVoidResult reflectOk =
520       info.ReflectTelemetryPrefToRegistry(false);
521   if (reflectOk.isErr()) {
522     return reflectOk.propagateErr();
523   }
524   EXPECT_TELEMETRY_IS_ENABLED(false);
525 
526   reflectOk = info.ReflectTelemetryPrefToRegistry(true);
527   if (reflectOk.isErr()) {
528     return reflectOk.propagateErr();
529   }
530   EXPECT_TELEMETRY_IS_ENABLED(true);
531 
532   return mozilla::Ok();
533 }
534 
TestCommitAbort()535 static mozilla::LauncherVoidResult TestCommitAbort() {
536   mozilla::LauncherVoidResult vr = SetupEnabledScenario();
537   if (vr.isErr()) {
538     return vr;
539   }
540 
541   // Retrieve the current timestamps to compare later
542   mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherValue =
543       ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD);
544   if (launcherValue.isErr() || launcherValue.inspect().isNothing()) {
545     return launcherValue.propagateErr();
546   }
547   mozilla::LauncherResult<mozilla::Maybe<uint64_t>> browserValue =
548       ReadRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD);
549   if (browserValue.isErr() || browserValue.inspect().isNothing()) {
550     return browserValue.propagateErr();
551   }
552   uint64_t launcherTs = launcherValue.inspect().value();
553   uint64_t browserTs = browserValue.inspect().value();
554 
555   vr = []() -> mozilla::LauncherVoidResult {
556     mozilla::LauncherRegistryInfo info;
557     EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
558     // No commit
559     return mozilla::Ok();
560   }();
561   if (vr.isErr()) {
562     return vr;
563   }
564 
565   // Exiting the scope discards the change.
566   mozilla::LauncherRegistryInfo info;
567   EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
568   EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
569   EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs);
570 
571   // Commit -> Check -> Abort -> Commit
572   EXPECT_COMMIT_IS_OK();
573   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
574   info.Abort();
575   EXPECT_COMMIT_IS_OK();
576 
577   // Nothing is changed.
578   EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp);
579   EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs);
580   EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs);
581   EXPECT_ENABLED_STATE_IS(EnabledState::Enabled);
582 
583   return mozilla::Ok();
584 }
585 
TestDisableDuringLauncherLaunch()586 static mozilla::LauncherVoidResult TestDisableDuringLauncherLaunch() {
587   mozilla::LauncherVoidResult vr = SetupEnabledScenario();
588   if (vr.isErr()) {
589     return vr;
590   }
591 
592   mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherTs =
593       ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD);
594   if (launcherTs.isErr()) {
595     return launcherTs.propagateErr();
596   }
597   if (launcherTs.inspect().isNothing()) {
598     return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED);
599   }
600 
601   vr = []() -> mozilla::LauncherVoidResult {
602     mozilla::LauncherRegistryInfo info;
603     EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
604 
605     // Call DisableDueToFailure with a different instance
606     mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult {
607       mozilla::LauncherRegistryInfo info;
608       mozilla::LauncherVoidResult vr = info.DisableDueToFailure();
609       if (vr.isErr()) {
610         return vr.propagateErr();
611       }
612       return mozilla::Ok();
613     }();
614     if (vr.isErr()) {
615       return vr;
616     }
617 
618     // Commit after disable.
619     EXPECT_COMMIT_IS_OK();
620 
621     return mozilla::Ok();
622   }();
623   if (vr.isErr()) {
624     return vr;
625   }
626 
627   // Make sure we're still FailDisabled and the launcher's timestamp is not
628   // updated
629   mozilla::LauncherRegistryInfo info;
630   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
631   EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs.inspect().value());
632 
633   return mozilla::Ok();
634 }
635 
TestDisableDuringBrowserLaunch()636 static mozilla::LauncherVoidResult TestDisableDuringBrowserLaunch() {
637   mozilla::LauncherVoidResult vr = SetupEnabledScenario();
638   if (vr.isErr()) {
639     return vr;
640   }
641 
642   mozilla::LauncherRegistryInfo info;
643   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
644   EXPECT_COMMIT_IS_OK();
645 
646   vr = []() -> mozilla::LauncherVoidResult {
647     mozilla::LauncherRegistryInfo info;
648     EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser);
649 
650     // Call DisableDueToFailure with a different instance
651     mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult {
652       mozilla::LauncherRegistryInfo info;
653       mozilla::LauncherVoidResult vr = info.DisableDueToFailure();
654       if (vr.isErr()) {
655         return vr.propagateErr();
656       }
657       return mozilla::Ok();
658     }();
659     if (vr.isErr()) {
660       return vr;
661     }
662 
663     // Commit after disable.
664     EXPECT_COMMIT_IS_OK();
665 
666     return mozilla::Ok();
667   }();
668   if (vr.isErr()) {
669     return vr;
670   }
671 
672   // Make sure we're still FailDisabled
673   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
674 
675   return mozilla::Ok();
676 }
677 
TestReEnable()678 static mozilla::LauncherVoidResult TestReEnable() {
679   mozilla::LauncherVoidResult vr = SetupEnabledScenario();
680   if (vr.isErr()) {
681     return vr;
682   }
683 
684   // Make FailDisabled
685   mozilla::LauncherRegistryInfo info;
686   vr = info.DisableDueToFailure();
687   if (vr.isErr()) {
688     return vr.propagateErr();
689   }
690   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
691 
692   // Attempt to launch when FailDisabled: Still be FailDisabled
693   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
694   EXPECT_COMMIT_IS_OK();
695   EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled);
696 
697   // Change the timestamp
698   vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
699   if (vr.isErr()) {
700     return vr;
701   }
702 
703   // Attempt to launch again: Launcher comes back
704   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher);
705   EXPECT_COMMIT_IS_OK();
706 
707   // Make ForceDisabled
708   vr = info.ReflectPrefToRegistry(false);
709   if (vr.isErr()) {
710     return vr.propagateErr();
711   }
712   EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
713 
714   // Attempt to launch when ForceDisabled: Still be ForceDisabled
715   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
716   EXPECT_COMMIT_IS_OK();
717   EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
718 
719   // Change the timestamp
720   vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678);
721   if (vr.isErr()) {
722     return vr;
723   }
724 
725   // Attempt to launch again: Still be ForceDisabled
726   EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser);
727   EXPECT_COMMIT_IS_OK();
728   EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled);
729 
730   return mozilla::Ok();
731 }
732 
main(int argc,char * argv[])733 int main(int argc, char* argv[]) {
734   auto fullPath = mozilla::GetFullBinaryPath();
735   if (!fullPath) {
736     return 1;
737   }
738 
739   // Global setup for all tests
740   gBrowserValue = fullPath.get();
741   gBrowserValue += kBrowserSuffix;
742 
743   gLauncherValue = fullPath.get();
744   gLauncherValue += kLauncherSuffix;
745 
746   gImageValue = fullPath.get();
747   gImageValue += kImageSuffix;
748 
749   gTelemetryValue = fullPath.get();
750   gTelemetryValue += kTelemetrySuffix;
751 
752   mozilla::LauncherResult<DWORD> timestamp = 0;
753   RUN_TEST(timestamp, GetCurrentImageTimestamp);
754   gMyImageTimestamp = timestamp.unwrap();
755 
756   auto onExit = mozilla::MakeScopeExit(
757       []() { mozilla::Unused << DeleteAllRegstryValues(); });
758 
759   mozilla::LauncherVoidResult vr = mozilla::Ok();
760 
761   // All testcases should call SetupEnabledScenario() or
762   // DeleteAllRegstryValues() to be order-independent
763   RUN_TEST(vr, TestEmptyRegistry);
764   RUN_TEST(vr, TestNormal);
765   RUN_TEST(vr, TestBrowserNoLauncher);
766   RUN_TEST(vr, TestLauncherNoBrowser);
767   RUN_TEST(vr, TestBrowserLessThanLauncher);
768   RUN_TEST(vr, TestImageTimestampChange);
769   RUN_TEST(vr, TestImageTimestampChangeWhenDisabled);
770   RUN_TEST(vr, TestDisableDueToFailure);
771   RUN_TEST(vr, TestPrefReflection);
772   RUN_TEST(vr, TestTelemetryConfig);
773   RUN_TEST(vr, TestCommitAbort);
774   RUN_TEST(vr, TestDisableDuringLauncherLaunch);
775   RUN_TEST(vr, TestDisableDuringBrowserLaunch);
776   RUN_TEST(vr, TestReEnable);
777 
778   return 0;
779 }
780