Add polyfill for Calender's `nextDate` method on linux

Remove autoupdatingCurrent, it's unavailable on linux
This commit is contained in:
Quentin 2018-08-27 12:50:54 +08:00
parent db5109b042
commit 5daabf3701
12 changed files with 173 additions and 42 deletions

View File

@ -21,6 +21,8 @@
/* End PBXAggregateTarget section */ /* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
62AA63022133970A00A442A5 /* Calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA63012133970A00A442A5 /* Calendar.swift */; };
62AA63042133A0FB00A442A5 /* CalendarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA63032133A0FB00A442A5 /* CalendarTests.swift */; };
667D2DF72132C5390071DC89 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667D2DF62132C5390071DC89 /* DeinitObserver.swift */; }; 667D2DF72132C5390071DC89 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667D2DF62132C5390071DC89 /* DeinitObserver.swift */; };
667D2DF92132C95D0071DC89 /* DeinitObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */; }; 667D2DF92132C95D0071DC89 /* DeinitObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */; };
OBJ_45 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; }; OBJ_45 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; };
@ -69,6 +71,8 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
626D761B2113F9D100FCAFFE /* README.zh_cn.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.zh_cn.md; sourceTree = "<group>"; }; 626D761B2113F9D100FCAFFE /* README.zh_cn.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.zh_cn.md; sourceTree = "<group>"; };
62AA63012133970A00A442A5 /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.swift; sourceTree = "<group>"; };
62AA63032133A0FB00A442A5 /* CalendarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarTests.swift; sourceTree = "<group>"; };
667D2DF62132C5390071DC89 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; }; 667D2DF62132C5390071DC89 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; };
667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserverTests.swift; sourceTree = "<group>"; }; 667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserverTests.swift; sourceTree = "<group>"; };
668685ED210DCC0E009305C3 /* .swift-version */ = {isa = PBXFileReference; lastKnownFileType = text; path = ".swift-version"; sourceTree = "<group>"; }; 668685ED210DCC0E009305C3 /* .swift-version */ = {isa = PBXFileReference; lastKnownFileType = text; path = ".swift-version"; sourceTree = "<group>"; };
@ -125,6 +129,15 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
62AA6300213396CE00A442A5 /* Platforms */ = {
isa = PBXGroup;
children = (
667D2DF62132C5390071DC89 /* DeinitObserver.swift */,
62AA63012133970A00A442A5 /* Calendar.swift */,
);
name = Platforms;
sourceTree = "<group>";
};
668685EC210DCBD4009305C3 /* Related */ = { 668685EC210DCBD4009305C3 /* Related */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -146,7 +159,6 @@
OBJ_10 /* Bucket.swift */, OBJ_10 /* Bucket.swift */,
OBJ_11 /* Extensions.swift */, OBJ_11 /* Extensions.swift */,
OBJ_13 /* Lock.swift */, OBJ_13 /* Lock.swift */,
667D2DF62132C5390071DC89 /* DeinitObserver.swift */,
); );
name = Utils; name = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -168,8 +180,9 @@
children = ( children = (
OBJ_26 /* AtomicTests.swift */, OBJ_26 /* AtomicTests.swift */,
OBJ_27 /* BucketTests.swift */, OBJ_27 /* BucketTests.swift */,
OBJ_29 /* ExtensionsTests.swift */, 62AA63032133A0FB00A442A5 /* CalendarTests.swift */,
667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */, 667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */,
OBJ_29 /* ExtensionsTests.swift */,
); );
name = UtilsTests; name = UtilsTests;
sourceTree = "<group>"; sourceTree = "<group>";
@ -235,6 +248,7 @@
OBJ_19 /* TaskHub.swift */, OBJ_19 /* TaskHub.swift */,
OBJ_21 /* Timeline.swift */, OBJ_21 /* Timeline.swift */,
668685F5210DD226009305C3 /* DateTime */, 668685F5210DD226009305C3 /* DateTime */,
62AA6300213396CE00A442A5 /* Platforms */,
668685F4210DD21A009305C3 /* Utils */, 668685F4210DD21A009305C3 /* Utils */,
); );
name = Schedule; name = Schedule;
@ -345,6 +359,7 @@
OBJ_45 /* Atomic.swift in Sources */, OBJ_45 /* Atomic.swift in Sources */,
OBJ_46 /* Bucket.swift in Sources */, OBJ_46 /* Bucket.swift in Sources */,
OBJ_47 /* Extensions.swift in Sources */, OBJ_47 /* Extensions.swift in Sources */,
62AA63022133970A00A442A5 /* Calendar.swift in Sources */,
OBJ_48 /* Interval.swift in Sources */, OBJ_48 /* Interval.swift in Sources */,
OBJ_49 /* Lock.swift in Sources */, OBJ_49 /* Lock.swift in Sources */,
OBJ_50 /* Monthday.swift in Sources */, OBJ_50 /* Monthday.swift in Sources */,
@ -372,6 +387,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 0; buildActionMask = 0;
files = ( files = (
62AA63042133A0FB00A442A5 /* CalendarTests.swift in Sources */,
OBJ_77 /* AtomicTests.swift in Sources */, OBJ_77 /* AtomicTests.swift in Sources */,
OBJ_78 /* BucketTests.swift in Sources */, OBJ_78 /* BucketTests.swift in Sources */,
OBJ_79 /* DateTimeTests.swift in Sources */, OBJ_79 /* DateTimeTests.swift in Sources */,

View File

@ -0,0 +1,56 @@
//
// Calendar.swift
// Schedule
//
// Created by Quentin MED on 2018/8/27.
//
import Foundation
extension Calendar {
func next(_ weekday: Weekday, after date: Date = Date()) -> Date? {
let components = weekday.toDateComponents()
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
return nextDate(after: date, matching: components, matchingPolicy: .strict)
#elseif os(Linux)
var c = dateComponents(in: .current, from: date)
var days = components.weekday! - c.weekday!
if days <= 0 {
days += 7
}
return self.date(byAdding: .day, value: days, to: date)?.zeroClock()
#endif
}
func next(_ monthday: Monthday, after date: Date = Date()) -> Date? {
let components = monthday.toDateComponents()
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
return nextDate(after: date, matching: components, matchingPolicy: .strict)
#elseif os(Linux)
var old = dateComponents(in: .current, from: date)
var new = DateComponents(calendar: self, timeZone: .current)
new.year = old.year
new.month = components.month
new.day = components.day
if components.month! < old.month! {
new.year! += 1
} else if components.month! == old.month! {
if components.day! <= old.day! {
new.year! += 1
}
}
return self.date(from: new)
#endif
}
}

View File

@ -36,7 +36,7 @@ extension Date {
func zeroClock() -> Date { func zeroClock() -> Date {
let calendar = Calendar.gregorian let calendar = Calendar.gregorian
let timeZone = TimeZone.autoupdatingCurrent let timeZone = TimeZone.current
var dateComponents = calendar.dateComponents(in: timeZone, from: self) var dateComponents = calendar.dateComponents(in: timeZone, from: self)
dateComponents.hour = 0 dateComponents.hour = 0
dateComponents.minute = 0 dateComponents.minute = 0

View File

@ -35,7 +35,7 @@ public enum Monthday {
case december(Int) case december(Int)
var isToday: Bool { var isToday: Bool {
let lhs = Calendar.gregorian.dateComponents(in: TimeZone.autoupdatingCurrent, from: Date()) let lhs = Calendar.gregorian.dateComponents(in: TimeZone.current, from: Date())
let rhs = toDateComponents() let rhs = toDateComponents()
return lhs.month == rhs.month && lhs.day == rhs.day return lhs.month == rhs.month && lhs.day == rhs.day
} }
@ -57,7 +57,7 @@ public enum Monthday {
case .december(let n): month = 12; day = n case .december(let n): month = 12; day = n
} }
return DateComponents(calendar: Calendar.gregorian, return DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent, timeZone: TimeZone.current,
month: month, month: month,
day: day) day: day)
} }

View File

@ -386,13 +386,12 @@ extension Schedule {
public static func every(_ weekday: Weekday) -> DateMiddleware { public static func every(_ weekday: Weekday) -> DateMiddleware {
let schedule = Schedule.make { () -> AnyIterator<Date> in let schedule = Schedule.make { () -> AnyIterator<Date> in
let calendar = Calendar.gregorian let calendar = Calendar.gregorian
let components = weekday.toDateComponents()
var date: Date! var date: Date!
return AnyIterator<Date> { return AnyIterator<Date> {
if weekday.isToday { if weekday.isToday {
date = Date().zeroClock() date = Date().zeroClock()
} else if date == nil { } else if date == nil {
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict) date = calendar.next(weekday, after: Date())
} else { } else {
date = calendar.date(byAdding: .day, value: 7, to: date) date = calendar.date(byAdding: .day, value: 7, to: date)
} }
@ -414,16 +413,15 @@ extension Schedule {
} }
/// Creates a schedule that executes the task every specific day in the month. /// Creates a schedule that executes the task every specific day in the month.
public static func every(_ monthDay: Monthday) -> DateMiddleware { public static func every(_ monthday: Monthday) -> DateMiddleware {
let schedule = Schedule.make { () -> AnyIterator<Date> in let schedule = Schedule.make { () -> AnyIterator<Date> in
let calendar = Calendar.gregorian let calendar = Calendar.gregorian
let components = monthDay.toDateComponents()
var date: Date! var date: Date!
return AnyIterator<Date> { return AnyIterator<Date> {
if monthDay.isToday { if monthday.isToday {
date = Date().zeroClock() date = Date().zeroClock()
} else if date == nil { } else if date == nil {
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict) date = calendar.next(monthday, after: Date())
} else { } else {
date = calendar.date(byAdding: .year, value: 1, to: date) date = calendar.date(byAdding: .year, value: 1, to: date)
} }

View File

@ -93,12 +93,12 @@ public struct Time {
formatter = DateFormatter() formatter = DateFormatter()
formatter?.locale = Locale(identifier: "en_US_POSIX") formatter?.locale = Locale(identifier: "en_US_POSIX")
formatter?.calendar = Calendar.gregorian formatter?.calendar = Calendar.gregorian
formatter?.timeZone = TimeZone.autoupdatingCurrent formatter?.timeZone = TimeZone.current
formatter.dateFormat = fmt formatter.dateFormat = fmt
} }
if let date = formatter.date(from: string) { if let date = formatter.date(from: string) {
Time.FormatterCache.setObject(formatter, forKey: NSString(string: fmt)) Time.FormatterCache.setObject(formatter, forKey: NSString(string: fmt))
let components = Calendar.gregorian.dateComponents(in: TimeZone.autoupdatingCurrent, from: date) let components = Calendar.gregorian.dateComponents(in: TimeZone.current, from: date)
if let hour = components.hour, if let hour = components.hour,
let minute = components.minute, let minute = components.minute,
let second = components.second, let second = components.second,
@ -119,7 +119,7 @@ public struct Time {
func toDateComponents() -> DateComponents { func toDateComponents() -> DateComponents {
return DateComponents(calendar: Calendar.gregorian, return DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent, timeZone: TimeZone.current,
hour: hour, minute: minute, hour: hour, minute: minute,
second: second, nanosecond: nanosecond) second: second, nanosecond: nanosecond)
} }

View File

@ -14,12 +14,12 @@ public enum Weekday: Int {
var isToday: Bool { var isToday: Bool {
return Calendar.gregorian return Calendar.gregorian
.dateComponents(in: .autoupdatingCurrent, from: Date()).weekday == rawValue .dateComponents(in: .current, from: Date()).weekday == rawValue
} }
func toDateComponents() -> DateComponents { func toDateComponents() -> DateComponents {
return DateComponents(calendar: Calendar.gregorian, return DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent, timeZone: TimeZone.current,
weekday: rawValue) weekday: rawValue)
} }
} }

View File

@ -0,0 +1,49 @@
//
// CalendarTests.swift
// ScheduleTests
//
// Created by Quentin MED on 2018/8/27.
//
import XCTest
@testable import Schedule
final class CalendarTests: XCTestCase {
func testNextWeekday() {
let date = Date(year: 2018, month: 8, day: 1, hour: 6)
let n0 = Calendar.gregorian.next(.monday, after: date)
XCTAssertEqual(n0?.dateComponents.year, 2018)
XCTAssertEqual(n0?.dateComponents.month, 8)
XCTAssertEqual(n0?.dateComponents.day, 6)
XCTAssertEqual(n0?.dateComponents.weekday, 2)
let n1 = Calendar.gregorian.next(.friday, after: date)
XCTAssertEqual(n1?.dateComponents.year, 2018)
XCTAssertEqual(n1?.dateComponents.month, 8)
XCTAssertEqual(n1?.dateComponents.day, 3)
XCTAssertEqual(n1?.dateComponents.weekday, 6)
}
func testNextMonthday() {
let date = Date(year: 2000, month: 4, day: 1, hour: 6)
let n0 = Calendar.gregorian.next(.april(1), after: date)
XCTAssertEqual(n0?.dateComponents.year, 2001)
XCTAssertEqual(n0?.dateComponents.month, 4)
XCTAssertEqual(n0?.dateComponents.day, 1)
let n1 = Calendar.gregorian.next(.october(10), after: date)
XCTAssertEqual(n1?.dateComponents.year, 2000)
XCTAssertEqual(n1?.dateComponents.month, 10)
XCTAssertEqual(n1?.dateComponents.day, 10)
}
static var allTests = [
("testNextWeekday", testNextWeekday),
("testNextMonthday", testNextMonthday)
]
}

View File

@ -8,6 +8,8 @@
import XCTest import XCTest
@testable import Schedule @testable import Schedule
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
final class DeinitObserverTests: XCTestCase { final class DeinitObserverTests: XCTestCase {
func testObserver() { func testObserver() {
@ -26,3 +28,5 @@ final class DeinitObserverTests: XCTestCase {
("testObserver", testObserver) ("testObserver", testObserver)
] ]
} }
#endif

View File

@ -11,19 +11,19 @@ import Foundation
extension Date { extension Date {
var dateComponents: DateComponents { var dateComponents: DateComponents {
return Calendar.gregorian.dateComponents(in: TimeZone.autoupdatingCurrent, from: self) return Calendar.gregorian.dateComponents(in: TimeZone.current, from: self)
} }
var localizedDescription: String { var localizedDescription: String {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.timeZone = TimeZone.autoupdatingCurrent formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return formatter.string(from: self) return formatter.string(from: self)
} }
init(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0, nanosecond: Int = 0) { init(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0, nanosecond: Int = 0) {
let components = DateComponents(calendar: Calendar.gregorian, let components = DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent, timeZone: TimeZone.current,
year: year, month: month, day: day, year: year, month: month, day: day,
hour: hour, minute: minute, second: second, hour: hour, minute: minute, second: second,
nanosecond: nanosecond) nanosecond: nanosecond)

View File

@ -11,13 +11,13 @@ import XCTest
final class TaskTests: XCTestCase { final class TaskTests: XCTestCase {
func testSchedule() { func testSchedule() {
let expectation = XCTestExpectation(description: "testSchedule") let e = expectation(description: "testSchedule")
let date = Date() let date = Date()
let task = Schedule.after(0.5.second).do { let task = Schedule.after(0.5.second).do {
XCTAssertTrue(Date().timeIntervalSince(date).isAlmostEqual(to: 0.5, leeway: 0.1)) XCTAssertTrue(Date().timeIntervalSince(date).isAlmostEqual(to: 0.5, leeway: 0.1))
expectation.fulfill() e.fulfill()
} }
wait(for: [expectation], timeout: 2) waitForExpectations(timeout: 2)
task.cancel() task.cancel()
} }
@ -46,15 +46,15 @@ final class TaskTests: XCTestCase {
} }
func testAddAndRemoveActions() { func testAddAndRemoveActions() {
let expectation = XCTestExpectation(description: "testAddAndRemoveActions") let e = expectation(description: "testAddAndRemoveActions")
let task = Schedule.after(0.5.second).do { } let task = Schedule.after(0.5.second).do { }
let date = Date() let date = Date()
let key = task.addAction { _ in let key = task.addAction { _ in
XCTAssertTrue(Date().timeIntervalSince(date).isAlmostEqual(to: 0.5, leeway: 0.1)) XCTAssertTrue(Date().timeIntervalSince(date).isAlmostEqual(to: 0.5, leeway: 0.1))
expectation.fulfill() e.fulfill()
} }
XCTAssertEqual(task.countOfActions, 2) XCTAssertEqual(task.countOfActions, 2)
wait(for: [expectation], timeout: 2) waitForExpectations(timeout: 2)
task.removeAction(byKey: key) task.removeAction(byKey: key)
XCTAssertEqual(task.countOfActions, 1) XCTAssertEqual(task.countOfActions, 1)
task.cancel() task.cancel()
@ -65,25 +65,28 @@ final class TaskTests: XCTestCase {
func testAddAndRemoveTags() { func testAddAndRemoveTags() {
let task = Schedule.never.do { } let task = Schedule.never.do { }
task.addTag("c") let tagA = UUID().uuidString
task.addTags("n", "s", "z") let tagB = UUID().uuidString
XCTAssertTrue(task.tags.contains("c")) let tagC = UUID().uuidString
XCTAssertTrue(task.tags.contains("z")) task.addTag(tagA)
task.removeTag("c") task.addTags(tagB, tagC)
XCTAssertFalse(task.tags.contains("c")) XCTAssertTrue(task.tags.contains(tagA))
task.removeTags("s", "z") XCTAssertTrue(task.tags.contains(tagC))
XCTAssertFalse(task.tags.contains("s")) task.removeTag(tagA)
XCTAssertFalse(task.tags.contains("z")) XCTAssertFalse(task.tags.contains(tagA))
task.removeTags(tagB, tagC)
XCTAssertFalse(task.tags.contains(tagB))
XCTAssertFalse(task.tags.contains(tagC))
task.cancel() task.cancel()
} }
func testReschedule() { func testReschedule() {
let expectation = XCTestExpectation(description: "testReschedule") let e = expectation(description: "testReschedule")
var i = 0 var i = 0
let task = Schedule.after(0.1.second).do { (task) in let task = Schedule.after(0.1.second).do { (task) in
i += 1 i += 1
if task.countOfExecution == 6 && task.timeline.estimatedNextExecution == nil { if task.countOfExecution == 6 && task.timeline.estimatedNextExecution == nil {
expectation.fulfill() e.fulfill()
} }
if task.countOfExecution > 6 { if task.countOfExecution > 6 {
XCTFail("should never come here") XCTFail("should never come here")
@ -92,11 +95,12 @@ final class TaskTests: XCTestCase {
DispatchQueue.global().async(after: 0.5.second) { DispatchQueue.global().async(after: 0.5.second) {
task.reschedule(Schedule.every(0.1.second).first(5)) task.reschedule(Schedule.every(0.1.second).first(5))
} }
wait(for: [expectation], timeout: 2) waitForExpectations(timeout: 2)
task.cancel() task.cancel()
} }
func testParasiticTask() { func testParasiticTask() {
let e = expectation(description: "testParasiticTask")
let fn = { let fn = {
let obj = NSObject() let obj = NSObject()
Schedule.after(0.5.second).do(host: obj, onElapse: { Schedule.after(0.5.second).do(host: obj, onElapse: {
@ -104,11 +108,14 @@ final class TaskTests: XCTestCase {
}) })
} }
fn() fn()
wait(for: [], timeout: 1) DispatchQueue.main.async(after: 0.75.seconds) {
e.fulfill()
}
waitForExpectations(timeout: 1)
} }
func testLifetime() { func testLifetime() {
let expectation = XCTestExpectation(description: "testLifetime") let e = expectation(description: "testLifetime")
let task = Schedule.after(1.hour).do { } let task = Schedule.after(1.hour).do { }
task.setLifetime(1.second) task.setLifetime(1.second)
XCTAssertEqual(task.lifetime, 1.second) XCTAssertEqual(task.lifetime, 1.second)
@ -122,9 +129,9 @@ final class TaskTests: XCTestCase {
} }
DispatchQueue.global().async(after: 2.second) { DispatchQueue.global().async(after: 2.second) {
XCTAssertTrue(task.isCancelled) XCTAssertTrue(task.isCancelled)
expectation.fulfill() e.fulfill()
} }
wait(for: [expectation], timeout: 5) waitForExpectations(timeout: 5)
} }
static var allTests = [ static var allTests = [

View File

@ -1,6 +1,6 @@
import XCTest import XCTest
#if !os(macOS) #if os(Linux)
public func allTests() -> [XCTestCaseEntry] { public func allTests() -> [XCTestCaseEntry] {
return [ return [
testCase(DateTimeTests.allTests), testCase(DateTimeTests.allTests),
@ -9,6 +9,7 @@ public func allTests() -> [XCTestCaseEntry] {
testCase(TaskTests.allTests), testCase(TaskTests.allTests),
testCase(AtomicTests.allTests), testCase(AtomicTests.allTests),
testCase(BucketTests.allTests), testCase(BucketTests.allTests),
testCase(CalendarTests.allTests),
testCase(ExtensionsTests.allTests) testCase(ExtensionsTests.allTests)
] ]
} }