chromium/ios/chrome/browser/metrics/model/incognito_usage_app_state_agent_unittest.mm

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/metrics/model/incognito_usage_app_state_agent.h"

#import "base/test/metrics/histogram_tester.h"
#import "base/test/scoped_mock_clock_override.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"

@interface IncognitoUsageAppStateAgent (Testing) <AppStateObserver,
                                                  SceneStateObserver>
- (void)applicationWillTerminate;
@property(nonatomic, assign) BOOL incognitoContentVisible;
@end

class IncognitoUsageAppStateAgentTest : public PlatformTest {
 public:
  void AdvanceClock(const base::TimeDelta& delay) {
    scoped_clock_.Advance(delay);
  }

  void SetScene1ActivationLevel(SceneActivationLevel activation_level) {
    scene1_activation_level_ = activation_level;
    [incognito_agent_ sceneState:mock_scene_state1_
        transitionedToActivationLevel:activation_level];
  }

  void SetScene2ActivationLevel(SceneActivationLevel activation_level) {
    scene2_activation_level_ = activation_level;
    [incognito_agent_ sceneState:mock_scene_state2_
        transitionedToActivationLevel:activation_level];
  }

  void SetScene1DisplaysIncognito(BOOL displays_incognito) {
    scene1_displays_incognito_ = displays_incognito;
    [incognito_agent_ sceneState:mock_scene_state1_
        isDisplayingIncognitoContent:displays_incognito];
  }
  void SetScene2DisplaysIncognito(BOOL displays_incognito) {
    scene2_displays_incognito_ = displays_incognito;
    [incognito_agent_ sceneState:mock_scene_state2_
        isDisplayingIncognitoContent:displays_incognito];
  }

 protected:
  void SetUp() override {
    PlatformTest::SetUp();
    incognito_agent_ = [[IncognitoUsageAppStateAgent alloc] init];
    mock_app_state_ = OCMClassMock([AppState class]);
    mock_scene_state1_ = OCMClassMock([SceneState class]);
    mock_scene_state2_ = OCMClassMock([SceneState class]);

    NSArray* connected_scenes = @[ mock_scene_state1_, mock_scene_state2_ ];
    OCMStub([mock_app_state_ connectedScenes]).andReturn(connected_scenes);
    [incognito_agent_ setAppState:mock_app_state_];

    OCMStub([mock_scene_state1_ incognitoContentVisible])
        .andDo(^(NSInvocation* invocation) {
          [invocation setReturnValue:&scene1_displays_incognito_];
        });
    OCMStub([mock_scene_state2_ incognitoContentVisible])
        .andDo(^(NSInvocation* invocation) {
          [invocation setReturnValue:&scene2_displays_incognito_];
        });
    OCMStub([mock_scene_state1_ activationLevel])
        .andDo(^(NSInvocation* invocation) {
          [invocation setReturnValue:&scene1_activation_level_];
        });
    OCMStub([mock_scene_state2_ activationLevel])
        .andDo(^(NSInvocation* invocation) {
          [invocation setReturnValue:&scene2_activation_level_];
        });
    [incognito_agent_ appState:mock_app_state_
                sceneConnected:mock_scene_state1_];
    [incognito_agent_ appState:mock_app_state_
                sceneConnected:mock_scene_state2_];
  }

  void TearDown() override {
    SetScene1DisplaysIncognito(NO);
    SetScene2DisplaysIncognito(NO);
    PlatformTest::TearDown();
  }

  base::HistogramTester histogram_tester_;
  base::ScopedMockClockOverride scoped_clock_;
  IncognitoUsageAppStateAgent* incognito_agent_;
  id mock_app_state_;
  id mock_scene_state1_;
  id mock_scene_state2_;
  __block BOOL scene1_displays_incognito_ = NO;
  __block BOOL scene2_displays_incognito_ = NO;
  __block SceneActivationLevel scene1_activation_level_ =
      SceneActivationLevelBackground;
  __block SceneActivationLevel scene2_activation_level_ =
      SceneActivationLevelBackground;
};

// Tests metrics that a session of 1 minute is recorded
TEST_F(IncognitoUsageAppStateAgentTest, NormalIncognitoSession) {
  // One scene Foregrounded, no Incognito
  SetScene1ActivationLevel(SceneActivationLevelForegroundActive);

  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  AdvanceClock(base::Minutes(1));

  // Display one incognito for 1 minute
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);
  AdvanceClock(base::Minutes(1));

  // Back to normal
  SetScene1DisplaysIncognito(NO);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  // Metrics is still not logged.
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  // Back to incognito
  AdvanceClock(base::Minutes(1));
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  // Metrics from previous time should be logged.
  histogram_tester_.ExpectUniqueTimeSample("IOS.Incognito.TimeSpent",
                                           base::Minutes(1), 1);
}

