From d2a3197de0ac2b032317dfd3045bb908e54427d6 Mon Sep 17 00:00:00 2001 From: shawn Date: Wed, 28 Aug 2024 08:53:49 +0800 Subject: [PATCH 1/3] add tests for LimitedSizeArray&ConfigLimitedSizeArray --- .../Utils/ConfigLimitedSizeArray.swift | 1 - Utils/Sources/Utils/LimitedSizeArray.swift | 1 - .../ConfigLimitedSizeArrayTest.swift | 145 ++++++++++++++++++ .../UtilsTests/LimitedSizeArrayTest.swift | 71 +++++++++ 4 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 Utils/Tests/UtilsTests/ConfigLimitedSizeArrayTest.swift create mode 100644 Utils/Tests/UtilsTests/LimitedSizeArrayTest.swift diff --git a/Utils/Sources/Utils/ConfigLimitedSizeArray.swift b/Utils/Sources/Utils/ConfigLimitedSizeArray.swift index b32b89d7..bd949b48 100644 --- a/Utils/Sources/Utils/ConfigLimitedSizeArray.swift +++ b/Utils/Sources/Utils/ConfigLimitedSizeArray.swift @@ -1,6 +1,5 @@ import Codec -// TODO: add tests // TODO: consider using a circular buffer instead of a regular array to reduce memory usage public enum ConfigLimitedSizeArrayError: Swift.Error { diff --git a/Utils/Sources/Utils/LimitedSizeArray.swift b/Utils/Sources/Utils/LimitedSizeArray.swift index 72816c0e..846b7464 100644 --- a/Utils/Sources/Utils/LimitedSizeArray.swift +++ b/Utils/Sources/Utils/LimitedSizeArray.swift @@ -1,6 +1,5 @@ import Codec -// TODO: add tests public struct LimitedSizeArray { public private(set) var array: [T] public static var minLength: Int { diff --git a/Utils/Tests/UtilsTests/ConfigLimitedSizeArrayTest.swift b/Utils/Tests/UtilsTests/ConfigLimitedSizeArrayTest.swift new file mode 100644 index 00000000..c84a5628 --- /dev/null +++ b/Utils/Tests/UtilsTests/ConfigLimitedSizeArrayTest.swift @@ -0,0 +1,145 @@ +import Codec +import Foundation +import Testing + +@testable import Utils + +struct MinLength3: ReadInt { + typealias TConfig = Int + + static func read(config _: Int) -> Int { + 3 + } +} + +struct MaxLength5: ReadInt { + typealias TConfig = Int + + static func read(config _: Int) -> Int { + 5 + } +} + +struct ConfigLimitedSizeArrayTests { + @Test func initWithDefaultValue() throws { + let config = 0 + let defaultValue = 1 + let array = try ConfigLimitedSizeArray(config: config, defaultValue: defaultValue) + #expect(array.array == [1, 1, 1]) + #expect(array.count == 3) + } + + @Test func initWithArrayWithinBounds() throws { + let config = 0 + let array = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3]) + #expect(array.array == [1, 2, 3]) + #expect(array.count == 3) + } + + @Test func initWithArrayOutOfBounds() throws { + let config = 0 + // Array smaller than min length + #expect(throws: ConfigLimitedSizeArrayError.tooFewElements) { + _ = try ConfigLimitedSizeArray(config: config, array: [1, 2]) + } + + // Array larger than max length + #expect(throws: ConfigLimitedSizeArrayError.tooManyElements) { + _ = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3, 4, 5, 6]) + } + } + + @Test func appendElement() throws { + let config = 0 + var array = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3]) + try array.append(4) + #expect(array.array == [1, 2, 3, 4]) + #expect(array.count == 4) + + // Appending beyond max length + #expect(throws: ConfigLimitedSizeArrayError.tooManyElements) { + try array.append(5) + try array.append(6) + } + } + + @Test func safeAppendElement() throws { + let config = 0 + var array = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3]) + array.safeAppend(4) + #expect(array.array == [1, 2, 3, 4]) + #expect(array.count == 4) + + // Safe append when max length is reached + array.safeAppend(5) + array.safeAppend(6) + #expect(array.array == [2, 3, 4, 5, 6]) + #expect(array.count == 5) + } + + @Test func insertElement() throws { + let config = 0 + var array = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3]) + try array.insert(0, at: 1) + #expect(array.array == [1, 0, 2, 3]) + #expect(array.count == 4) + + // Inserting beyond max length + #expect(throws: ConfigLimitedSizeArrayError.tooManyElements) { + try array.insert(5, at: 0) + try array.insert(6, at: 0) + } + + // Inserting at invalid index + #expect(throws: ConfigLimitedSizeArrayError.invalidIndex) { + try array.insert(7, at: -1) + } + + #expect(throws: ConfigLimitedSizeArrayError.invalidIndex) { + try array.insert(8, at: 10) + } + } + + @Test func removeElement() throws { + let config = 0 + var array = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3, 4]) + let removed = try array.remove(at: 2) + #expect(removed == 3) + #expect(array.array == [1, 2, 4]) + #expect(array.count == 3) + + // Removing below min length + #expect(throws: ConfigLimitedSizeArrayError.tooFewElements) { + _ = try array.remove(at: 0) + _ = try array.remove(at: 0) + } + + // Removing at invalid index + #expect(throws: ConfigLimitedSizeArrayError.invalidIndex) { + _ = try array.remove(at: -1) + } + + #expect(throws: ConfigLimitedSizeArrayError.invalidIndex) { + _ = try array.remove(at: 10) + } + } + + @Test func equatable() throws { + let config = 0 + let array1 = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3]) + let array2 = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3]) + let array3 = try ConfigLimitedSizeArray(config: config, array: [3, 2, 1]) + + #expect(array1 == array2) + #expect(array1 != array3) + } + + // TODO: Codable + // @Test func codable() throws { + // let config = 0 + // let array = try ConfigLimitedSizeArray(config: config, array: [1, 2, 3]) + // let encoded = try JamEncoder.encode(array) + // let decoded = try JamDecoder.decode(ConfigLimitedSizeArray.self, from: encoded, withConfig: config) + // #expect(decoded == array) + // } +} diff --git a/Utils/Tests/UtilsTests/LimitedSizeArrayTest.swift b/Utils/Tests/UtilsTests/LimitedSizeArrayTest.swift new file mode 100644 index 00000000..156bac8e --- /dev/null +++ b/Utils/Tests/UtilsTests/LimitedSizeArrayTest.swift @@ -0,0 +1,71 @@ +import Codec +import Foundation +import Testing + +@testable import Utils + +struct ConstInt5: ConstInt { + static let value = 5 +} + +struct ConstInt10: ConstInt { + static let value = 10 +} + +struct ConstInt0: ConstInt { + static let value = 0 +} + +struct LimitedSizeArrayTests { + @Test func initWithDefaultValue() throws { + let defaultValue = 1 + let array = LimitedSizeArray(defaultValue: defaultValue) + #expect(array.array == [1, 1, 1, 1, 1]) + #expect(array.count == 5) + } + + @Test func expressibleByArrayLiteral() throws { + let array: LimitedSizeArray = [1, 2, 3, 4, 5] + #expect(array.array == [1, 2, 3, 4, 5]) + } + + @Test func appendElement() throws { + var array = LimitedSizeArray([1, 2, 3, 4, 5]) + array.append(6) + #expect(array.array == [1, 2, 3, 4, 5, 6]) + #expect(array.count == 6) + } + + @Test func insertElement() throws { + var array = LimitedSizeArray([1, 2, 3, 4, 5]) + array.insert(0, at: 2) + #expect(array.array == [1, 2, 0, 3, 4, 5]) + #expect(array.count == 6) + } + + @Test func removeElement() throws { + var array = LimitedSizeArray([1, 2, 3, 4, 5, 6]) + let removed = array.remove(at: 2) + #expect(removed == 3) + #expect(array.array == [1, 2, 4, 5, 6]) + #expect(array.count == 5) + } + + @Test func equatable() throws { + let array1: LimitedSizeArray = [1, 2, 3, 4, 5] + let array2: LimitedSizeArray = [1, 2, 3, 4, 5] + let array3: LimitedSizeArray = [5, 4, 3, 2, 1] + + #expect(array1 == array2) + #expect(array1 != array3) + } + + // TODO: Codable + // @Test func codable() throws { + // let array: LimitedSizeArray = [1, 2, 3, 4, 5] + // let encoded = try JamEncoder.encode(array) + // let decoded = try JamDecoder.decode(LimitedSizeArray.self, from: encoded, withConfig: ()) + + // #expect(decoded == array) + // } +} From bbece4730606f855effac861ba067977ffc95be7 Mon Sep 17 00:00:00 2001 From: shawn Date: Fri, 30 Aug 2024 10:43:13 +0800 Subject: [PATCH 2/3] add InMemoryDataProviderTests --- .../InMemoryDataProviderTests.swift | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift diff --git a/Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift b/Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift new file mode 100644 index 00000000..7cf4b17d --- /dev/null +++ b/Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift @@ -0,0 +1,83 @@ +import Testing +import Utils + +@testable import Blockchain + +struct InMemoryDataProviderTests { + @Test func testInitialization() async throws { + let genesis = StateRef(State.dummy(config: ProtocolConfigRef.mainnet)) + let provider = await InMemoryDataProvider(genesis: genesis) + + #expect(await (provider.getHeads()) == [Data32()]) + #expect(await (provider.getFinalizedHead()) == Data32()) + } + + @Test func testAddAndRetrieveBlock() async throws { + let genesis = StateRef(State.dummy(config: ProtocolConfigRef.mainnet)) + let provider = await InMemoryDataProvider(genesis: genesis) + + let block = BlockRef(Block.dummy(config: ProtocolConfigRef.mainnet)) + await provider.add(block: block) + + #expect(await (provider.hasBlock(hash: block.hash)) == true) + #expect(try await (provider.getBlock(hash: block.hash)) == block) + await #expect(throws: BlockchainDataProviderError.noData) { + try await provider.getBlock(hash: Data32()) + } + } + + @Test func testAddAndRetrieveState() async throws { + let genesis = StateRef(State.dummy(config: ProtocolConfigRef.mainnet)) + let provider = await InMemoryDataProvider(genesis: genesis) + + let state = StateRef(State.dummy(config: ProtocolConfigRef.mainnet)) + await provider.add(state: state) + + #expect(await (provider.hasState(hash: state.value.lastBlockHash)) == true) + #expect(try await (provider.getState(hash: state.value.lastBlockHash)) == state) + } + + @Test func testUpdateHead() async throws { + let genesis = StateRef(State.dummy(config: ProtocolConfigRef.mainnet)) + let provider = await InMemoryDataProvider(genesis: genesis) + + let newBlock = BlockRef(Block.dummy(config: ProtocolConfigRef.mainnet)) + + await provider.add(block: newBlock) + try await provider.updateHead(hash: newBlock.hash, parent: Data32()) + + #expect(await provider.isHead(hash: newBlock.hash) == true) + #expect(await provider.isHead(hash: Data32()) == false) + await #expect(throws: BlockchainDataProviderError.noData) { + try await provider.updateHead(hash: newBlock.hash, parent: Data32()) + } + } + + @Test func testSetFinalizedHead() async throws { + let genesis = StateRef(State.dummy(config: ProtocolConfigRef.mainnet)) + let provider = await InMemoryDataProvider(genesis: genesis) + + let block = BlockRef(Block.dummy(config: ProtocolConfigRef.mainnet)) + await provider.add(block: block) + await provider.setFinalizedHead(hash: block.hash) + + #expect(await (provider.getFinalizedHead()) == block.hash) + } + + @Test func testRemoveHash() async throws { + let genesis = StateRef(State.dummy(config: ProtocolConfigRef.mainnet)) + let provider = await InMemoryDataProvider(genesis: genesis) + + let state = StateRef(State.dummy(config: ProtocolConfigRef.dev)) + let timeslotIndex = state.value.timeslot + await provider.add(state: state) + + #expect(await (provider.hasState(hash: state.value.lastBlockHash)) == true) + #expect(try await (provider.getBlockHash(byTimeslot: timeslotIndex).contains(state.value.lastBlockHash)) == true) + + await provider.remove(hash: state.value.lastBlockHash) + + #expect(await (provider.hasState(hash: state.value.lastBlockHash)) == false) + #expect(try await (provider.getBlockHash(byTimeslot: timeslotIndex).contains(state.value.lastBlockHash)) == false) + } +} From 3ad93b3cc7b534c1de8ed0294634e7475b0c97d7 Mon Sep 17 00:00:00 2001 From: shawn Date: Mon, 2 Sep 2024 23:48:16 +0800 Subject: [PATCH 3/3] InMemoryDataProviderTests update --- .../Tests/BlockchainTests/InMemoryDataProviderTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift b/Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift index 7cf4b17d..943b9bcd 100644 --- a/Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift +++ b/Blockchain/Tests/BlockchainTests/InMemoryDataProviderTests.swift @@ -73,11 +73,11 @@ struct InMemoryDataProviderTests { await provider.add(state: state) #expect(await (provider.hasState(hash: state.value.lastBlockHash)) == true) - #expect(try await (provider.getBlockHash(byTimeslot: timeslotIndex).contains(state.value.lastBlockHash)) == true) + #expect(await (provider.getBlockHash(byTimeslot: timeslotIndex).contains(state.value.lastBlockHash)) == true) await provider.remove(hash: state.value.lastBlockHash) #expect(await (provider.hasState(hash: state.value.lastBlockHash)) == false) - #expect(try await (provider.getBlockHash(byTimeslot: timeslotIndex).contains(state.value.lastBlockHash)) == false) + #expect(await (provider.getBlockHash(byTimeslot: timeslotIndex).contains(state.value.lastBlockHash)) == false) } }