//////////////////////////////////////////////////////////////////////////// // // Copyright 2015 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////// import XCTest import RealmSwift private func createStringObjects(_ factor: Int) -> Realm { let realm = inMemoryRealm(factor.description) try! realm.write { for _ in 0..<(1000 * factor) { realm.create(SwiftStringObject.self, value: ["a"]) realm.create(SwiftStringObject.self, value: ["b"]) } } return realm } private var smallRealm: Realm! private var mediumRealm: Realm! private var largeRealm: Realm! private let isRunningOnDevice = TARGET_IPHONE_SIMULATOR == 0 class SwiftPerformanceTests: TestCase { override class var defaultTestSuite: XCTestSuite { #if !DEBUG && os(iOS) if isRunningOnDevice { return super.defaultTestSuite } #endif return XCTestSuite(name: "SwiftPerformanceTests") } override class func setUp() { super.setUp() autoreleasepool { smallRealm = createStringObjects(1) mediumRealm = createStringObjects(5) largeRealm = createStringObjects(50) } } override class func tearDown() { smallRealm = nil mediumRealm = nil largeRealm = nil super.tearDown() } override func resetRealmState() { // Do nothing, as we need to keep our in-memory realms around between tests } override func measure(_ block: (() -> Void)) { super.measure { autoreleasepool { block() } } } override func measureMetrics(_ metrics: [XCTPerformanceMetric], automaticallyStartMeasuring: Bool, for block: () -> Void) { super.measureMetrics(metrics, automaticallyStartMeasuring: automaticallyStartMeasuring) { autoreleasepool { block() } } } func inMeasureBlock(block: () -> Void) { measureMetrics(type(of: self).defaultPerformanceMetrics, automaticallyStartMeasuring: false) { _ = block() } } private func copyRealmToTestPath(_ realm: Realm) -> Realm { do { try FileManager.default.removeItem(at: testRealmURL()) } catch let error as NSError { XCTAssertTrue(error.domain == NSCocoaErrorDomain && error.code == 4) } catch { fatalError("Unexpected error: \(error)") } try! realm.writeCopy(toFile: testRealmURL()) return realmWithTestPath() } func testInsertMultiple() { inMeasureBlock { let realm = self.realmWithTestPath() self.startMeasuring() try! realm.write { for _ in 0..<5000 { let obj = SwiftStringObject() obj.stringCol = "a" realm.add(obj) } } self.stopMeasuring() self.tearDown() } } func testInsertSingleLiteral() { inMeasureBlock { let realm = self.realmWithTestPath() self.startMeasuring() for _ in 0..<50 { try! realm.write { _ = realm.create(SwiftStringObject.self, value: ["a"]) } } self.stopMeasuring() self.tearDown() } } func testInsertMultipleLiteral() { inMeasureBlock { let realm = self.realmWithTestPath() self.startMeasuring() try! realm.write { for _ in 0..<5000 { realm.create(SwiftStringObject.self, value: ["a"]) } } self.stopMeasuring() self.tearDown() } } func testCountWhereQuery() { let realm = copyRealmToTestPath(largeRealm) measure { for _ in 0..<50 { let results = realm.objects(SwiftStringObject.self).filter("stringCol = 'a'") _ = results.count } } } func testCountWhereTableView() { let realm = copyRealmToTestPath(mediumRealm) measure { for _ in 0..<50 { let results = realm.objects(SwiftStringObject.self).filter("stringCol = 'a'") _ = results.first _ = results.count } } } func testEnumerateAndAccessQuery() { let realm = copyRealmToTestPath(largeRealm) measure { for stringObject in realm.objects(SwiftStringObject.self).filter("stringCol = 'a'") { _ = stringObject.stringCol } } } func testEnumerateAndAccessAll() { let realm = copyRealmToTestPath(largeRealm) measure { for stringObject in realm.objects(SwiftStringObject.self) { _ = stringObject.stringCol } } } func testEnumerateAndAccessAllSlow() { let realm = copyRealmToTestPath(largeRealm) measure { let results = realm.objects(SwiftStringObject.self) for i in 0..=4.2) RunLoop.current.run(mode: RunLoop.Mode.default, before: Date.distantFuture) #else RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: Date.distantFuture) #endif } queue.sync {} self.stopMeasuring() token.invalidate() } } func testValueForKeyForListObjects() { let realm = try! Realm() try! realm.write { for value in 0..<10000 { let listObject = SwiftListOfSwiftObject() let object = SwiftObject() object.intCol = value object.stringCol = String(value) listObject.array.append(object) realm.add(listObject) } } let objects = realm.objects(SwiftListOfSwiftObject.self) measure { _ = objects.value(forKeyPath: "array") as! [List] } } func testValueForKeyForIntObjects() { let realm = try! Realm() try! realm.write { for value in 0..<10000 { autoreleasepool { let object = SwiftObject() object.intCol = value realm.add(object) } } } let objects = realm.objects(SwiftObject.self) measure { _ = objects.value(forKeyPath: "intCol") as! [Int] } } func testValueForKeyForStringObjects() { let realm = try! Realm() try! realm.write { for value in 0..<10000 { autoreleasepool { let object = SwiftObject() object.stringCol = String(value) realm.add(object) } } } let objects = realm.objects(SwiftObject.self) measure { _ = objects.value(forKeyPath: "stringCol") as! [String] } } func testValueForKeyForOptionalIntObjects() { let realm = try! Realm() try! realm.write { for value in 0..<10000 { autoreleasepool { let object = SwiftOptionalObject() object.optIntCol.value = value realm.add(object) } } } let objects = realm.objects(SwiftOptionalObject.self) measure { _ = objects.value(forKeyPath: "optIntCol") as! [Int] } } func testValueForKeyForOptionalStringObjects() { let realm = try! Realm() try! realm.write { for value in 0..<10000 { autoreleasepool { let object = SwiftOptionalObject() object.optStringCol = String(value) realm.add(object) } } } let objects = realm.objects(SwiftOptionalObject.self) measure { _ = objects.value(forKeyPath: "optStringCol") as! [String] } } }