1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <algorithm> 6 7 #include "base/rand_util.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "base/time/time.h" 10 #include "chrome/browser/apps/ephemeral_app_service.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 13 namespace { 14 15 // Generate a time N number of days before the reference time. 16 // The generated time can be randomized. 17 base::Time GetPastTime(const base::Time& reference_time, 18 int days_before, 19 bool randomize_time = false) { 20 base::Time generated_time = 21 reference_time - base::TimeDelta::FromDays(days_before); 22 23 // Add an hour so that the time is well within the number of days before. 24 generated_time += base::TimeDelta::FromHours(1); 25 26 // Add a random number of seconds between 0 - 10 hours. 27 if (randomize_time) 28 generated_time += base::TimeDelta::FromSeconds(base::RandInt(0, 36000)); 29 30 return generated_time; 31 } 32 33 } // namespace 34 35 class EphemeralAppServiceTest : public testing::Test { 36 protected: 37 typedef EphemeralAppService::LaunchTimeAppMap LaunchTimeAppMap; 38 39 EphemeralAppServiceTest() {} 40 virtual ~EphemeralAppServiceTest() {} 41 42 void RunTest(int ephemeral_app_count, 43 const LaunchTimeAppMap& launch_times, 44 const std::set<std::string>& expected_removed_ids) { 45 std::set<std::string> remove_app_ids; 46 EphemeralAppService::GetAppsToRemove(ephemeral_app_count, 47 launch_times, 48 &remove_app_ids); 49 EXPECT_EQ(expected_removed_ids, remove_app_ids); 50 } 51 52 void RunTestCheckLRU(int ephemeral_app_count, 53 LaunchTimeAppMap& launch_times, 54 int expected_removed_count) { 55 std::set<std::string> remove_app_ids; 56 EphemeralAppService::GetAppsToRemove(ephemeral_app_count, 57 launch_times, 58 &remove_app_ids); 59 EXPECT_EQ(expected_removed_count, (int) remove_app_ids.size()); 60 61 // Move the launch times of removed apps to another map. 62 LaunchTimeAppMap removed_apps; 63 for (LaunchTimeAppMap::iterator it = launch_times.begin(); 64 it != launch_times.end(); ) { 65 if (remove_app_ids.find(it->second) != remove_app_ids.end()) { 66 removed_apps.insert(*it); 67 launch_times.erase(it++); 68 } else { 69 ++it; 70 } 71 } 72 73 if (launch_times.empty()) 74 return; 75 76 // Verify that the removed apps have launch times earlier than or equal to 77 // all retained apps. We can actually just compare with the first entry in 78 // |launch_times| but will make no implementation assumptions. 79 for (LaunchTimeAppMap::const_iterator removed = removed_apps.begin(); 80 removed != removed_apps.end(); ++removed) { 81 for (LaunchTimeAppMap::iterator retained = launch_times.begin(); 82 retained != launch_times.end(); ++retained) { 83 EXPECT_LE(removed->first, retained->first); 84 } 85 } 86 } 87 88 // Generate X app launch times, N days before the reference time. 89 // If |generated_ids| is not NULL, the generated app IDs will be added 90 // to the set. 91 void GenerateLaunchTimes(const base::Time& reference_time, 92 int days_before, 93 int count, 94 LaunchTimeAppMap* launch_times, 95 std::set<std::string>* generated_ids = NULL) { 96 for (int i = 0; i < count; ++i) { 97 std::string app_id = base::IntToString(launch_times->size()); 98 launch_times->insert(std::make_pair( 99 GetPastTime(reference_time, days_before, true), 100 app_id)); 101 102 if (generated_ids) 103 generated_ids->insert(app_id); 104 } 105 } 106 107 // Add inactive apps that should always be removed by garbage collection. 108 void AddInactiveApps(const base::Time& reference_time, 109 int count, 110 LaunchTimeAppMap* launch_times, 111 std::set<std::string>* generated_ids = NULL) { 112 GenerateLaunchTimes(reference_time, 113 EphemeralAppService::kAppInactiveThreshold + 1, 114 count, 115 launch_times, 116 generated_ids); 117 } 118 119 // Add recently launched apps that should NOT be removed by garbage 120 // collection regardless of the number of cached ephemeral apps. 121 void AddRecentlyLaunchedApps(const base::Time& reference_time, 122 int count, 123 LaunchTimeAppMap* launch_times, 124 std::set<std::string>* generated_ids = NULL) { 125 GenerateLaunchTimes(reference_time, 126 EphemeralAppService::kAppKeepThreshold, 127 count, 128 launch_times, 129 generated_ids); 130 } 131 132 // Add apps launched between the kAppInactiveThreshold and kAppKeepThreshold, 133 // which may or may not be removed by garbage collection depending on the 134 // number of ephemeral apps in the cache. 135 void AddIntermediateApps(const base::Time& reference_time, 136 int count, 137 LaunchTimeAppMap* launch_times, 138 std::set<std::string>* generated_ids = NULL) { 139 int days_before = base::RandInt(EphemeralAppService::kAppKeepThreshold + 1, 140 EphemeralAppService::kAppInactiveThreshold); 141 GenerateLaunchTimes(reference_time, 142 days_before, 143 count, 144 launch_times, 145 generated_ids); 146 } 147 }; 148 149 // Verify that inactive apps are removed even if the cache has not reached 150 // capacity. 151 // Test case: | inactive | 152 // Expected output: All inactive apps removed. 153 TEST_F(EphemeralAppServiceTest, RemoveInactiveApps) { 154 base::Time time_now = base::Time::Now(); 155 LaunchTimeAppMap launch_times; 156 std::set<std::string> expected_removed_ids; 157 158 AddInactiveApps( 159 time_now, 160 EphemeralAppService::kMaxEphemeralAppsCount / 5, 161 &launch_times, 162 &expected_removed_ids); 163 RunTest(launch_times.size(), launch_times, expected_removed_ids); 164 } 165 166 // Verify that inactive apps are removed even if the cache has not reached 167 // capacity. 168 // Test case: | inactive | intermediate | recently launched | 169 // Expected output: All inactive apps removed, other apps retained. 170 TEST_F(EphemeralAppServiceTest, RemoveInactiveAppsKeepOthers) { 171 base::Time time_now = base::Time::Now(); 172 LaunchTimeAppMap launch_times; 173 std::set<std::string> expected_removed_ids; 174 175 AddInactiveApps( 176 time_now, 177 EphemeralAppService::kMaxEphemeralAppsCount / 5, 178 &launch_times, 179 &expected_removed_ids); 180 AddIntermediateApps( 181 time_now, 182 EphemeralAppService::kMaxEphemeralAppsCount / 5, 183 &launch_times); 184 AddRecentlyLaunchedApps( 185 time_now, 186 EphemeralAppService::kMaxEphemeralAppsCount / 5, 187 &launch_times); 188 RunTest(launch_times.size(), launch_times, expected_removed_ids); 189 } 190 191 // Verify that recently launched apps will not be removed, even when the cache 192 // overflows. 193 // Test case: | recently launched | 194 // Expected output: All recently launched apps retained. 195 TEST_F(EphemeralAppServiceTest, KeepRecentLaunch) { 196 base::Time time_now = base::Time::Now(); 197 LaunchTimeAppMap launch_times; 198 199 AddRecentlyLaunchedApps( 200 time_now, 201 3, 202 &launch_times); 203 RunTest(launch_times.size(), launch_times, std::set<std::string>()); 204 205 AddRecentlyLaunchedApps( 206 time_now, 207 EphemeralAppService::kMaxEphemeralAppsCount, 208 &launch_times); // overflow 209 RunTest(launch_times.size(), launch_times, std::set<std::string>()); 210 } 211 212 // Verify that recently launched apps will not be removed, even when the cache 213 // overflows. 214 // Test case: | intermediate (overflow) | recently launched (overflow) | 215 // Expected output: All recently launched apps retained, intermediate apps 216 // removed. 217 TEST_F(EphemeralAppServiceTest, KeepRecentLaunchRemoveOthers) { 218 base::Time time_now = base::Time::Now(); 219 LaunchTimeAppMap launch_times; 220 std::set<std::string> expected_removed_ids; 221 222 AddRecentlyLaunchedApps( 223 time_now, 224 EphemeralAppService::kMaxEphemeralAppsCount + 3, 225 &launch_times); // overflow 226 AddIntermediateApps( 227 time_now, 228 3, 229 &launch_times, 230 &expected_removed_ids); // overflow 231 RunTest(launch_times.size(), launch_times, expected_removed_ids); 232 } 233 234 // Verify that the LRU algorithm is implemented correctly. 235 // Test case: | intermediate (overflow) | 236 // Expected output: The least recently launched apps are removed. 237 TEST_F(EphemeralAppServiceTest, RemoveOverflow) { 238 base::Time time_now = base::Time::Now(); 239 LaunchTimeAppMap launch_times; 240 241 const int kOverflow = 3; 242 AddIntermediateApps( 243 time_now, 244 EphemeralAppService::kMaxEphemeralAppsCount + kOverflow, 245 &launch_times); // overflow 246 RunTestCheckLRU(launch_times.size(), launch_times, kOverflow); 247 } 248 249 // Verify that GetAppsToRemove() takes into account the number of running apps, 250 // since they are not included in the launch times. 251 // Test case: | intermediate (overflow) | running apps | 252 // Expected output: The least recently launched apps are removed. 253 TEST_F(EphemeralAppServiceTest, RemoveOverflowWithRunningApps) { 254 base::Time time_now = base::Time::Now(); 255 LaunchTimeAppMap launch_times; 256 257 const int kRunningApps = 3; 258 AddIntermediateApps( 259 time_now, 260 EphemeralAppService::kMaxEphemeralAppsCount, 261 &launch_times); // overflow 262 RunTestCheckLRU( 263 launch_times.size() + kRunningApps, 264 launch_times, 265 kRunningApps); 266 } 267