Skip to content

Commit 8ba8fb2

Browse files
kritzcreekchristoph-dfinity
authored andcommitted
Updates for 2.0
- Switch to mops - Switch to core
1 parent 5ba5f52 commit 8ba8fb2

File tree

15 files changed

+652
-680
lines changed

15 files changed

+652
-680
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,12 @@ name: ci
22
on: [push, pull_request]
33
jobs:
44
tests:
5-
runs-on: ubuntu-24.04
5+
runs-on: ubuntu-latest
66
steps:
77
- uses: actions/checkout@v1
88
- name: Check if Git tag exists
99
run: echo "HEAD_TAG=$(git tag --points-at HEAD)" >> $GITHUB_ENV
10-
- name: "install wasmtime"
11-
run: |
12-
mkdir /home/runner/bin
13-
echo "/home/runner/bin" >> $GITHUB_PATH
14-
wget https://github.com/bytecodealliance/wasmtime/releases/download/v18.0.0/wasmtime-v18.0.0-x86_64-linux.tar.xz
15-
tar xf wasmtime-v18.0.0-x86_64-linux.tar.xz
16-
cp wasmtime-v18.0.0-x86_64-linux/wasmtime /home/runner/bin/wasmtime
17-
- name: "install vessel"
18-
run: |
19-
wget --output-document /home/runner/bin/vessel https://github.com/kritzcreek/vessel/releases/download/v0.7.0/vessel-linux64
20-
chmod +x /home/runner/bin/vessel
10+
- name: Install mops
11+
uses: ZenVoich/setup-mops@v1
2112
- name: "test"
22-
run: |
23-
pushd test
24-
./run_test.sh
25-
popd
26-
- name: "docs"
27-
run: $(vessel bin)/mo-doc
28-
- name: Upload docs
29-
uses: JamesIves/github-pages-deploy-action@releases/v3
30-
if: env.HEAD_TAG != ''
31-
with:
32-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33-
BRANCH: gh-pages # The branch the action should deploy to.
34-
FOLDER: docs/ # The folder the action should deploy.
13+
run: mops test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.vessel
2+
.mops
23
*.wasm
34
/docs/

justfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
check:
2+
$(mops toolchain bin moc) --check $(mops sources) src/*.mo src/matchers/*.mo test/*.mo

mops.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "matchers"
3+
version = "2.0.0"
4+
description = "Composable assertions for unit testing"
5+
repository = "https://github.com/kritzcreek/motoko-matchers"
6+
keywords = [ "testing" ]
7+
license = "Apache-2.0"
8+
9+
[dependencies]
10+
core = "1.0.0"
11+
12+
[toolchain]
13+
moc = "0.16.0"
14+
wasmtime = "21.0.0"

package-set.dhall

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/Canister.mo

Lines changed: 141 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,160 @@
11
/// Unit testing for canisters
22
///
33
/// The `Tester` class in this module can be used to define unit tests for canisters.
4-
import Buffer "mo:base/Buffer";
5-
import List "mo:base/List";
6-
import Nat "mo:base/Nat";
7-
import M "Matchers";
4+
import List "mo:core/pure/List";
5+
import L "mo:core/List";
6+
import Nat "mo:core/Nat";
87

98
module {
109

11-
public type Protocol = { #start : Nat; #cont : [Text]; #done : [Text] };
12-
public type TestResult = { #success; #fail : Text };
10+
public type Protocol = { #start : Nat; #cont : [Text]; #done : [Text] };
11+
public type TestResult = { #success; #fail : Text };
1312

14-
type Test = (Text, () -> async TestResult);
13+
type Test = (Text, () -> async TestResult);
1514

16-
/// Instantiate one of these on canister initialization. Then you can use it to
17-
/// register your tests and it will take care of running them.
18-
///
19-
/// Use `runAll` for simple setups and `run` once that stops working.
20-
///
21-
/// When using `run` the `Tester` will execute your tests in the right batch
22-
/// sizes. It will keep calling your `test` function over and over, so make sure
23-
/// to not do any work outside the registered tests.
24-
///
25-
/// ```motoko
26-
/// import Canister "canister:YourCanisterNameHere";
27-
/// import C "mo:matchers/Canister";
28-
/// import M "mo:matchers/Matchers";
29-
/// import T "mo:matchers/Testable";
30-
///
31-
/// actor {
32-
/// let it = C.Tester({ batchSize = 8 });
33-
/// public shared func test() : async Text {
34-
///
35-
/// it.should("greet me", func () : async C.TestResult = async {
36-
/// let greeting = await Canister.greet("Christoph");
37-
/// M.attempt(greeting, M.equals(T.text("Hello, Christoph!")))
38-
/// });
39-
///
40-
/// it.shouldFailTo("greet him-whose-name-shall-not-be-spoken", func () : async () = async {
41-
/// let greeting = await Canister.greet("Voldemort");
42-
/// ignore greeting
43-
/// });
44-
///
45-
/// await it.runAll()
46-
/// // await it.run()
47-
/// }
48-
/// }
49-
/// ```
50-
public class Tester(options : { batchSize : Nat }) {
51-
var tests : List.List<Test> = List.nil();
52-
var running : Bool = false;
15+
/// Instantiate one of these on canister initialization. Then you can use it to
16+
/// register your tests and it will take care of running them.
17+
///
18+
/// Use `runAll` for simple setups and `run` once that stops working.
19+
///
20+
/// When using `run` the `Tester` will execute your tests in the right batch
21+
/// sizes. It will keep calling your `test` function over and over, so make sure
22+
/// to not do any work outside the registered tests.
23+
///
24+
/// ```motoko
25+
/// import Canister "canister:YourCanisterNameHere";
26+
/// import C "mo:matchers/Canister";
27+
/// import M "mo:matchers/Matchers";
28+
/// import T "mo:matchers/Testable";
29+
///
30+
/// actor {
31+
/// let it = C.Tester({ batchSize = 8 });
32+
/// public shared func test() : async Text {
33+
///
34+
/// it.should("greet me", func () : async C.TestResult = async {
35+
/// let greeting = await Canister.greet("Christoph");
36+
/// M.attempt(greeting, M.equals(T.text("Hello, Christoph!")))
37+
/// });
38+
///
39+
/// it.shouldFailTo("greet him-whose-name-shall-not-be-spoken", func () : async () = async {
40+
/// let greeting = await Canister.greet("Voldemort");
41+
/// ignore greeting
42+
/// });
43+
///
44+
/// await it.runAll()
45+
/// // await it.run()
46+
/// }
47+
/// }
48+
/// ```
49+
public class Tester(options : { batchSize : Nat }) {
50+
var tests : List.List<Test> = List.empty();
51+
var running : Bool = false;
5352

54-
/// Registers a test. You can use `attempt` to use a `Matcher` to
55-
/// produce a `TestResult`.
56-
public func should(name : Text, test : () -> async TestResult) {
57-
if (running) return;
58-
tests := List.push((name, test), tests);
59-
};
60-
61-
/// Registers a test that should throw an exception.
62-
public func shouldFailTo(name : Text, test : () -> async ()) {
63-
if (running) return;
64-
tests := List.push(
65-
(
66-
name,
67-
func() : async TestResult = async {
68-
try {
69-
let testResult = await test();
70-
#fail("Should've failed, but didn't");
71-
} catch _ {
72-
#success;
73-
};
74-
},
75-
),
76-
tests,
77-
);
78-
};
53+
/// Registers a test. You can use `attempt` to use a `Matcher` to
54+
/// produce a `TestResult`.
55+
public func should(name : Text, test : () -> async TestResult) {
56+
if (running) return;
57+
tests := List.pushFront(tests, (name, test));
58+
};
7959

80-
/// Runs all your tests in one go and returns a summary Text. If calling
81-
/// this runs out of gas, try using `run` with a configured `batchSize`.
82-
public func runAll() : async Text {
83-
running := true;
84-
var allTests = List.reverse(tests);
85-
var result = "";
86-
var failed = 0;
87-
var testCount = List.size(allTests);
88-
label l loop {
89-
switch allTests {
90-
case null break l;
91-
case (?((name, test), tl)) {
92-
allTests := tl;
93-
try {
94-
result #= switch (await test()) {
95-
case (#success) {
96-
"\"" # name # "\"" # " succeeded.\n";
97-
};
98-
case (#fail(msg)) {
99-
failed += 1;
100-
"\"" # name # "\"" # " failed: " # msg # "\n";
101-
};
102-
};
103-
} catch _ {
104-
failed += 1;
105-
result #= "\"" # name # "\"" # "failed with an unexpected trap." # "\n";
106-
};
107-
};
108-
};
60+
/// Registers a test that should throw an exception.
61+
public func shouldFailTo(name : Text, test : () -> async ()) {
62+
if (running) return;
63+
tests := List.pushFront(
64+
tests,
65+
(
66+
name,
67+
func() : async TestResult = async {
68+
try {
69+
let _testResult = await test();
70+
#fail("Should've failed, but didn't");
71+
} catch _ {
72+
#success;
10973
};
74+
},
75+
),
76+
);
77+
};
11078

111-
if (failed == 0) {
112-
result #= "Success! ";
113-
} else {
114-
result #= "Failure! ";
79+
/// Runs all your tests in one go and returns a summary Text. If calling
80+
/// this runs out of gas, try using `run` with a configured `batchSize`.
81+
public func runAll() : async Text {
82+
running := true;
83+
var allTests = List.reverse(tests);
84+
var result = "";
85+
var failed = 0;
86+
var testCount = List.size(allTests);
87+
label l loop {
88+
switch allTests {
89+
case null break l;
90+
case (?((name, test), tl)) {
91+
allTests := tl;
92+
try {
93+
result #= switch (await test()) {
94+
case (#success) {
95+
"\"" # name # "\"" # " succeeded.\n";
96+
};
97+
case (#fail(msg)) {
98+
failed += 1;
99+
"\"" # name # "\"" # " failed: " # msg # "\n";
100+
};
101+
};
102+
} catch _ {
103+
failed += 1;
104+
result #= "\"" # name # "\"" # "failed with an unexpected trap." # "\n";
115105
};
116-
result # Nat.toText(testCount - failed) # "/" # Nat.toText(testCount) # " succeeded.";
106+
};
117107
};
108+
};
118109

119-
/// You must call this as the last thing in your unit test.
120-
public func run() : async Protocol {
121-
if (not running) {
122-
running := true;
123-
tests := List.reverse(tests);
124-
return #start(List.size(tests));
125-
};
126-
let results : Buffer.Buffer<Text> = Buffer.Buffer(options.batchSize);
127-
var capacity = options.batchSize;
128-
while (capacity > 0) {
129-
capacity -= 1;
130-
switch tests {
131-
case null {
132-
return #done(Buffer.toArray(results));
133-
};
134-
case (?((name, test), tl)) {
135-
tests := tl;
136-
try {
137-
let testResult = await test();
138-
results.add(
139-
switch testResult {
140-
case (#success) {
141-
"\"" # name # "\"" # " succeeded.\n";
142-
};
143-
case (#fail(msg)) {
144-
"\"" # name # "\"" # " failed: " # msg # "\n";
145-
};
146-
}
147-
);
148-
} catch _ {
149-
results.add("\"" # name # "\"" # "failed with an unexpected trap." # "\n");
150-
};
151-
};
152-
};
153-
};
154-
return if (List.isNil(tests)) {
155-
#done(Buffer.toArray(results));
156-
} else {
157-
#cont(Buffer.toArray(results));
110+
if (failed == 0) {
111+
result #= "Success! ";
112+
} else {
113+
result #= "Failure! ";
114+
};
115+
result # Nat.toText(testCount - failed) # "/" # Nat.toText(testCount) # " succeeded.";
116+
};
117+
118+
/// You must call this as the last thing in your unit test.
119+
public func run() : async Protocol {
120+
if (not running) {
121+
running := true;
122+
tests := List.reverse(tests);
123+
return #start(List.size(tests));
124+
};
125+
let results : L.List<Text> = L.empty();
126+
var capacity = options.batchSize;
127+
while (capacity > 0) {
128+
capacity -= 1;
129+
switch tests {
130+
case null {
131+
return #done(L.toArray(results));
132+
};
133+
case (?((name, test), tl)) {
134+
tests := tl;
135+
try {
136+
let testResult = await test();
137+
L.add(results,
138+
switch testResult {
139+
case (#success) {
140+
"\"" # name # "\"" # " succeeded.\n";
141+
};
142+
case (#fail(msg)) {
143+
"\"" # name # "\"" # " failed: " # msg # "\n";
144+
};
145+
}
146+
);
147+
} catch _ {
148+
L.add(results, "\"" # name # "\"" # "failed with an unexpected trap." # "\n");
158149
};
150+
};
159151
};
152+
};
153+
return if (List.isEmpty(tests)) {
154+
#done(L.toArray(results));
155+
} else {
156+
#cont(L.toArray(results));
157+
};
160158
};
159+
};
161160
};

0 commit comments

Comments
 (0)