Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Rectangle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
944F25CD2CE5A144004B2FD2 /* PrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 944F25CC2CE5A144004B2FD2 /* PrefsViewController.swift */; };
944F25CF2CE5A144004B2FD2 /* ShortcutRecordingObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 944F25CE2CE5A144004B2FD2 /* ShortcutRecordingObserver.swift */; };
944F25D12CE5A144004B2FD2 /* ShortcutRecordingObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 944F25D02CE5A144004B2FD2 /* ShortcutRecordingObserverTests.swift */; };
01f52cab9bce43359775bdd7 /* JSONDefaultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = de0950b8b00f40308934a523 /* JSONDefaultTests.swift */; };
94E9B08E2C3B8D97004C7F41 /* MacTilingDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94E9B08D2C3B8D97004C7F41 /* MacTilingDefaults.swift */; };
94E9B0902C3E4578004C7F41 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94E9B08F2C3E4578004C7F41 /* StringExtension.swift */; };
9818E00D28B59205004AA524 /* CompoundSnapArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9818E00C28B59205004AA524 /* CompoundSnapArea.swift */; };
Expand Down Expand Up @@ -260,6 +261,7 @@
944F25CC2CE5A144004B2FD2 /* PrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsViewController.swift; sourceTree = "<group>"; };
944F25CE2CE5A144004B2FD2 /* ShortcutRecordingObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutRecordingObserver.swift; sourceTree = "<group>"; };
944F25D02CE5A144004B2FD2 /* ShortcutRecordingObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutRecordingObserverTests.swift; sourceTree = "<group>"; };
de0950b8b00f40308934a523 /* JSONDefaultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDefaultTests.swift; sourceTree = "<group>"; };
94E9B08D2C3B8D97004C7F41 /* MacTilingDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacTilingDefaults.swift; sourceTree = "<group>"; };
94E9B08F2C3E4578004C7F41 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
9698D1D9EE0180D7C4468304 /* TopCenterLeftTwelfthCalculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopCenterLeftTwelfthCalculation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -729,6 +731,7 @@
children = (
9824701F22AF9B7E0037B409 /* RectangleTests.swift */,
944F25D02CE5A144004B2FD2 /* ShortcutRecordingObserverTests.swift */,
de0950b8b00f40308934a523 /* JSONDefaultTests.swift */,
9824702122AF9B7E0037B409 /* Info.plist */,
);
path = RectangleTests;
Expand Down Expand Up @@ -1182,6 +1185,7 @@
files = (
9824702022AF9B7E0037B409 /* RectangleTests.swift in Sources */,
944F25D12CE5A144004B2FD2 /* ShortcutRecordingObserverTests.swift in Sources */,
01f52cab9bce43359775bdd7 /* JSONDefaultTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
3 changes: 2 additions & 1 deletion Rectangle/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,11 @@ class JSONDefault<T: Codable>: StringDefault {
}

init(key: String, defaultValue: T) {
super.init(key: key)
loadFromJSON()
if typedValue == nil {
typedValue = defaultValue
}
super.init(key: key)
}

override func load(from codable: CodableDefault) {
Expand Down
53 changes: 53 additions & 0 deletions RectangleTests/JSONDefaultTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/// JSONDefaultTests.swift

import XCTest
@testable import Rectangle

class JSONDefaultTests: XCTestCase {

/// `systemWideMouseDownApps` / `doubleClickToolBarIgnoredApps` are only customizable via
/// `defaults write` (there is no in-app UI). They are declared through
/// `JSONDefault.init(key:defaultValue:)`, which must honor a value the user persisted for the
/// key and fall back to the default only when nothing is stored. These tests guard that contract
/// using a dedicated test key against the real `UserDefaults.standard` suite `JSONDefault` reads.
private static let testKey = "testJSONDefaultStoredValueRoundTrip"

private func writeJSON<T: Encodable>(_ value: T, forKey key: String) throws {
let data = try JSONEncoder().encode(value)
UserDefaults.standard.set(String(data: data, encoding: .utf8), forKey: key)
}

func testStoredValueIsHonoredOverDefault() throws {
let key = Self.testKey
let storedValue: Set<String> = ["com.example.customApp", "org.something.other"]
let defaultValue: Set<String> = ["org.languagetool.desktop", "com.microsoft.teams2"]

try writeJSON(storedValue, forKey: key)
defer { UserDefaults.standard.removeObject(forKey: key) }

let jsonDefault = JSONDefault<Set<String>>(key: key, defaultValue: defaultValue)
XCTAssertEqual(jsonDefault.typedValue, storedValue)
}

func testMissingValueFallsBackToDefault() throws {
let key = Self.testKey
let defaultValue: Set<String> = ["org.languagetool.desktop", "com.microsoft.teams2"]

UserDefaults.standard.removeObject(forKey: key)
defer { UserDefaults.standard.removeObject(forKey: key) }

let jsonDefault = JSONDefault<Set<String>>(key: key, defaultValue: defaultValue)
XCTAssertEqual(jsonDefault.typedValue, defaultValue)
}

func testInvalidJSONFallsBackToDefault() throws {
let key = Self.testKey
let defaultValue: Set<String> = ["org.languagetool.desktop", "com.microsoft.teams2"]

UserDefaults.standard.set("not valid json", forKey: key)
defer { UserDefaults.standard.removeObject(forKey: key) }

let jsonDefault = JSONDefault<Set<String>>(key: key, defaultValue: defaultValue)
XCTAssertEqual(jsonDefault.typedValue, defaultValue)
}
}