Skip to content

Commit

Permalink
update codec coverage (#245)
Browse files Browse the repository at this point in the history
* update  Codec test

* update blstest

* update jam test

* update encoder test

* update encode & decode

* update Results test

* update codec

* update todo & test

* update test

* udpate decoder

* update codec test

* update encode & decode

* update codec

* update more test

* update encode & decode

* update test
  • Loading branch information
MacOMNI authored Dec 10, 2024
1 parent d38d800 commit ccaedb9
Show file tree
Hide file tree
Showing 10 changed files with 906 additions and 5 deletions.
26 changes: 26 additions & 0 deletions Codec/Sources/Codec/JamEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ private class EncodeContext: Encoder {
}
}

fileprivate func encodeOptional(_ value: Encodable) throws {
let mirror = Mirror(reflecting: value)
if let someValue = mirror.children.first?.value as? Encodable {
data.append(UInt8(1)) // Encode presence flag
try encode(someValue) // Encode the unwrapped value
} else {
data.append(UInt8(0)) // Encode absence flag
}
}

fileprivate func encode(_ value: some Encodable) throws {
if let value = value as? Data {
encodeData(value, lengthPrefix: true)
Expand All @@ -94,6 +104,8 @@ private class EncodeContext: Encoder {
encodeData(value.data, lengthPrefix: false)
} else if let value = value as? [Encodable] {
try encodeArray(value)
} else if Mirror(reflecting: value).displayStyle == .optional {
try encodeOptional(value)
} else {
try value.encode(to: self)
}
Expand Down Expand Up @@ -489,6 +501,20 @@ private struct JamSingleValueEncodingContainer: SingleValueEncodingContainer {
encoder.encodeInt(value)
}

mutating func encode(_: Double) throws {
throw EncodingError.invalidValue(
Double.self,
EncodingError.Context(codingPath: codingPath, debugDescription: "Double is not supported")
)
}

mutating func encode(_: Float) throws {
throw EncodingError.invalidValue(
Float.self,
EncodingError.Context(codingPath: codingPath, debugDescription: "Float is not supported")
)
}

mutating func encode(_ value: some Encodable) throws {
try encoder.encode(value)
}
Expand Down
236 changes: 236 additions & 0 deletions Codec/Tests/CodecTests/DecoderTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import Foundation
import Testing

@testable import Codec

struct DecoderTests {
@Test func decodeOverflowLength() throws {
let overflowLength = UInt64.max
let lengthData = Data([241]) + withUnsafeBytes(of: overflowLength) { Data($0) }
let data = Data(repeating: 0, count: 8)
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(Data.self, from: lengthData + data)
}
}

@Test func decodeUnsupportedType() throws {
struct UnsupportedType: Codable {}
let unsupportedEncodedData = Data([0])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(UnsupportedType.self, from: unsupportedEncodedData)
}
}

@Test func decodeCorruptedString() throws {
let invalidUTF8Data = Data([0x02, 0xC3, 0x28])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(String.self, from: invalidUTF8Data)
}
}

@Test func decodeCorruptedNumericData() throws {
let corruptedNumericData = Data([0xFF, 0xFF, 0xFF, 0xFF])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(Int.self, from: corruptedNumericData)
}
}

@Test func decodeIncorrectEncoding() throws {
let incorrectEncodedData = Data([255, 255, 255])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(Int.self, from: incorrectEncodedData)
}
}

@Test func decodeInvalidKeyedContainer() throws {
let invalidKeyedData = Data([1, 1, 0, 0, 0, 0, 0, 0, 0])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([String: Int].self, from: invalidKeyedData)
}
}

@Test func decodeInvalidUnkeyedContainer() throws {
let invalidUnkeyedData = Data([5, 104, 101, 108])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([Int].self, from: invalidUnkeyedData)
}
struct UnkeyedDouble: Codable {
var doubleValue: Double
}
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(UnkeyedDouble.self, from: invalidUnkeyedData)
}
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(UnkeyedDouble?.self, from: invalidUnkeyedData)
}
struct UnkeyedFloat: Codable {
var floatValue: Float
}
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(UnkeyedFloat?.self, from: invalidUnkeyedData)
}
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(UnkeyedFloat.self, from: invalidUnkeyedData)
}
}

