public async Task TestDefaultAppFlowImplementation() { var tracker = new MyAppFlowTracker2(new InMemoryKeyValueStore().GetTypeAdapter <AppFlowEvent>()); AppFlow.AddAppFlowTracker(tracker); Log.MethodEntered(); // This will internally notify the AppFlow instance Assert.NotEmpty(await tracker.store.GetAllKeys()); var s = StopwatchV2.StartNewV2(); for (int i = 0; i < 20; i++) { await TaskV2.Delay(500); if ((await tracker.store.GetAllKeys()).Count() == 0) { break; } } Assert.True(s.ElapsedMilliseconds < 5000, "s.ElapsedMilliseconds=" + s.ElapsedMilliseconds); Assert.Empty(await tracker.store.GetAllKeys()); // Make sure the DefaultAppFlowImpl itself does not create more events while sending the existing ones: for (int i = 0; i < 10; i++) { await TaskV2.Delay(100); var c1 = tracker.eventsThatWereSent.Count; await TaskV2.Delay(100); var c2 = tracker.eventsThatWereSent.Count; Assert.Equal(c1, c2); } }
public void TestAppFlowTracking() { AppFlow.AddAppFlowTracker(new MyAppFlowTracker1()); Log.MethodEntered(); // This will internally notify the AppFlow instance MyAppFlowTracker1 t = AppFlow.GetAllOfType <MyAppFlowTracker1>().First(); Assert.True(t.wasCalledByTestAppFlowTrackingTest); }
private static LocalAnalytics CreateLocalAnalyticsSystem() { LocalAnalytics analytics = new LocalAnalytics(new InMemoryKeyValueStore()); analytics.createStoreFor = (_) => new InMemoryKeyValueStore().GetTypeAdapter <AppFlowEvent>(); // Setup the AppFlow logic to use the LocalAnalytics system: AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics)); return(analytics); }
public static void Setup(DefaultFeatureFlagStore featureFlagStore) { IoC.inject.SetSingleton(new FeatureFlagManager(featureFlagStore)); LocalAnalytics analytics = new LocalAnalytics(); AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics)); var xpSystem = new DefaultProgressionSystem(analytics); IoC.inject.SetSingleton <ProgressionSystem>(xpSystem); }
[Fact] // Event tracking should by default not interrupt the normal application flow: public void TestErrorInTrackEvent() { var tracker = new AppFlowThatThrowsErrors(); AppFlow.AddAppFlowTracker(tracker); // Even though TrackEvent does not work correctly the application flow is not interrupted: AppFlow.TrackEvent("category 1", "action 1"); IoC.inject.Get <IAppFlow>(this).TrackEvent("category 1", "action 2"); // Directly using the tracker would not protect against the inner exception: Assert.Throws <InvalidOperationException>(() => tracker.TrackEvent("category 1", "action 3")); }
public override IEnumerator RunTest() { AppFlow.AddAppFlowTracker(new AppFlowToLog().WithAllTrackingActive()); var links = gameObject.GetLinkMap(); links.Get <Button>("OptionsButton").SetOnClickAction(delegate { gameObject.GetViewStack().ShowView("ExampleUi2_OptionsScreen", gameObject); }); links.Get <Button>("UserDetailsButton").SetOnClickAction(ShowUserUi); yield return(null); }
public static async Task <ProgressionSystem <FeatureFlag> > Setup(KeyValueStoreTypeAdapter <FeatureFlag> featureFlagStore, LocalAnalytics analytics) { var ffm = new FeatureFlagManager <FeatureFlag>(featureFlagStore); IoC.inject.SetSingleton(ffm); AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics).WithBasicTrackingActive()); var xpSystem = new ProgressionSystem <FeatureFlag>(analytics, ffm); IoC.inject.SetSingleton <IProgressionSystem <FeatureFlag> >(xpSystem); await xpSystem.UpdateCurrentCategoryCounts(); return(xpSystem); }
public async Task TestDefaultProgressionSystem() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM contains the sheetId: var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var googleSheetsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var ffm = new FeatureFlagManager <FeatureFlag>(new FeatureFlagStore(new InMemoryKeyValueStore(), googleSheetsStore)); IoC.inject.SetSingleton <FeatureFlagManager <FeatureFlag> >(ffm); LocalAnalytics analytics = new LocalAnalytics(); AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics)); var xpSystem = new ProgressionSystem <FeatureFlag>(analytics, ffm); IoC.inject.SetSingleton <IProgressionSystem <FeatureFlag> >(xpSystem); await CleanupFilesFromTest(analytics, xpSystem); // Simulate User progression by causing analytics events: var eventCount = 1000; for (int i = 0; i < eventCount; i++) { AppFlow.TrackEvent(EventConsts.catMutation, "User did mutation nr " + i); } var flagId4 = "MyFlag4"; var flag4 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(flagId4); Assert.Equal(1000, flag4.requiredXp); // The user needs >= 1000 XP for the feature // Now that the user has 1000 XP the condition of the TestXpSystem is met: Assert.True(await FeatureFlag.IsEnabled(flagId4)); // The number of mutation events: Assert.Equal(eventCount, xpSystem.cachedCategoryCounts[EventConsts.catMutation]); // Since there are only mutation events the XP is equal to the factor*event count: Assert.Equal(await xpSystem.GetLatestXp(), eventCount * xpSystem.xpFactors[EventConsts.catMutation]); await CleanupFilesFromTest(analytics, xpSystem); }
public override IEnumerator RunTest() { var testTracker = new TestAppFlowTracker(); testTracker.WithAllTrackingActive(); AppFlow.AddAppFlowTracker(testTracker); setupImmutableDatastore(); // In setupImmutableDatastore() Log.MethodEntered is used, so there must be a recorded method: AssertV2.AreEqual(1, testTracker.recordedEvents.Count(x => x.category == EventConsts.catMethod)); // In setupImmutableDatastore() the datastore will be set as a singleton: AssertV2.AreEqual(1, testTracker.recordedEvents.Count(x => x.action.Contains("DataStore"))); var store = MyDataModel.GetStore(); AssertV2.IsNotNull(store, "store"); store.Dispatch(new ActionSetBool1() { newB = true }); store.Dispatch(new ActionSetString1 { newS = "abc" }); AssertV2.AreEqual(true, store.GetState().subSection1.bool1); AssertV2.AreEqual("abc", store.GetState().subSection1.string1); // After the 2 mutations, there should be 2 mutation AppFlow events recorded: AssertV2.AreEqual(2, testTracker.recordedEvents.Count(x => x.category == EventConsts.catMutation)); var presenter = new MyDataModelPresenter(); presenter.targetView = gameObject; yield return(presenter.LoadModelIntoView(store).AsCoroutine()); // After the presenter loaded the UI there should be a load start and load end event recorded: AssertV2.AreEqual(2, testTracker.recordedEvents.Count(x => x.category == EventConsts.catPresenter)); // The MyDataModelPresenter uses a GetLinkMap() when connecting to the view: AssertV2.AreEqual(1, testTracker.recordedEvents.Count(x => x.category == EventConsts.catLinked)); yield return(new WaitForSeconds(1)); }
public async Task TestAppFlowToGoogleAnalytics() { var tracker = new GoogleAnalytics(TEST_APP_KEY, NewInMemoryStore()); AppFlow.AddAppFlowTracker(tracker); var t = Log.MethodEntered(); // This will internally notify the AppFlow instance await TaskV2.Delay(100); Log.MethodDone(t); // Check that in the store of the tracker there are now events waiting to be sent: var count1 = (await tracker.store.GetAllKeys()).Count(); Assert.True(count1 > 0); await TaskV2.Delay(3000); // Check that the events are no longer in the store (sent to Google Analytics): var count2 = (await tracker.store.GetAllKeys()).Count(); Assert.True(count2 < count1, "count2=" + count2 + ", count1=" + count1); }
public async Task TestProgressiveDisclosure() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM contains the sheetId: var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var googleSheetsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var testStore = new FeatureFlagStore(new InMemoryKeyValueStore(), googleSheetsStore); IoC.inject.SetSingleton <FeatureFlagManager <FeatureFlag> >(new FeatureFlagManager <FeatureFlag>(testStore)); // Make sure user would normally be included in the rollout: var flagId4 = "MyFlag4"; var flag4 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(flagId4); Assert.Equal(flagId4, flag4.id); Assert.Equal(100, flag4.rolloutPercentage); // There is no user progression system setup so the requiredXp value of the feature flag is ignored: Assert.True(await FeatureFlag.IsEnabled(flagId4)); Assert.True(await flag4.IsFeatureUnlocked()); // Setup progression system and check again: var xpSystem = new TestXpSystem(); IoC.inject.SetSingleton <IProgressionSystem <FeatureFlag> >(xpSystem); // Now that there is a progression system Assert.False(await flag4.IsFeatureUnlocked()); Assert.False(await FeatureFlag.IsEnabled(flagId4)); var eventCount = 1000; var store = new MutationObserverKeyValueStore().WithFallbackStore(new InMemoryKeyValueStore()); // Lets assume the users xp correlates with the number of triggered local analytics events: store.onSet = delegate { xpSystem.currentXp++; return(Task.FromResult(true)); }; // Set the store to be the target of the local analytics so that whenever any LocalAnalytics analytics = new LocalAnalytics(store); analytics.createStoreFor = (_) => new InMemoryKeyValueStore().GetTypeAdapter <AppFlowEvent>(); // Setup the AppFlow logic to use LocalAnalytics: AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics)); // Simulate User progression by causing analytics events: for (int i = 0; i < eventCount; i++) { AppFlow.TrackEvent(EventConsts.catMutation, "User did mutation nr " + i); } // Get the analtics store for category "Mutations": var mutationStore = await analytics.GetStoreForCategory(EventConsts.catMutation).GetAll(); Assert.Equal(eventCount, mutationStore.Count()); // All events so far were mutations Assert.True(eventCount <= xpSystem.currentXp); // The user received xp for each mutation Assert.Equal(1000, flag4.requiredXp); // The user needs >= 1000 xp for the feature // Now that the user has more than 1000 xp the condition of the TestXpSystem is met: Assert.True(await flag4.IsFeatureUnlocked()); Assert.True(await FeatureFlag.IsEnabled(flagId4)); }