diff --git a/Tests/XMLCoderTests/NestedChoiceTests.swift b/Tests/XMLCoderTests/NestedChoiceTests.swift new file mode 100644 index 00000000..40963719 --- /dev/null +++ b/Tests/XMLCoderTests/NestedChoiceTests.swift @@ -0,0 +1,250 @@ +// +// NestedChoiceTests.swift +// XMLCoderTests +// +// Created by James Bean on 7/15/19. +// + +import XCTest +import XMLCoder + +private struct Container: Equatable { + let paragraphs: [Paragraph] +} + +private struct Paragraph: Equatable { + let entries: [Entry] +} + +private enum Entry: Equatable { + case run(Run) + case properties(Properties) + case br(Break) +} + +private struct Run: Codable, Equatable { + let id: Int + let text: String +} + +private struct Properties: Codable, Equatable { + let id: Int + let title: String +} + +private struct Break: Codable, Equatable { } + +extension Container: Codable { + enum CodingKeys: String, CodingKey { + case paragraphs = "p" + } +} + +extension Paragraph: Codable { + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + self.entries = try container.decode([Entry].self) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(entries) + } +} + +extension Entry: XMLChoiceCodable { + private enum CodingKeys: String, CodingKey { + case run, properties, br + } + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + do { + self = .run(try container.decode(Run.self, forKey: .run)) + } catch DecodingError.keyNotFound { + do { + self = .properties(try container.decode(Properties.self, forKey: .properties)) + } catch DecodingError.keyNotFound { + self = .br(try container.decode(Break.self, forKey: .br)) + } + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case let .run(value): + try container.encode(value, forKey: .run) + case let .properties(value): + try container.encode(value, forKey: .properties) + case let .br(value): + try container.encode(value, forKey: .br) + } + } +} + +class NestedChoiceTests: XCTestCase { + + func testBreakDecoding() throws { + let xml = "

" + let result = try XMLDecoder().decode(Break.self, from: xml.data(using: .utf8)!) + let expected = Break() + XCTAssertEqual(result, expected) + } + + func testPropertiesDecoding() throws { + let xml = """ + + 431 + A Word About Wake Times + + """ + let result = try XMLDecoder().decode(Properties.self, from: xml.data(using: .utf8)!) + let expected = Properties(id: 431, title: "A Word About Wake Times") + XCTAssertEqual(result, expected) + } + + func testPropertiesAsEntryDecoding() throws { + let xml = """ + + + 431 + A Word About Wake Times + + + """ + let result = try XMLDecoder().decode(Entry.self, from: xml.data(using: .utf8)!) + let expected: Entry = .properties(Properties(id: 431, title: "A Word About Wake Times")) + XCTAssertEqual(result, expected) + } + + func testRunDecoding() throws { + let xml = """ + + 1518 + I am answering it again. + + """ + let result = try XMLDecoder().decode(Run.self, from: xml.data(using: .utf8)!) + let expected = Run(id: 1518, text: "I am answering it again.") + XCTAssertEqual(result, expected) + } + + func testRunAsEntryDecoding() throws { + let xml = """ + + + 1518 + I am answering it again. + + + """ + let result = try XMLDecoder().decode(Entry.self, from: xml.data(using: .utf8)!) + let expected = Entry.run(Run(id: 1518, text: "I am answering it again.")) + XCTAssertEqual(result, expected) + } + + func testEntriesDecoding() throws { + let xml = """ + + + 1518 + I am answering it again. + + + 431 + A Word About Wake Times + + + """ + let result = try XMLDecoder().decode([Entry].self, from: xml.data(using: .utf8)!) + let expected: [Entry] = [ + .run(Run(id: 1518, text: "I am answering it again.")), + .properties(Properties(id: 431, title: "A Word About Wake Times")) + ] + XCTAssertEqual(result, expected) + } + + func testParagraphDecoding() throws { + let xml = """ +

+ + 1518 + I am answering it again. + + + 431 + A Word About Wake Times + +

+ """ + let result = try XMLDecoder().decode(Paragraph.self, from: xml.data(using: .utf8)!) + let expected = Paragraph( + entries: [ + .run(Run(id: 1518, text: "I am answering it again.")), + .properties(Properties(id: 431, title: "A Word About Wake Times")) + ] + ) + XCTAssertEqual(result, expected) + } + + func testNestedEnums() throws { + let xml = """ + +

+ + 1518 + I am answering it again. + + + 431 + A Word About Wake Times + +

+

