From a55a4cbed040beab9d06fab16e81363453329185 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Tue, 27 Nov 2018 10:54:51 -0800 Subject: [PATCH] Added test library, attribute literal expressibility, and tests --- Package.swift | 12 ++- Sources/JSONAPI/Resource/Attribute.swift | 9 ++ .../JSONAPITestLib/Attribute+Literal.swift | 96 +++++++++++++++++++ .../Attribute/AttributeTests.swift | 21 ++++ .../TestLib/Attribute+LiteralTests.swift | 42 ++++++++ Tests/JSONAPITests/XCTestManifests.swift | 21 ++++ 6 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 Sources/JSONAPITestLib/Attribute+Literal.swift create mode 100644 Tests/JSONAPITests/Attribute/AttributeTests.swift create mode 100644 Tests/JSONAPITests/TestLib/Attribute+LiteralTests.swift diff --git a/Package.swift b/Package.swift index 771cde2..7d527e9 100644 --- a/Package.swift +++ b/Package.swift @@ -6,23 +6,27 @@ import PackageDescription let package = Package( name: "JSONAPI", products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "JSONAPI", targets: ["JSONAPI"]), + .library( + name: "JSONAPITestLib", + targets: ["JSONAPITestLib"]) ], dependencies: [ + // antitypical/Result without the Foundation requirement: .package(url: "https://github.com/mattpolzin/Result", .branch("master")) ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "JSONAPI", dependencies: ["Result"]), + .target( + name: "JSONAPITestLib", + dependencies: ["JSONAPI"]), .testTarget( name: "JSONAPITests", - dependencies: ["JSONAPI"]), + dependencies: ["JSONAPITestLib"]) ], swiftLanguageVersions: [.v4_2] ) diff --git a/Sources/JSONAPI/Resource/Attribute.swift b/Sources/JSONAPI/Resource/Attribute.swift index 03cbec6..62f4720 100644 --- a/Sources/JSONAPI/Resource/Attribute.swift +++ b/Sources/JSONAPI/Resource/Attribute.swift @@ -24,6 +24,15 @@ extension TransformedAttribute: CustomStringConvertible { extension TransformedAttribute: Equatable where Transformer.From: Equatable, Transformer.To: Equatable {} +extension TransformedAttribute where Transformer == IdentityTransformer { + // If we are using the identity transform, we can skip the transform and guarantee no + // error is thrown. + public init(value: RawValue) { + rawValue = value + self.value = value + } +} + public typealias ValidatedAttribute = TransformedAttribute where RawValue == Validator.From public typealias Attribute = TransformedAttribute> diff --git a/Sources/JSONAPITestLib/Attribute+Literal.swift b/Sources/JSONAPITestLib/Attribute+Literal.swift new file mode 100644 index 0000000..9b27bbf --- /dev/null +++ b/Sources/JSONAPITestLib/Attribute+Literal.swift @@ -0,0 +1,96 @@ + +import JSONAPI + +extension TransformedAttribute: ExpressibleByUnicodeScalarLiteral where RawValue: ExpressibleByUnicodeScalarLiteral, Transformer == IdentityTransformer { + public typealias UnicodeScalarLiteralType = RawValue.UnicodeScalarLiteralType + + public init(unicodeScalarLiteral value: RawValue.UnicodeScalarLiteralType) { + self.init(value: RawValue(unicodeScalarLiteral: value)) + } +} + +extension TransformedAttribute: ExpressibleByExtendedGraphemeClusterLiteral where RawValue: ExpressibleByExtendedGraphemeClusterLiteral, Transformer == IdentityTransformer { + public typealias ExtendedGraphemeClusterLiteralType = RawValue.ExtendedGraphemeClusterLiteralType + + public init(extendedGraphemeClusterLiteral value: RawValue.ExtendedGraphemeClusterLiteralType) { + self.init(value: RawValue(extendedGraphemeClusterLiteral: value)) + } +} + +extension TransformedAttribute: ExpressibleByStringLiteral where RawValue: ExpressibleByStringLiteral, Transformer == IdentityTransformer { + public typealias StringLiteralType = RawValue.StringLiteralType + + public init(stringLiteral value: RawValue.StringLiteralType) { + self.init(value: RawValue(stringLiteral: value)) + } +} + +extension TransformedAttribute: ExpressibleByNilLiteral where RawValue: ExpressibleByNilLiteral, Transformer == IdentityTransformer { + public init(nilLiteral: ()) { + self.init(value: RawValue(nilLiteral: ())) + } +} + +extension TransformedAttribute: ExpressibleByFloatLiteral where RawValue: ExpressibleByFloatLiteral, Transformer == IdentityTransformer { + public typealias FloatLiteralType = RawValue.FloatLiteralType + + public init(floatLiteral value: RawValue.FloatLiteralType) { + self.init(value: RawValue(floatLiteral: value)) + } +} + +extension TransformedAttribute: ExpressibleByBooleanLiteral where RawValue: ExpressibleByBooleanLiteral, Transformer == IdentityTransformer { + public typealias BooleanLiteralType = RawValue.BooleanLiteralType + + public init(booleanLiteral value: BooleanLiteralType) { + self.init(value: RawValue(booleanLiteral: value)) + } +} + +extension TransformedAttribute: ExpressibleByIntegerLiteral where RawValue: ExpressibleByIntegerLiteral, Transformer == IdentityTransformer { + public typealias IntegerLiteralType = RawValue.IntegerLiteralType + + public init(integerLiteral value: IntegerLiteralType) { + self.init(value: RawValue(integerLiteral: value)) + } +} + +// regretably, array and dictionary literals are not so easy because Dictionaries and Arrays +// cannot be turned back into variadic arguments to pass onto the RawValue type's constructor. + +// we can still provide a case for the Array and Dictionary types, though. +public protocol DictionaryType { + associatedtype Key: Hashable + associatedtype Value + + init(_ keysAndValues: S, uniquingKeysWith combine: (Dictionary.Value, Dictionary.Value) throws -> Dictionary.Value) rethrows where S : Sequence, S.Element == (Key, Value) +} +extension Dictionary: DictionaryType {} + +extension TransformedAttribute: ExpressibleByDictionaryLiteral where RawValue: DictionaryType, Transformer == IdentityTransformer { + public typealias Key = RawValue.Key + + public typealias Value = RawValue.Value + + public init(dictionaryLiteral elements: (RawValue.Key, RawValue.Value)...) { + + // we arbitrarily keep the first value if two values are assigned to the same key + self.init(value: RawValue(elements, uniquingKeysWith: { val, _ in val })) + } +} + +public protocol ArrayType { + associatedtype Element + + init(_ s: S) where Element == S.Element, S : Sequence +} +extension Array: ArrayType {} +extension ArraySlice: ArrayType {} + +extension TransformedAttribute: ExpressibleByArrayLiteral where RawValue: ArrayType, Transformer == IdentityTransformer { + public typealias ArrayLiteralElement = RawValue.Element + + public init(arrayLiteral elements: ArrayLiteralElement...) { + self.init(value: RawValue(elements)) + } +} diff --git a/Tests/JSONAPITests/Attribute/AttributeTests.swift b/Tests/JSONAPITests/Attribute/AttributeTests.swift new file mode 100644 index 0000000..ad676c4 --- /dev/null +++ b/Tests/JSONAPITests/Attribute/AttributeTests.swift @@ -0,0 +1,21 @@ +// +// AttributeTests.swift +// JSONAPITests +// +// Created by Mathew Polzin on 11/27/18. +// + +import XCTest +import JSONAPI + +class AttributeTests: XCTestCase { + + func test_AttributeIsTransformedAttribute() { + XCTAssertEqual(try TransformedAttribute>(rawValue: "hello"), try Attribute(rawValue: "hello")) + } + + func test_AttributeNonThrowingConstructor() { + XCTAssertEqual(try Attribute(rawValue: "hello"), Attribute(value: "hello")) + } + +} diff --git a/Tests/JSONAPITests/TestLib/Attribute+LiteralTests.swift b/Tests/JSONAPITests/TestLib/Attribute+LiteralTests.swift new file mode 100644 index 0000000..30188f1 --- /dev/null +++ b/Tests/JSONAPITests/TestLib/Attribute+LiteralTests.swift @@ -0,0 +1,42 @@ +// +// Attribute+LiteralTests.swift +// JSONAPITests +// +// Created by Mathew Polzin on 11/27/18. +// + +import XCTest +import JSONAPI +import JSONAPITestLib + +class Attribute_LiteralTests: XCTestCase { + + func test_StringLiteral() { + XCTAssertEqual(Attribute(value: "hello"), "hello") + } + + func test_BooleanLiteral() { + XCTAssertEqual(Attribute(value: false), false) + } + + func test_IntegerLiteral() { + XCTAssertEqual(Attribute(value: 12), 12) + } + + func test_FloatLiteral() { + XCTAssertEqual(Attribute(value: 1.2), 1.2) + XCTAssertEqual(Attribute(value: 1.2), 1.2) + } + + func test_ArrayLiteral() { + XCTAssertEqual(Attribute<[String]>(value: ["hello", "world"]), ["hello", "world"]) + } + + func test_DictionaryLiteral() { + XCTAssertEqual(Attribute<[String : Int]>(value: ["hello": 1]), ["hello": 1]) + } + + func test_NilLiteral() { + XCTAssertEqual(Attribute(value: nil), nil) + } +} diff --git a/Tests/JSONAPITests/XCTestManifests.swift b/Tests/JSONAPITests/XCTestManifests.swift index 3f325cc..4638f44 100644 --- a/Tests/JSONAPITests/XCTestManifests.swift +++ b/Tests/JSONAPITests/XCTestManifests.swift @@ -1,5 +1,24 @@ import XCTest +extension AttributeTests { + static let __allTests = [ + ("test_AttributeIsTransformedAttribute", test_AttributeIsTransformedAttribute), + ("test_AttributeNonThrowingConstructor", test_AttributeNonThrowingConstructor), + ] +} + +extension Attribute_LiteralTests { + static let __allTests = [ + ("test_ArrayLiteral", test_ArrayLiteral), + ("test_BooleanLiteral", test_BooleanLiteral), + ("test_DictionaryLiteral", test_DictionaryLiteral), + ("test_FloatLiteral", test_FloatLiteral), + ("test_IntegerLiteral", test_IntegerLiteral), + ("test_NilLiteral", test_NilLiteral), + ("test_StringLiteral", test_StringLiteral), + ] +} + extension DocumentTests { static let __allTests = [ ("test_errorDocumentNoMeta", test_errorDocumentNoMeta), @@ -180,6 +199,8 @@ extension ResourceBodyTests { #if !os(macOS) public func __allTests() -> [XCTestCaseEntry] { return [ + testCase(AttributeTests.__allTests), + testCase(Attribute_LiteralTests.__allTests), testCase(DocumentTests.__allTests), testCase(EntityTests.__allTests), testCase(IncludedTests.__allTests),