@Test func decodeCorruptedNestedStructure() throws {
let corruptedEncodedData = Data([2, 3, 0, 1, 2])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([String: [Int]].self, from: corruptedEncodedData)
}
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([String: [Int]]?.self, from: corruptedEncodedData)
}
}

@Test func decodeEmptyDataForArray() throws {
let emptyData = Data()
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([Int].self, from: emptyData)
}
}

@Test func decodeUnsupportedArrayFormat() throws {
let unsupportedArrayFormat = Data([5, 0, 0, 0, 0, 0, 0, 0])
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([Int].self, from: unsupportedArrayFormat)
}
}

@Test func decodeLargeArray() throws {
let maxLength = 0xFFFF_FFFF
let encoded = try JamEncoder.encode(maxLength)
var data = Data()
data.append(contentsOf: encoded)
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([Int].self, from: Data(data + Data(repeating: 0, count: maxLength)))
}
}

@Test func decodeInvalidInt() throws {
let encoded16 = try JamEncoder.encode(0xFFFF_FFFF)
#expect(throws: Error.self) {
_ = try JamDecoder.decode(UInt16.self, from: encoded16)
}

let maxLength: UInt64 = 0x1_0000_0000
let encoded = try JamEncoder.encode(maxLength)
#expect(encoded.count == 8)
let decoded = try JamDecoder.decode(UInt64.self, from: encoded)
#expect(decoded == maxLength)
let lengthData = Data([241, 0, 0, 0, 0, 0, 0, 0])
let data = Data(repeating: 0, count: Int(maxLength))
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode(Data.self, from: lengthData + data)
}
#expect(throws: DecodingError.self) {
_ = try JamDecoder.decode([UInt8].self, from: lengthData + data)
}
}

@Test func decodeData() throws {
let encodedData = Data([3, 0, 1, 2])
let decoded = try JamDecoder.decode(Data.self, from: encodedData)

#expect(decoded == Data([0, 1, 2]))
}

@Test func decodeBool() throws {
let encodedTrue = Data([1])
let encodedFalse = Data([0])

let decodedTrue = try JamDecoder.decode(Bool.self, from: encodedTrue)
let decodedFalse = try JamDecoder.decode(Bool.self, from: encodedFalse)

#expect(decodedTrue == true)
#expect(decodedFalse == false)
}

@Test func decodeString() throws {
let encoded = Data([5, 104, 101, 108, 108, 111])
let decoded = try JamDecoder.decode(String.self, from: encoded)
#expect(decoded == "hello")

#expect(throws: Error.self) {
_ = try JamDecoder.decode(String.self, from: Data([6, 104, 101, 108, 108, 111]))
}
}

@Test func decodeArray() throws {
let encoded = Data([3, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0])
let decoded = try JamDecoder.decode([Int].self, from: encoded)

#expect(decoded == [1, 2, 3])
}

@Test func decodeInt() throws {
let encoded = Data([1, 0, 0, 0, 0, 0, 0, 0])
let decoded = try JamDecoder.decode(Int.self, from: encoded)

#expect(decoded == 1)
}

@Test func decodeOptional() throws {
let encodedSome = Data([1, 1, 0, 0, 0, 0, 0, 0, 0])
let encodedNone = Data([0])

let decodedSome = try JamDecoder.decode(Int?.self, from: encodedSome)
let decodedNone = try JamDecoder.decode(Int?.self, from: encodedNone)

#expect(decodedSome == .some(1))
#expect(decodedNone == .none)
}

@Test func decodeFixedWidthInteger() throws {
var encodedInt8 = Data([251])
let encodedUInt64 = Data([21, 205, 91, 7, 0, 0, 0, 0])

let decodedInt8 = try JamDecoder.decode(Int8.self, from: encodedInt8)
let decodedUInt64 = try JamDecoder.decode(UInt64.self, from: encodedUInt64)

#expect(decodedInt8 == -5)
#expect(decodedUInt64 == 123_456_789)
#expect(throws: Error.self) {
_ = try encodedInt8.read(length: 8)
}
}

