Skip to content
This repository has been archived by the owner on Oct 23, 2023. It is now read-only.

Commit

Permalink
Refactor of test helpers (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
reddavis authored May 17, 2022
1 parent 93e2a0b commit 11021ef
Show file tree
Hide file tree
Showing 14 changed files with 374 additions and 426 deletions.
41 changes: 41 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Use the latest 2.1 version of CircleCI pipeline process engine.
# See: https://circleci.com/docs/2.0/configuration-reference
# For a detailed guide to building and testing on iOS, read the docs:
# https://circleci.com/docs/2.0/testing-ios/
version: 2.1

## Orbs
orbs:
macos: circleci/macos@2

## Jobs
jobs:
unit_test:
# Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub.
# See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor
macos:
xcode: 13.3.1 # Specify the Xcode version to use
environment:
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- macos/preboot-simulator:
version: "15.4"
platform: "iOS"
device: "iPhone 13 Pro"
- checkout
- run:
name: Install xcpretty
command: gem install xcpretty
- run:
name: Run unit tests
command: Scripts/test -d "OS=15.4,name=iPhone 13 Pro" | xcpretty --color --report junit --output ~/test-results/results.xml
- store_test_results:
path: ~/test-results
- store_artifacts:
path: ~/Library/Logs/DiagnosticReports

## Workflows
workflows:
on_push:
jobs:
- unit_test
18 changes: 0 additions & 18 deletions .github/workflows/ci.yml

This file was deleted.

2 changes: 1 addition & 1 deletion Example/AppStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ struct HighlyComplicatedIncrementMiddleware: Middlewareable {
func execute(event: AppEvent, state: () -> AppState) async {
guard case .incrementWithDelayViaMiddleware = event else { return }

try? await Task.sleep(seconds: 2)
try? await Task.sleep(seconds: 0.5)
self._outputStream.yield(.increment)
}
}
4 changes: 1 addition & 3 deletions ExampleTests/RootScreenTests.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import XCTest
import RedUxTestUtilities
@testable import Example


class RootScreenTests: XCTestCase {
func testStateChange() async {
let store = RootScreen.LocalStore.make()

XCTAssertStateChange(
await XCTAssertStateChange(
store: store,
events: [
.increment,
Expand Down
86 changes: 0 additions & 86 deletions ExampleTests/Supporting Files/StateAssertions.swift

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/reddavis/Asynchrone", from: "0.12.0")
.package(url: "https://github.com/reddavis/Asynchrone", from: "0.17.0")
],
targets: [
.target(
Expand Down
16 changes: 3 additions & 13 deletions RedUx.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
A40EE2A1279F344100663E6C /* Details.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40EE2A0279F344100663E6C /* Details.swift */; };
A40EE2A327A02D7600663E6C /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40EE2A227A02D7600663E6C /* ViewModel.swift */; };
A40EE2A527A044FE00663E6C /* ViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40EE2A427A044FE00663E6C /* ViewModelTests.swift */; };
A410A72E282D48700035A40D /* Assertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A48E085C2743D1FE008090E5 /* Assertions.swift */; };
A425FB70275F9769002AFD72 /* ReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A425FB6F275F9769002AFD72 /* ReducerTests.swift */; };
A4773C442827B10800828A14 /* ActionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4773C432827B10800828A14 /* ActionStatus.swift */; };
A4773C462827B12800828A14 /* ValueStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4773C452827B12800828A14 /* ValueStatus.swift */; };
Expand Down Expand Up @@ -114,8 +115,6 @@
A4A12B5927B6A77D0094B270 /* Middlewareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Middlewareable.swift; sourceTree = "<group>"; };
A4A12B5B27B6CCC70094B270 /* AnyMiddlewareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyMiddlewareable.swift; sourceTree = "<group>"; };
A4B9E5A627A269590000ED07 /* RedUxable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedUxable.swift; sourceTree = "<group>"; };
A4C06ADF27AA8EBA00C3B5D7 /* XCTAssertStatesEventuallyEqualError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTAssertStatesEventuallyEqualError.swift; sourceTree = "<group>"; };
A4C06AE827AAACC600C3B5D7 /* StateAssertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateAssertions.swift; sourceTree = "<group>"; };
A4C36199276A086A00511525 /* AppStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStore.swift; sourceTree = "<group>"; };
A4D2CF9F26C0FC34008D25DE /* View+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extension.swift"; sourceTree = "<group>"; };
A4D2CFA526C16A69008D25DE /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -184,15 +183,6 @@
path = Status;
sourceTree = "<group>";
};
A47BE5FC27BC46DD0011ECE6 /* Supporting Files */ = {
isa = PBXGroup;
children = (
A4C06AE827AAACC600C3B5D7 /* StateAssertions.swift */,
A4C06ADF27AA8EBA00C3B5D7 /* XCTAssertStatesEventuallyEqualError.swift */,
);
path = "Supporting Files";
sourceTree = "<group>";
};
A47BE66A27CBED280011ECE6 /* Extensions */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -310,7 +300,6 @@
isa = PBXGroup;
children = (
A48E08642743D463008090E5 /* RootScreenTests.swift */,
A47BE5FC27BC46DD0011ECE6 /* Supporting Files */,
);
path = ExampleTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -595,6 +584,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A410A72E282D48700035A40D /* Assertions.swift in Sources */,
A48E08652743D463008090E5 /* RootScreenTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -1028,7 +1018,7 @@
repositoryURL = "https://github.com/reddavis/Asynchrone";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.14.0;
minimumVersion = 0.16.0;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
19 changes: 9 additions & 10 deletions RedUx/Source/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ public final class Store<State, Event, Environment> {

defer {
self.isProcessingEvent = false
self.state = state
}

while !self.eventBacklog.isEmpty {
repeat {
let event = self.eventBacklog.removeFirst()
let eventStream = self.reducer(&state, event, self.environment)
self.state = state

Task { [state] in
Task(priority: .high) { [state] in
for middleware in self.middlewares {
await middleware.execute(event: event, state: { state })
}
Expand All @@ -96,7 +96,7 @@ public final class Store<State, Event, Environment> {
self.send(event)
}
}
}
} while !self.eventBacklog.isEmpty
}

// MARK: Middleware
Expand Down Expand Up @@ -143,13 +143,12 @@ extension Store {
)

// Propagate changes to state to scoped store.
scopedStore.parentStatePropagationTask = Task(priority: .high) { [stateSequence, weak scopedStore] in
scopedStore?.state = toScopedState(self.state)

for await state in stateSequence {
scopedStore?.state = toScopedState(state)
scopedStore.parentStatePropagationTask = Just(self.state)
.eraseToAnyAsyncSequenceable()
.chain(with: self.stateSequence)
.sink { [weak scopedStore] in
scopedStore?.state = toScopedState($0)
}
}

return scopedStore
}
Expand Down
25 changes: 9 additions & 16 deletions RedUx/Source/ViewModel.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Asynchrone
import SwiftUI

/// A view model wraps a store and observes state changes that can be used
Expand Down Expand Up @@ -43,8 +44,8 @@ public final class ViewModel<State: Equatable, Event>: ObservableObject {
@Published public var state: State

// Private
private var stateTask: Task<Void, Never>?
private let _send: (Event) -> Void
private var stateTask: Task<Void, Error>?

// MARK: Initialization

Expand All @@ -53,23 +54,15 @@ public final class ViewModel<State: Equatable, Event>: ObservableObject {
public init<Environment>(_ store: Store<State, Event, Environment>) {
self.state = store.state
self._send = { store.send($0) }
self.stateTask = Task { [weak self] in
guard let self = self else { return }

do {

self.stateTask = store
.stateSequence
.removeDuplicates()
.sink(priority: .userInitiated) { state in
await MainActor.run {
self.state = store.state
self.state = state
}

for try await state in store.stateSequence.removeDuplicates() {
guard !Task.isCancelled else { break }

await MainActor.run {
self.state = state
}
}
} catch { }
}
}
}

deinit {
Expand Down
Loading

0 comments on commit 11021ef

Please sign in to comment.