// Tests metrics that a session of 5 seconds is not recorded
TEST_F(IncognitoUsageAppStateAgentTest, ShortIncognitoSession) {
  // One scene Foregrounded, no Incognito
  SetScene1ActivationLevel(SceneActivationLevelForegroundActive);

  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  AdvanceClock(base::Minutes(1));

  // Display one incognito for 5 seconds
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);
  AdvanceClock(base::Seconds(5));

  // Back to normal
  SetScene1DisplaysIncognito(NO);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  // Metrics is still not logged.
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  // Back to incognito
  AdvanceClock(base::Minutes(1));
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  // Metrics from previous time should not be logged.
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);
}

// Tests metrics that a short interruption of 5 seconds is not recorded.
TEST_F(IncognitoUsageAppStateAgentTest, ShortNormalSession) {
  // One scene Foregrounded, no Incognito
  SetScene1ActivationLevel(SceneActivationLevelForegroundActive);

  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  AdvanceClock(base::Minutes(1));

  // Display one incognito for 1 minute
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);
  AdvanceClock(base::Minutes(1));

  // Back to normal
  SetScene1DisplaysIncognito(NO);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  // Metrics is still not logged.
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  // Back to incognito
  AdvanceClock(base::Seconds(5));
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  // Nothing logged yet
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  AdvanceClock(base::Seconds(55));
  SetScene1DisplaysIncognito(NO);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  // Metrics is still not logged.
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  AdvanceClock(base::Minutes(1));
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  // Metrics from previous time should be logged.
  histogram_tester_.ExpectUniqueTimeSample("IOS.Incognito.TimeSpent",
                                           base::Minutes(2), 1);
}

// Tests metrics that the current incognito life time is reported.
TEST_F(IncognitoUsageAppStateAgentTest, ApplicationTerminatesInIncognito) {
  // One scene Foregrounded, no Incognito
  SetScene1ActivationLevel(SceneActivationLevelForegroundActive);

  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  AdvanceClock(base::Minutes(1));
  // Display one incognito for 1 minute
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);
  AdvanceClock(base::Minutes(1));

  [incognito_agent_ applicationWillTerminate];
  // Metrics from previous time should be logged.
  histogram_tester_.ExpectUniqueTimeSample("IOS.Incognito.TimeSpent",
                                           base::Minutes(1), 1);
}

// Tests metrics that the last incognito life time is reported.
TEST_F(IncognitoUsageAppStateAgentTest, ApplicationTerminatesInNormal) {
  // One scene Foregrounded, no Incognito
  SetScene1ActivationLevel(SceneActivationLevelForegroundActive);

  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  AdvanceClock(base::Minutes(1));
  // Display one incognito for 1 minute
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);
  AdvanceClock(base::Minutes(1));
  SetScene1DisplaysIncognito(NO);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
  histogram_tester_.ExpectTotalCount("IOS.Incognito.TimeSpent", 0);

  [incognito_agent_ applicationWillTerminate];
  // Metrics from previous time should be logged.
  histogram_tester_.ExpectUniqueTimeSample("IOS.Incognito.TimeSpent",
                                           base::Minutes(1), 1);
}

// Tests incognitoContentVisible in various scenarios.
TEST_F(IncognitoUsageAppStateAgentTest, IncognitoContentVisibleValue) {
  // Two scenes background normal.
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);

  // Incognito in background.
  SetScene1DisplaysIncognito(YES);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);

  // Foreground incognito
  SetScene1ActivationLevel(SceneActivationLevelForegroundActive);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);

  // Background it
  SetScene1ActivationLevel(SceneActivationLevelBackground);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);

  // Foreground incognito
  SetScene1ActivationLevel(SceneActivationLevelForegroundActive);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);

  // Switch to normal
  SetScene1DisplaysIncognito(NO);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);

  // Foreground second scene
  SetScene2ActivationLevel(SceneActivationLevelForegroundActive);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);

  // Incognito in foreground.
  SetScene1DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);

  // Second Incognito in foreground.
  SetScene2DisplaysIncognito(YES);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);

  // Background 1
  SetScene1ActivationLevel(SceneActivationLevelBackground);
  EXPECT_TRUE(incognito_agent_.incognitoContentVisible);

  // Switch the other to normal.
  SetScene2DisplaysIncognito(NO);
  EXPECT_FALSE(incognito_agent_.incognitoContentVisible);
}