+ + 1519 + I am answering it again. + +

+
+ """ + let result = try XMLDecoder().decode(Container.self, from: xml.data(using: .utf8)!) + let expected = Container( + paragraphs: [ + Paragraph( + entries: [ + .run(Run(id: 1518, text: "I am answering it again.")), + .properties(Properties(id: 431, title: "A Word About Wake Times")), + ] + ), + Paragraph( + entries: [ + .run(Run(id: 1519, text: "I am answering it again.")), + ] + ) + ] + ) + XCTAssertEqual(result, expected) + } + + func testNestedEnumsRoundTrip() throws { + let original = Container( + paragraphs: [ + Paragraph( + entries: [ + .run(Run(id: 1518, text: "I am answering it again.")), + .properties(Properties(id: 431, title: "A Word About Wake Times")), + ] + ), + Paragraph( + entries: [ + .run(Run(id: 1519, text: "I am answering it again.")), + ] + ) + ] + ) + let encoded = try XMLEncoder().encode(original, withRootKey: "container") + let decoded = try XMLDecoder().decode(Container.self, from: encoded) + XCTAssertEqual(decoded, original) + } +} diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index 24c8ebe2..cd1327e3 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 1482D5A222DD2D9400AE2D6E /* SimpleChoiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1482D5A122DD2D9400AE2D6E /* SimpleChoiceTests.swift */; }; 1482D5A422DD2F4D00AE2D6E /* CompositeChoiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1482D5A322DD2F4D00AE2D6E /* CompositeChoiceTests.swift */; }; 1482D5A822DD6AEE00AE2D6E /* SingleElementBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1482D5A722DD6AED00AE2D6E /* SingleElementBox.swift */; }; + 1482D5AA22DD961E00AE2D6E /* NestedChoiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1482D5A922DD961E00AE2D6E /* NestedChoiceTests.swift */; }; A61DCCD821DF9CA200C0A19D /* ClassTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61DCCD621DF8DB300C0A19D /* ClassTests.swift */; }; A61FE03921E4D60B0015D993 /* UnkeyedIntTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61FE03721E4D4F10015D993 /* UnkeyedIntTests.swift */; }; A61FE03C21E4EAB10015D993 /* KeyedIntTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61FE03A21E4EA8B0015D993 /* KeyedIntTests.swift */; }; @@ -155,6 +156,7 @@ 1482D5A122DD2D9400AE2D6E /* SimpleChoiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleChoiceTests.swift; sourceTree = ""; }; 1482D5A322DD2F4D00AE2D6E /* CompositeChoiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositeChoiceTests.swift; sourceTree = ""; }; 1482D5A722DD6AED00AE2D6E /* SingleElementBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleElementBox.swift; sourceTree = ""; }; + 1482D5A922DD961E00AE2D6E /* NestedChoiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedChoiceTests.swift; sourceTree = ""; }; A61DCCD621DF8DB300C0A19D /* ClassTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassTests.swift; sourceTree = ""; }; A61FE03721E4D4F10015D993 /* UnkeyedIntTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnkeyedIntTests.swift; sourceTree = ""; }; A61FE03A21E4EA8B0015D993 /* KeyedIntTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedIntTests.swift; sourceTree = ""; }; @@ -414,6 +416,7 @@ BF63EF1D21CEC99A001D38C5 /* BenchmarkTests.swift */, 1482D5A122DD2D9400AE2D6E /* SimpleChoiceTests.swift */, 1482D5A322DD2F4D00AE2D6E /* CompositeChoiceTests.swift */, + 1482D5A922DD961E00AE2D6E /* NestedChoiceTests.swift */, OBJ_28 /* BooksTest.swift */, D1B6A2C02297EF5A005B8A6E /* BorderTest.swift */, OBJ_29 /* BreakfastTest.swift */, @@ -724,6 +727,7 @@ BF63EF0821CD7AF8001D38C5 /* URLBoxTests.swift in Sources */, BF9457DD21CBB62C005ACFDE /* DateBoxTests.swift in Sources */, A61DCCD821DF9CA200C0A19D /* ClassTests.swift in Sources */, + 1482D5AA22DD961E00AE2D6E /* NestedChoiceTests.swift in Sources */, BF9457CD21CBB516005ACFDE /* FloatBoxTests.swift in Sources */, BF9457F621CBB6BC005ACFDE /* KeyedTests.swift in Sources */, BF9457C821CBB516005ACFDE /* BoolBoxTests.swift in Sources */,