1 // Copyright (c) 2020 650 Industries, Inc. All rights reserved. 2 3 import ExpoModulesTestCore 4 5 @testable import EXUpdates 6 7 import EXManifests 8 9 class ReaperSelectionPolicyDevelopmentClientSpec : ExpoSpec { specnull10 override func spec() { 11 var update1: Update! 12 var update2: Update! 13 var update3: Update! 14 var update4: Update! 15 var update5: Update! 16 var selectionPolicy: ReaperSelectionPolicy! 17 18 beforeEach { 19 let runtimeVersion = "1.0" 20 let database = UpdatesDatabase() 21 22 // test updates with different scopes to ensure this policy ignores scopes 23 update1 = Update( 24 manifest: ManifestFactory.manifest(forManifestJSON: [:]), 25 config: UpdatesConfig.config(fromDictionary: [ 26 UpdatesConfig.EXUpdatesConfigScopeKeyKey: "scope1" 27 ]), 28 database: database, 29 updateId: UUID(), 30 scopeKey: "scope1", 31 commitTime: Date(timeIntervalSince1970: 1608667851), 32 runtimeVersion: runtimeVersion, 33 keep: true, 34 status: .StatusReady, 35 isDevelopmentMode: false, 36 assetsFromManifest: [] 37 ) 38 update2 = Update( 39 manifest: ManifestFactory.manifest(forManifestJSON: [:]), 40 config: UpdatesConfig.config(fromDictionary: [ 41 UpdatesConfig.EXUpdatesConfigScopeKeyKey: "scope2" 42 ]), 43 database: database, 44 updateId: UUID(), 45 scopeKey: "scope2", 46 commitTime: Date(timeIntervalSince1970: 1608667852), 47 runtimeVersion: runtimeVersion, 48 keep: true, 49 status: .StatusReady, 50 isDevelopmentMode: false, 51 assetsFromManifest: [] 52 ) 53 update3 = Update( 54 manifest: ManifestFactory.manifest(forManifestJSON: [:]), 55 config: UpdatesConfig.config(fromDictionary: [ 56 UpdatesConfig.EXUpdatesConfigScopeKeyKey: "scope3" 57 ]), 58 database: database, 59 updateId: UUID(), 60 scopeKey: "scope3", 61 commitTime: Date(timeIntervalSince1970: 1608667853), 62 runtimeVersion: runtimeVersion, 63 keep: true, 64 status: .StatusReady, 65 isDevelopmentMode: false, 66 assetsFromManifest: [] 67 ) 68 update4 = Update( 69 manifest: ManifestFactory.manifest(forManifestJSON: [:]), 70 config: UpdatesConfig.config(fromDictionary: [ 71 UpdatesConfig.EXUpdatesConfigScopeKeyKey: "scope4" 72 ]), 73 database: database, 74 updateId: UUID(), 75 scopeKey: "scope4", 76 commitTime: Date(timeIntervalSince1970: 1608667854), 77 runtimeVersion: runtimeVersion, 78 keep: true, 79 status: .StatusReady, 80 isDevelopmentMode: false, 81 assetsFromManifest: [] 82 ) 83 update5 = Update( 84 manifest: ManifestFactory.manifest(forManifestJSON: [:]), 85 config: UpdatesConfig.config(fromDictionary: [ 86 UpdatesConfig.EXUpdatesConfigScopeKeyKey: "scope5" 87 ]), 88 database: database, 89 updateId: UUID(), 90 scopeKey: "scope5", 91 commitTime: Date(timeIntervalSince1970: 1608667855), 92 runtimeVersion: runtimeVersion, 93 keep: true, 94 status: .StatusReady, 95 isDevelopmentMode: false, 96 assetsFromManifest: [] 97 ) 98 99 // for readability/writability, test with a policy that keeps only 3 updates; 100 // the actual functionality is independent of the number 101 selectionPolicy = ReaperSelectionPolicyDevelopmentClient.init(maxUpdatesToKeep: 3) 102 } 103 104 describe("updates to delete") { 105 it("basic case") { 106 update1.lastAccessed = Date(timeIntervalSince1970: 1619569811) 107 update2.lastAccessed = Date(timeIntervalSince1970: 1619569812) 108 update3.lastAccessed = Date(timeIntervalSince1970: 1619569813) 109 update4.lastAccessed = Date(timeIntervalSince1970: 1619569814) 110 update5.lastAccessed = Date(timeIntervalSince1970: 1619569815) 111 112 // the order of the array shouldn't matter 113 let updatesToDelete = selectionPolicy.updatesToDelete(withLaunchedUpdate: update5, updates: [update2, update5, update4, update1, update3], filters: nil) 114 expect(updatesToDelete.count) == 2 115 expect(updatesToDelete.contains(update1)) == true 116 expect(updatesToDelete.contains(update2)) == true 117 } 118 119 it("same last accessed date") { 120 // if multiple updates have the same lastAccessed date, should use commitTime to determine 121 // which updates to delete 122 update1.lastAccessed = Date(timeIntervalSince1970: 1619569810) 123 update2.lastAccessed = Date(timeIntervalSince1970: 1619569810) 124 update3.lastAccessed = Date(timeIntervalSince1970: 1619569810) 125 update4.lastAccessed = Date(timeIntervalSince1970: 1619569810) 126 127 let updatesToDelete = selectionPolicy.updatesToDelete(withLaunchedUpdate: update4, updates: [update3, update4, update1, update2], filters: nil) 128 expect(updatesToDelete.count) == 1 129 expect(updatesToDelete.contains(update1)) == true 130 } 131 132 it("launched update is oldest") { 133 // if the least recently accessed update happens to be launchedUpdate, delete instead the next 134 // least recently accessed update 135 update1.lastAccessed = Date(timeIntervalSince1970: 1619569811) 136 update2.lastAccessed = Date(timeIntervalSince1970: 1619569812) 137 update3.lastAccessed = Date(timeIntervalSince1970: 1619569813) 138 update4.lastAccessed = Date(timeIntervalSince1970: 1619569814) 139 140 let updatesToDelete = selectionPolicy.updatesToDelete(withLaunchedUpdate: update1, updates: [update1, update2, update3, update4], filters: nil) 141 expect(updatesToDelete.count) == 1 142 expect(updatesToDelete.contains(update2)) == true 143 } 144 145 it("below max number") { 146 // no need to delete any updates if we have <= the max number of updates 147 let updatesToDeleteWith2Total = selectionPolicy.updatesToDelete(withLaunchedUpdate: update2, updates: [update1, update2], filters: nil) 148 let updatesToDeleteWith3Total = selectionPolicy.updatesToDelete(withLaunchedUpdate: update3, updates: [update1, update2, update3], filters: nil) 149 expect(updatesToDeleteWith2Total.count) == 0 150 expect(updatesToDeleteWith3Total.count) == 0 151 } 152 } 153 } 154 } 155