Skip to content

Commit e11cf66

Browse files
authored
Merge pull request #99 from unsignedapps/flagvaluedictionary-codable
Add `Codable` conformance to `FlagValueDictionary`
2 parents 244a766 + eca51ef commit e11cf66

2 files changed

Lines changed: 95 additions & 16 deletions

File tree

Sources/Vexil/Sources/FlagValueDictionary.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import Foundation
1414
/// A simple dictionary-backed FlagValueSource that can be useful for testing
1515
/// and other purposes.
1616
///
17-
open class FlagValueDictionary: Identifiable, ExpressibleByDictionaryLiteral {
17+
open class FlagValueDictionary: Identifiable, ExpressibleByDictionaryLiteral, Codable {
1818

1919
// MARK: - Properties
2020

2121
/// A Unique Identifier for this FlagValueDictionary
22-
public let id = UUID()
22+
public let id: UUID
2323

2424
/// Our internal dictionary type
2525
public typealias DictionaryType = [String: BoxedFlagValue]
@@ -33,18 +33,43 @@ open class FlagValueDictionary: Identifiable, ExpressibleByDictionaryLiteral {
3333

3434
// MARK: - Initialisation
3535

36+
/// Private (but for @testable) memeberwise initialiser
37+
init (id: UUID, storage: DictionaryType) {
38+
self.id = id
39+
self.storage = storage
40+
}
41+
3642
/// Initialises a `FlagValueDictionary` with the specified dictionary
3743
///
38-
public init (_ dictionary: DictionaryType = [:]) {
39-
self.storage = dictionary
44+
public convenience init (_ dictionary: DictionaryType = [:]) {
45+
self.init(
46+
id: UUID(),
47+
storage: dictionary
48+
)
4049
}
4150

4251
/// Initialises a `FlagValueDictionary` using a dictionary literal
4352
///
4453
public required init(dictionaryLiteral elements: (String, BoxedFlagValue)...) {
54+
self.id = UUID()
4555
self.storage = elements.reduce(into: [:]) { dict, pair in
4656
dict.updateValue(pair.1, forKey: pair.0)
4757
}
4858
}
4959

60+
// MARK: - Codable Support
61+
62+
enum CodingKeys: String, CodingKey {
63+
case id
64+
case storage
65+
}
66+
67+
}
68+
69+
// MARK: - Equatable Support
70+
71+
extension FlagValueDictionary: Equatable {
72+
public static func == (lhs: FlagValueDictionary, rhs: FlagValueDictionary) -> Bool {
73+
return lhs.id == rhs.id && lhs.storage == rhs.storage
74+
}
5075
}

Tests/VexilTests/FlagValueDictionaryTests.swift

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created by Rob Amos on 17/8/20.
66
//
77

8+
import Foundation
89
@testable import Vexil
910
import XCTest
1011

@@ -25,19 +26,72 @@ final class FlagValueDictionaryTests: XCTestCase {
2526

2627
// MARK: - Writing Values
2728

28-
func testWritesValues () {
29-
AssertNoThrow {
30-
let source = FlagValueDictionary()
31-
let flagPole = FlagPole(hoist: TestFlags.self, sources: [ source ])
29+
func testWritesValues () throws {
30+
let source = FlagValueDictionary()
31+
let flagPole = FlagPole(hoist: TestFlags.self, sources: [ source ])
32+
33+
let snapshot = flagPole.emptySnapshot()
34+
snapshot.topLevelFlag = true
35+
snapshot.oneFlagGroup.secondLevelFlag = false
36+
try flagPole.save(snapshot: snapshot, to: source)
37+
38+
XCTAssertEqual(source.storage["top-level-flag"], .bool(true))
39+
XCTAssertEqual(source.storage["one-flag-group.second-level-flag"], .bool(false))
40+
}
41+
42+
// MARK: - Equatable Tests
43+
44+
func testEquatable() {
45+
46+
let identifier1 = UUID()
47+
let original = FlagValueDictionary(
48+
id: identifier1,
49+
storage: [
50+
"top-level-flag": .bool(true)
51+
]
52+
)
53+
54+
let same = FlagValueDictionary(
55+
id: identifier1,
56+
storage: [
57+
"top-level-flag": .bool(true)
58+
]
59+
)
60+
61+
let differentContent = FlagValueDictionary(
62+
id: identifier1,
63+
storage: [
64+
"top-level-flag": .bool(false)
65+
]
66+
)
67+
68+
let differentIdentifier = FlagValueDictionary(
69+
id: UUID(),
70+
storage: [
71+
"top-level-flag": .bool(true)
72+
]
73+
)
74+
75+
XCTAssertEqual(original, same)
76+
XCTAssertNotEqual(original, differentContent)
77+
XCTAssertNotEqual(original, differentIdentifier)
78+
79+
}
80+
81+
// MARK: - Codable Tests
82+
83+
func testCodable() throws {
84+
// BoxedFlagValue's Codable support is more heavily tested in it's tests
85+
let source: FlagValueDictionary = [
86+
"bool-flag": .bool(true),
87+
"string-flag": .string("alpha"),
88+
"integer-flag": .integer(123)
89+
]
3290

33-
let snapshot = flagPole.emptySnapshot()
34-
snapshot.topLevelFlag = true
35-
snapshot.oneFlagGroup.secondLevelFlag = false
36-
try flagPole.save(snapshot: snapshot, to: source)
91+
let encoded = try JSONEncoder().encode(source)
92+
let decoded = try JSONDecoder().decode(FlagValueDictionary.self, from: encoded)
3793

38-
XCTAssertEqual(source.storage["top-level-flag"], .bool(true))
39-
XCTAssertEqual(source.storage["one-flag-group.second-level-flag"], .bool(false))
40-
}
94+
XCTAssertEqual(source, decoded)
4195
}
4296

4397

@@ -89,7 +143,7 @@ private struct TestFlags: FlagContainer {
89143
var oneFlagGroup: OneFlags
90144

91145
@Flag(description: "Top level test flag")
92-
var topLevelFlag: Bool = false
146+
var topLevelFlag = false
93147

94148
}
95149

0 commit comments

Comments
 (0)