public async Task WaitsForChromaSdkDeviceAccessEvent()
        {
            using var cpl = ColoreProviderLock.GetLock();

            var chromaApi = new Mock <IChromaApi>()
            {
                DefaultValue = DefaultValue.Mock
            };

            using var cf = new WinChromaFactory
                  {
                      ChromaApi     = chromaApi.Object,
                      ChromaAppInfo = null,
                  };

            Assert.Equal(TimeSpan.Zero, cf.WarmupDelay);

            var cw = GetChromaWindowInstance(cf);

            var tcs = new TaskCompletionSource <IntPtr>();

            chromaApi
            .Setup(x => x.RegisterEventNotifications(It.IsAny <IntPtr>()))
            .Callback((IntPtr hWnd) => tcs.SetResult(hWnd));

            var tChroma = cf.CreateAsync();

            var hWnd = await tcs.Task.ConfigureAwait(false);

            Assert.Equal(cw.Handle, hWnd);

            // Reference: https://assets.razerzone.com/dev_portal/C%2B%2B/html/en/_rz_chroma_s_d_k_8h.html#afc89b7127b37c6448277a2334b1e34db
            const int deviceAccess  = 2;
            const int grantedAccess = 1;

            SendChromaEventMessage(cw, deviceAccess, grantedAccess);

            var chroma = await tChroma.ConfigureAwait(false);

            Assert.NotNull(chroma);

            await chroma.UninitializeAsync().ConfigureAwait(false);
        }
        public void RazerChromaApiGetsCalledOnGameEvents()
        {
            const string statusFile  = "Status.json";
            const string journalFile = "Journal.190101020000.01.log";

            using var cpl = ColoreProviderLock.GetLock();

            var chromaApi = new Mock <IChromaApi> {
                DefaultValue = DefaultValue.Mock
            };
            var mockIA   = chromaApi.Setup(x => x.InitializeAsync(It.IsAny <AppInfo>()));
            var mockCKEA = chromaApi.Setup(x => x.CreateKeyboardEffectAsync(It.IsAny <KeyboardEffectType>(), It.IsAny <It.IsValueType>()));
            var mockUA   = chromaApi.Setup(x => x.UninitializeAsync());

            using TestFolder
                  dirRoot    = new TestFolder(_gameRootFolder),
                  dirOpts    = new TestFolder(_gameOptionsFolder),
                  dirJournal = new TestFolder();

            dirJournal.WriteText(statusFile, EventSequence.BuildEvent("Status", new { Flags = 0 }));
            dirJournal.WriteText(journalFile, EventSequence.BuildEvent("Fileheader", new { part = 1, language = @"English\UK", gameversion = "3.5.0.200 EDH", build = "r210198/r0 " }));

            using var cc = new ChromaController(dirRoot.Name, dirOpts.Name, dirJournal.Name)
                  {
                      ChromaFactory = new ChromaFactory
                      {
                          ChromaApi     = chromaApi.Object,
                          ChromaAppInfo = null,
                      },
                      AnimationFrameRate     = 0,
                      DetectGameInForeground = false,
                  };

            Assert.False(cc.DetectGameInForeground);

            using var ceIA = new CountdownEvent(1);
            mockIA.Callback(() => ceIA.Signal());

            using var ceCKEA = new CountdownEvent(1);
            mockCKEA.Callback(() => ceCKEA.Signal());

            using var ceUA = new CountdownEvent(1);
            mockUA.Callback(() => ceUA.Signal());

            cc.Start();

            Assert.True(ceIA.Wait(1000));
            Assert.True(ceCKEA.Wait(1000));

            var seq = BuildEventSequence();

            ceCKEA.Reset(seq.Count(x => x.ChangesGameState));

            seq.Play(dirJournal, journalFile, statusFile);

            Assert.True(ceCKEA.Wait(200 * seq.Count));

            cc.Stop();

            Assert.True(ceUA.Wait(1000));
        }