Skip to content

Commit 070397a

Browse files
committed
Ensure that duplicate Opts aren’t passed when starting a Caffeination
1 parent 6747620 commit 070397a

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

Sources/CaffeineKit/Caffeination.swift

+12-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import Cocoa
1111
/// An instance of a sleep-prevention session.
1212
public class Caffeination {
1313

14-
/// An customization option for a `Caffeination`.
14+
/**
15+
A customization option for a `Caffeination`.
16+
- Note: Any given `Caffeination` can be passed only **one** of each `Opt`. So, for instance, it is valid to pass a `.timed` and `.process` Opt; however, it is not possible to have two different `.process` Opts.
17+
*/
1518
public enum Opt {
1619
/// Prevents disk idle sleep.
1720
case disk
@@ -230,8 +233,7 @@ public class Caffeination {
230233
- safety: Whether to enable safety measures to ensure that no "zombie" `caffeinate` processes can outlive the current application. Set to `true` by default, which is recommended.
231234
- terminationHandler: A handler that will be called when the Caffeination stops. Will be set to `nil` if the parameter is not specified.
232235
*/
233-
public init(withOpts opts: [Opt] = Opt.defaults, safety: Bool = true, terminationHandler: ((Caffeination) -> Void)? = nil) {
234-
// TODO: duplicate opt entries need to be disallowed because they result in unexpected behavior
236+
public init(withOpts opts: [Opt] = Opt.defaults, safety: Bool = true, terminationHandler: ((Caffeination) -> Void)? = nil) {
235237
self.opts = opts
236238
if safety {
237239
self.interceptAppTermination = true
@@ -329,14 +331,20 @@ public class Caffeination {
329331
Logger.logLevel = level
330332
}
331333

332-
/// Ensures that the Caffeinate executable exists and that no Caffeination is already active.
334+
/// Ensures that the Caffeinate executable exists, no Caffeination is already active, and no duplicate Opts have been passed to this Caffeination.
333335
private func preCaffeinateSafetyCheck() throws {
334336
guard isActive == false else {
335337
throw CaffeinationError.alreadyActive
336338
}
337339
guard Caffeination.caffeinateExists else {
338340
throw CaffeinationError.caffeinateNotFound
339341
}
342+
let optsSorted = opts.sorted(by: { $0.argumentList[0] > $1.argumentList[0] })
343+
for i in 1..<optsSorted.count {
344+
if optsSorted[i - 1].argumentList[0] == optsSorted[i].argumentList[0] {
345+
throw CaffeinationError.duplicateOpts
346+
}
347+
}
340348
}
341349

342350
// Allows the termination handler to be mutated even after the process has started

Sources/CaffeineKit/Enums.swift

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public enum CaffeinationError: Swift.Error {
4040

4141
/// Thrown if an attempt is made to start a Caffeination on an already-active instance.
4242
case alreadyActive
43+
44+
/// Thrown if duplicate options are passed to a Caffeination
45+
case duplicateOpts
4346
}
4447

4548
/// An error related to signal trapping.

Tests/CaffeineKitTests/CaffeineKitTests.swift

+17
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,21 @@ final class CaffeineKitTests: XCTestCase {
238238
let nilOpt = Caffeination.Opt.from([String(rand), "-t"])
239239
XCTAssert(nilOpt == nil)
240240
}
241+
242+
func testDuplicateOptsThrows() {
243+
let caf = Caffeination(withOpts: [.timed(2), .disk, .timed(4)])
244+
do {
245+
try caf.start()
246+
XCTFail("Duplicate opts, so error should have been thrown")
247+
} catch let e as CaffeinationError {
248+
switch e {
249+
case .duplicateOpts:
250+
break
251+
default:
252+
XCTFail("Wrong error thrown for duplicate opts")
253+
}
254+
} catch {
255+
XCTFail("Non-CaffeinationError thrown for duplicate opts")
256+
}
257+
}
241258
}

0 commit comments

Comments
 (0)