@Test func decodeInvalidData() throws {
let invalidEncodedData = Data([0, 0, 0, 123])
#expect(throws: Error.self) {
_ = try JamDecoder.decode(Int8.self, from: invalidEncodedData)
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode(Double.self, from: Data())
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode(Float.self, from: Data())
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode(Int?.self, from: Data())
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode(Int?.self, from: Data([2]))
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode(String.self, from: Data([1, 2, 3]))
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode([Int].self, from: Data([21, 205, 91, 7, 0, 0, 0, 0]))
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode([Data].self, from: Data([21, 205, 91, 7, 0, 0, 0, 0]))
}
#expect(throws: Error.self) {
_ = try JamDecoder.decode(Data.self, from: Data([21, 205, 91, 7, 0, 0, 0, 0]))
}
}

@Test func decodeEmptyString() throws {
let invalidEncodedData = Data()
#expect(throws: Error.self) {
_ = try JamDecoder.decode(String.self, from: invalidEncodedData)
}
}
}
92 changes: 92 additions & 0 deletions Codec/Tests/CodecTests/EncodeSizeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import Foundation
import Testing

@testable import Codec

extension Int: EncodedSize, @retroactive Error {
public var encodedSize: Int {
MemoryLayout<Int>.size
}

public static var encodeedSizeHint: Int? {
MemoryLayout<Int>.size
}
}

struct EncodeSizeTests {
@Test
func encodeFixedWidthInteger() throws {
#expect(Int(42).encodedSize == MemoryLayout<Int>.size)
#expect(Int8(-5).encodedSize == MemoryLayout<Int8>.size)
#expect(UInt32(123_456).encodedSize == MemoryLayout<UInt32>.size)
#expect(Int.encodeedSizeHint == MemoryLayout<Int>.size)
#expect(Int8.encodeedSizeHint == MemoryLayout<Int8>.size)
#expect(UInt32.encodeedSizeHint == MemoryLayout<UInt32>.size)
}

@Test
func encodeBool() throws {
#expect(true.encodedSize == 1)
#expect(false.encodedSize == 1)
#expect(Bool.encodeedSizeHint == 1)
}

@Test
func encodeStringAndData() throws {
#expect("test".encodedSize == 4)
#expect("".encodedSize == 0)
#expect(Data([0x01, 0x02, 0x03]).encodedSize == 4)
#expect(Data().encodedSize == 1)
#expect(String.encodeedSizeHint == nil)
#expect(Data.encodeedSizeHint == nil)
}

@Test
func encodeArrayAndSet() throws {
let intArray = [1, 2, 3]
let emptyArray: [Int] = []
let intSet: Set<Int> = [4, 5, 6]
let emptySet: Set<Int> = []

#expect(intArray.encodedSize == UInt32(3).variableEncodingLength() + 3 * MemoryLayout<Int>.size)
#expect(emptyArray.encodedSize == UInt32(0).variableEncodingLength())
#expect(intSet.encodedSize >= UInt32(3).variableEncodingLength())
#expect(emptySet.encodedSize == UInt32(0).variableEncodingLength())
#expect([Int].encodeedSizeHint == nil)
#expect(Set<Int>.encodeedSizeHint == nil)
}

@Test
func encodeDictionary() throws {
let dict: [Int: String] = [1: "one", 2: "two"]
let emptyDict: [Int: String] = [:]

let expectedSize = UInt32(2).variableEncodingLength() +
1.encodedSize + "one".encodedSize +
1.encodedSize + "two".encodedSize

#expect(dict.encodedSize == expectedSize)
#expect(emptyDict.encodedSize == UInt32(0).variableEncodingLength())
#expect([Int: String].encodeedSizeHint == nil)
}

@Test
func encodeOptional() throws {
let someValue: Int? = 42
let noneValue: Int? = nil

#expect(someValue.encodedSize == 1 + MemoryLayout<Int>.size)
#expect(noneValue.encodedSize == 1)
#expect(Int?.encodeedSizeHint == nil)
}

@Test
func encodeResult() throws {
let successResult: Result<String, Int> = .success("OK")
let failureResult: Result<String, Int> = .failure(404)

#expect(successResult.encodedSize == 1 + "OK".encodedSize)
#expect(failureResult.encodedSize == 1 + MemoryLayout<Int>.size)
#expect(Result<String, Int>.encodeedSizeHint == nil)
}
}
Loading

0 comments on commit ccaedb9

Please sign in to comment.