From e38640d94f6926f8b9ea693762f23a48f12656ce Mon Sep 17 00:00:00 2001 From: ZenVoich Date: Mon, 31 Jul 2023 11:32:13 +0400 Subject: [PATCH 01/15] WIP `expect` --- README.md | 2 +- src/expect.mo | 364 ++++++++++++++++++++++++++++++++++++++++++++ test/expect.test.mo | 51 +++++++ 3 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 src/expect.mo create mode 100644 test/expect.test.mo diff --git a/README.md b/README.md index 85d4c2f..8901972 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ test("this test will run", func() { ``` ## Async tests -If there are `await`'s in your tests, use functions from `mo:test/async`. +If there are `await`s in your tests, use functions from `mo:test/async`. ```motoko import {test; suite} "mo:test/async"; diff --git a/src/expect.mo b/src/expect.mo new file mode 100644 index 0000000..bb20d4c --- /dev/null +++ b/src/expect.mo @@ -0,0 +1,364 @@ +import Debug "mo:base/Debug"; +import Nat "mo:base/Nat"; +import Nat8 "mo:base/Nat8"; +import Nat16 "mo:base/Nat16"; +import Nat32 "mo:base/Nat32"; +import Nat64 "mo:base/Nat64"; +import Int "mo:base/Int"; +import Int8 "mo:base/Int8"; +import Int16 "mo:base/Int16"; +import Int32 "mo:base/Int32"; +import Int64 "mo:base/Int64"; +import Float "mo:base/Float"; +import Char "mo:base/Char"; +import Text "mo:base/Text"; +import TrieMap "mo:base/TrieMap"; +import Hash "mo:base/Hash"; +import Array "mo:base/Array"; +import Option "mo:base/Option"; +import Bool "mo:base/Bool"; +import Iter "mo:base/Iter"; + +module { + public func compare(a : T, b : T, comp : (T, T) -> Bool, toText : (T) -> Text, operator : Text) { + let res = comp(a, b); + if (not res) { + fail(toText(b), operator, toText(a)); + }; + }; + + public func fail(actual : Text, condition : Text, reference : Text) { + // let prefix = "\1b[31m·\1b[0m"; + // let prefix = "\1b[31m•\1b[0m"; + // let prefix = "\1b[31m⚠\1b[0m"; + let prefix = "\1b[31m⌇\1b[0m"; + // let prefix = "\1b[31m!\1b[0m"; + var msg = "\n" # prefix # " expected \1b[31m" # actual # "\1b[0m "; + if (condition != "") { + msg #= "\n" # prefix # " \1b[30m" # condition # "\1b[0m"; + } + else { + msg #= prefix # ""; + }; + msg #= " " # reference # ""; + + Debug.trap(msg); + }; + + public func makeCompare(comp : (T, T) -> Bool, toText : (T) -> Text) : (T, T) -> () { + return func(a : T, b : T) = compare(a, b, comp, toText, "=="); + }; + + public func bindCompare(a : T, comp : (T, T) -> Bool, toText : (T) -> Text) : (T) -> () { + return func(b : T) = compare(a, b, comp, toText, "to be =="); + }; + + type ExpectBool = { + isTrue : () -> (); + isFalse : () -> (); + equal : Bool -> (); + notEqual : Bool -> (); + }; + + // type ExpectFloat = { + // equalWithin : (Float, Float) -> Bool; + // notEqualWithin : (Float, Float) -> Bool; + // less : (Float) -> Bool; + // lessOrEqual : (Float) -> Bool; + // greater : (Float) -> Bool; + // greaterOrEqual : (Float) -> Bool; + // }; + + type ExpectNum = { + equal : (T) -> (); + notEqual : (T) -> (); + less : (T) -> (); + lessOrEqual : (T) -> (); + greater : (T) -> (); + greaterOrEqual : (T) -> (); + }; + + type NumModule = { + equal : (T, T) -> Bool; + notEqual : (T, T) -> Bool; + less : (T, T) -> Bool; + lessOrEqual : (T, T) -> Bool; + greater : (T, T) -> Bool; + greaterOrEqual : (T, T) -> Bool; + toText : (T) -> Text; + }; + + func makeExpectNum(mod : NumModule) : T -> ExpectNum { + func makeNumCompare(a : T, comp : (T, T) -> Bool, toText : (T) -> Text, condition : Text) : (T) -> () { + return func(b : T) { + if (not comp(a, b)) { + fail(toText(a), condition, toText(b)); + }; + }; + }; + + return func(val : T) : ExpectNum { + return { + equal = makeNumCompare(val, mod.equal, mod.toText, "to be =="); + notEqual = makeNumCompare(val, mod.notEqual, mod.toText, "to be !="); + less = makeNumCompare(val, mod.less, mod.toText, "to be <"); + lessOrEqual = makeNumCompare(val, mod.lessOrEqual, mod.toText, "to be <="); + greater = makeNumCompare(val, mod.greater, mod.toText, "to be >"); + greaterOrEqual = makeNumCompare(val, mod.greaterOrEqual, mod.toText, "to be >="); + } + }; + }; + + public let expect = { + bool = func(a : Bool) : ExpectBool { + return { + isTrue = func() { + if (a != true) { + fail(Bool.toText(a), "to be ==", Bool.toText(true)); + }; + }; + isFalse = func() { + if (a != false) { + fail(Bool.toText(a), "to be ==", Bool.toText(false)); + }; + }; + equal = func(b : Bool) { + if (a != b) { + fail(Bool.toText(a), "to be ==", Bool.toText(b)); + }; + }; + notEqual = func(b : Bool) { + if (a == b) { + fail(Bool.toText(a), "to be !=", Bool.toText(b)); + }; + }; + }; + }; + nat = func(a : Nat) : ExpectNum { + makeExpectNum({ + equal = Nat.equal; + notEqual = Nat.notEqual; + less = Nat.less; + lessOrEqual = Nat.lessOrEqual; + greater = Nat.greater; + greaterOrEqual = Nat.greaterOrEqual; + toText = Nat.toText; + })(a); + }; + nat8 = func(a : Nat8) : ExpectNum { + makeExpectNum({ + equal = Nat8.equal; + notEqual = Nat8.notEqual; + less = Nat8.less; + lessOrEqual = Nat8.lessOrEqual; + greater = Nat8.greater; + greaterOrEqual = Nat8.greaterOrEqual; + toText = Nat8.toText; + })(a); + }; + nat16 = func(a : Nat16) : ExpectNum { + makeExpectNum({ + equal = Nat16.equal; + notEqual = Nat16.notEqual; + less = Nat16.less; + lessOrEqual = Nat16.lessOrEqual; + greater = Nat16.greater; + greaterOrEqual = Nat16.greaterOrEqual; + toText = Nat16.toText; + })(a); + }; + nat32 = func(a : Nat32) : ExpectNum { + makeExpectNum({ + equal = Nat32.equal; + notEqual = Nat32.notEqual; + less = Nat32.less; + lessOrEqual = Nat32.lessOrEqual; + greater = Nat32.greater; + greaterOrEqual = Nat32.greaterOrEqual; + toText = Nat32.toText; + })(a); + }; + nat64 = func(a : Nat64) : ExpectNum { + makeExpectNum({ + equal = Nat64.equal; + notEqual = Nat64.notEqual; + less = Nat64.less; + lessOrEqual = Nat64.lessOrEqual; + greater = Nat64.greater; + greaterOrEqual = Nat64.greaterOrEqual; + toText = Nat64.toText; + })(a); + }; + int = func(a : Int) : ExpectNum { + makeExpectNum({ + equal = Int.equal; + notEqual = Int.notEqual; + less = Int.less; + lessOrEqual = Int.lessOrEqual; + greater = Int.greater; + greaterOrEqual = Int.greaterOrEqual; + toText = Int.toText; + })(a); + }; + int8 = func(a : Int8) : ExpectNum { + makeExpectNum({ + equal = Int8.equal; + notEqual = Int8.notEqual; + less = Int8.less; + lessOrEqual = Int8.lessOrEqual; + greater = Int8.greater; + greaterOrEqual = Int8.greaterOrEqual; + toText = Int8.toText; + })(a); + }; + int16 = func(a : Int16) : ExpectNum { + makeExpectNum({ + equal = Int16.equal; + notEqual = Int16.notEqual; + less = Int16.less; + lessOrEqual = Int16.lessOrEqual; + greater = Int16.greater; + greaterOrEqual = Int16.greaterOrEqual; + toText = Int16.toText; + })(a); + }; + int32 = func(a : Int32) : ExpectNum { + makeExpectNum({ + equal = Int32.equal; + notEqual = Int32.notEqual; + less = Int32.less; + lessOrEqual = Int32.lessOrEqual; + greater = Int32.greater; + greaterOrEqual = Int32.greaterOrEqual; + toText = Int32.toText; + })(a); + }; + int64 = func(a : Int64) : ExpectNum { + makeExpectNum({ + equal = Int64.equal; + notEqual = Int64.notEqual; + less = Int64.less; + lessOrEqual = Int64.lessOrEqual; + greater = Int64.greater; + greaterOrEqual = Int64.greaterOrEqual; + toText = Int64.toText; + })(a); + }; + // float = func(val : Float) : ExpectFloat { + // func equalWithin(b : Float, epsilon : Float) : Bool { + // Float.equalWithin(val, b, epsilon); + // }; + // { + // equalWithin = func(b : Float, epsilon : Float) : Bool { + // compare(val, b, equalWithin, Float.toText); + // }; + // notEqualWithin = bindCompare(val, Float.notEqualWithin, Float.toText); + // less = bindCompare(val, Float.less, Float.toText); + // lessOrEqual = bindCompare(val, Float.lessOrEqual, Float.toText); + // greater = bindCompare(val, Float.greater, Float.toText); + // greaterOrEqual = bindCompare(val, Float.greaterOrEqual, Float.toText); + // }; + // }; + char = func(val : Char) : ExpectNum { + { + equal = bindCompare(val, Char.equal, Char.toText); + notEqual = bindCompare(val, Char.notEqual, Char.toText); + less = bindCompare(val, Char.less, Char.toText); + lessOrEqual = bindCompare(val, Char.lessOrEqual, Char.toText); + greater = bindCompare(val, Char.greater, Char.toText); + greaterOrEqual = bindCompare(val, Char.greaterOrEqual, Char.toText); + }; + }; + text = func(val : Text) : ExpectNum { + { + equal = bindCompare(val, Text.equal, func(a) = a); + notEqual = bindCompare(val, Text.notEqual, func(a) = a); + less = bindCompare(val, Text.less, func(a) = a); + lessOrEqual = bindCompare(val, Text.lessOrEqual, func(a) = a); + greater = bindCompare(val, Text.greater, func(a) = a); + greaterOrEqual = bindCompare(val, Text.greaterOrEqual, func(a) = a); + contains = bindCompare(val, func(a : Text, b) = Text.contains(a, #text b), func(a) = a); + startsWith = bindCompare(val, func(a : Text, b) = Text.startsWith(a, #text b), func(a) = a); + endsWith = bindCompare(val, func(a : Text, b) = Text.endsWith(a, #text b), func(a) = a); + }; + }; + array = func(ar : [T], itemToText : (T) -> Text) : {has : (T, (T, T) -> Bool) -> ()} { + func arrayToText(ar : [T]) : Text { + var text = "["; + label l do { + for (i in ar.keys()) { + text #= itemToText(ar[i]); + + if (i + 1 < ar.size()) { + if (text.size() > 100) { + text #= "..."; + break l; + }; + text #= ", "; + }; + }; + text #= "]"; + }; + return text; + }; + + return { + has = func(a : T, equal : (T, T) -> Bool) { + let has = Array.find(ar, func b = equal(a, b)); + if (Option.isNull(has)) { + fail(arrayToText(ar), "to have item", itemToText(a)); + }; + }; + }; + }; + iter = func(iter : Iter.Iter, itemToText : (T) -> Text) : {has : (T, (T, T) -> Bool) -> ()} { + func iterToText(iter : Iter.Iter) : Text { + var text = ""; + let iterCopy = Iter.map(iter, func(x) = x); + var i = 0; + + label l while (true) { + let ?item = iterCopy.next() else break l; + + if (i != 0) { + text #= ", "; + }; + text #= itemToText(item); + i += 1; + + if (text.size() > 100) { + break l; + }; + }; + + if (Option.isNull(iterCopy.next())) { + text #= "..."; + }; + + return text; + }; + + func iterFind(iter : Iter.Iter, predicate : (T) -> Bool) : ?T { + let iterCopy = Iter.map(iter, func(x) = x); + + label l while (true) { + let ?item = iterCopy.next() else return null; + if (predicate(item)) { + return ?item; + }; + }; + + return null; + }; + + return { + has = func(a : T, equal : (T, T) -> Bool) { + let has = iterFind(iter, func b = equal(a, b)); + if (Option.isNull(has)) { + fail(iterToText(iter), "to have item", itemToText(a)); + }; + }; + }; + }; + }; +}; \ No newline at end of file diff --git a/test/expect.test.mo b/test/expect.test.mo new file mode 100644 index 0000000..286b969 --- /dev/null +++ b/test/expect.test.mo @@ -0,0 +1,51 @@ +import Debug "mo:base/Debug"; +import Nat "mo:base/Nat"; +import {expect; compare; makeCompare; fail} "../src/expect"; +import {test; suite} "../src"; + +type Custom = { + x : Nat; + y : Nat; +}; + +class expectCustom(a : Custom) { + func show(x : Custom) : Text = debug_show(x); + + public func equal(b : Custom) { + if (a != b) { + fail(show(a), "to be ==", show(b)); + }; + // compare(a, b, func(a, b) = a == b, show, "to be =="); + // makeCompare(func(a, b) = a == b, show)(a, b); + }; + + public func greater(b : Custom) { + makeCompare(func(a, b) = a.x > b.x and a.y > b.y, show)(a, b); + }; +}; + +test("nat", func() { + let myNat = 33; + expect.nat(myNat).notEqual(22); + expect.nat(myNat).equal(33); + expect.bool(true).isTrue(); + expect.bool(false).isFalse(); + expect.bool(true).equal(true); + expect.bool(false).equal(false); + expect.bool(true).notEqual(false); + expect.nat(myNat).less(66); +}); + +// test("array", func() { +// expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText).has(1, Nat.equal); +// }); + +test("array", func() { + expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText).has(6, Nat.equal); + // expect.iter([1,2,3,4,5,6,7,8,9,0].vals(), Nat.toText).has(21, Nat.equal); +}); + +test("custom", func() { + expectCustom({x = 1; y = 3}).equal({x = 1; y = 3}); + expectCustom({x = 2; y = 4}).greater({x = 1; y = 3}); +}); \ No newline at end of file From 85d5eaedacc18f040c6484feeed58f0678ce9472 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Fri, 8 Sep 2023 17:34:43 +0400 Subject: [PATCH 02/15] - --- src/async.mo | 6 +- src/expect.mo | 195 +++++++++++++++++++++++--------------------------- src/lib.mo | 6 +- 3 files changed, 96 insertions(+), 111 deletions(-) diff --git a/src/async.mo b/src/async.mo index b493bb4..94e35b4 100644 --- a/src/async.mo +++ b/src/async.mo @@ -1,17 +1,17 @@ import Debug "mo:base/Debug"; module { - public func test(name: Text, fn: () -> async ()): async () { + public func test(name : Text, fn : () -> async ()) : async () { Debug.print("mops:1:start " # name); await fn(); Debug.print("mops:1:end " # name); }; - public func suite(name: Text, fn: () -> async ()): async () { + public func suite(name : Text, fn : () -> async ()) : async () { await test(name, fn); }; - public func skip(name: Text, fn: () -> async ()): async () { + public func skip(name : Text, fn : () -> async ()) : async () { Debug.print("mops:1:skip " # name); }; }; \ No newline at end of file diff --git a/src/expect.mo b/src/expect.mo index bb20d4c..d41b124 100644 --- a/src/expect.mo +++ b/src/expect.mo @@ -29,10 +29,10 @@ module { public func fail(actual : Text, condition : Text, reference : Text) { // let prefix = "\1b[31m·\1b[0m"; - // let prefix = "\1b[31m•\1b[0m"; // let prefix = "\1b[31m⚠\1b[0m"; - let prefix = "\1b[31m⌇\1b[0m"; + // let prefix = "\1b[31m⌇\1b[0m"; // let prefix = "\1b[31m!\1b[0m"; + let prefix = "\1b[31m•\1b[0m"; var msg = "\n" # prefix # " expected \1b[31m" # actual # "\1b[0m "; if (condition != "") { msg #= "\n" # prefix # " \1b[30m" # condition # "\1b[0m"; @@ -60,6 +60,13 @@ module { notEqual : Bool -> (); }; + type ExpectOption = { + isNull : () -> (); + // isFalse : () -> (); + // equal : Bool -> (); + // notEqual : Bool -> (); + }; + // type ExpectFloat = { // equalWithin : (Float, Float) -> Bool; // notEqualWithin : (Float, Float) -> Bool; @@ -134,59 +141,31 @@ module { }; }; }; - nat = func(a : Nat) : ExpectNum { - makeExpectNum({ - equal = Nat.equal; - notEqual = Nat.notEqual; - less = Nat.less; - lessOrEqual = Nat.lessOrEqual; - greater = Nat.greater; - greaterOrEqual = Nat.greaterOrEqual; - toText = Nat.toText; - })(a); - }; - nat8 = func(a : Nat8) : ExpectNum { - makeExpectNum({ - equal = Nat8.equal; - notEqual = Nat8.notEqual; - less = Nat8.less; - lessOrEqual = Nat8.lessOrEqual; - greater = Nat8.greater; - greaterOrEqual = Nat8.greaterOrEqual; - toText = Nat8.toText; - })(a); - }; - nat16 = func(a : Nat16) : ExpectNum { - makeExpectNum({ - equal = Nat16.equal; - notEqual = Nat16.notEqual; - less = Nat16.less; - lessOrEqual = Nat16.lessOrEqual; - greater = Nat16.greater; - greaterOrEqual = Nat16.greaterOrEqual; - toText = Nat16.toText; - })(a); - }; - nat32 = func(a : Nat32) : ExpectNum { - makeExpectNum({ - equal = Nat32.equal; - notEqual = Nat32.notEqual; - less = Nat32.less; - lessOrEqual = Nat32.lessOrEqual; - greater = Nat32.greater; - greaterOrEqual = Nat32.greaterOrEqual; - toText = Nat32.toText; - })(a); - }; - nat64 = func(a : Nat64) : ExpectNum { - makeExpectNum({ - equal = Nat64.equal; - notEqual = Nat64.notEqual; - less = Nat64.less; - lessOrEqual = Nat64.lessOrEqual; - greater = Nat64.greater; - greaterOrEqual = Nat64.greaterOrEqual; - toText = Nat64.toText; + // option = func(a : ?T) : ExpectOption { + // return { + // isNull = func() { + // switch (a) { + // case (?v) { + // // ERROR: show is not defined for operand type Any + // fail(debug_show(v), "to be ==", "null"); + // }; + // case (null) {}; + // }; + // // if (a != null) { + // // fail(debug_show(a), "to be ==", "null"); + // // }; + // }; + // }; + // }; + num = func(a : Int) : ExpectNum { + makeExpectNum({ + equal = Int.equal; + notEqual = Int.notEqual; + less = Int.less; + lessOrEqual = Int.lessOrEqual; + greater = Int.greater; + greaterOrEqual = Int.greaterOrEqual; + toText = Int.toText; })(a); }; int = func(a : Int) : ExpectNum { @@ -244,6 +223,61 @@ module { toText = Int64.toText; })(a); }; + nat = func(a : Nat) : ExpectNum { + makeExpectNum({ + equal = Nat.equal; + notEqual = Nat.notEqual; + less = Nat.less; + lessOrEqual = Nat.lessOrEqual; + greater = Nat.greater; + greaterOrEqual = Nat.greaterOrEqual; + toText = Nat.toText; + })(a); + }; + nat8 = func(a : Nat8) : ExpectNum { + makeExpectNum({ + equal = Nat8.equal; + notEqual = Nat8.notEqual; + less = Nat8.less; + lessOrEqual = Nat8.lessOrEqual; + greater = Nat8.greater; + greaterOrEqual = Nat8.greaterOrEqual; + toText = Nat8.toText; + })(a); + }; + nat16 = func(a : Nat16) : ExpectNum { + makeExpectNum({ + equal = Nat16.equal; + notEqual = Nat16.notEqual; + less = Nat16.less; + lessOrEqual = Nat16.lessOrEqual; + greater = Nat16.greater; + greaterOrEqual = Nat16.greaterOrEqual; + toText = Nat16.toText; + })(a); + }; + nat32 = func(a : Nat32) : ExpectNum { + makeExpectNum({ + equal = Nat32.equal; + notEqual = Nat32.notEqual; + less = Nat32.less; + lessOrEqual = Nat32.lessOrEqual; + greater = Nat32.greater; + greaterOrEqual = Nat32.greaterOrEqual; + toText = Nat32.toText; + })(a); + }; + nat64 = func(a : Nat64) : ExpectNum { + makeExpectNum({ + equal = Nat64.equal; + notEqual = Nat64.notEqual; + less = Nat64.less; + lessOrEqual = Nat64.lessOrEqual; + greater = Nat64.greater; + greaterOrEqual = Nat64.greaterOrEqual; + toText = Nat64.toText; + })(a); + }; // float = func(val : Float) : ExpectFloat { // func equalWithin(b : Float, epsilon : Float) : Bool { // Float.equalWithin(val, b, epsilon); @@ -282,7 +316,7 @@ module { endsWith = bindCompare(val, func(a : Text, b) = Text.endsWith(a, #text b), func(a) = a); }; }; - array = func(ar : [T], itemToText : (T) -> Text) : {has : (T, (T, T) -> Bool) -> ()} { + array = func(ar : [T], itemToText : (T) -> Text) : { has : (T, (T, T) -> Bool) -> () } { func arrayToText(ar : [T]) : Text { var text = "["; label l do { @@ -311,54 +345,5 @@ module { }; }; }; - iter = func(iter : Iter.Iter, itemToText : (T) -> Text) : {has : (T, (T, T) -> Bool) -> ()} { - func iterToText(iter : Iter.Iter) : Text { - var text = ""; - let iterCopy = Iter.map(iter, func(x) = x); - var i = 0; - - label l while (true) { - let ?item = iterCopy.next() else break l; - - if (i != 0) { - text #= ", "; - }; - text #= itemToText(item); - i += 1; - - if (text.size() > 100) { - break l; - }; - }; - - if (Option.isNull(iterCopy.next())) { - text #= "..."; - }; - - return text; - }; - - func iterFind(iter : Iter.Iter, predicate : (T) -> Bool) : ?T { - let iterCopy = Iter.map(iter, func(x) = x); - - label l while (true) { - let ?item = iterCopy.next() else return null; - if (predicate(item)) { - return ?item; - }; - }; - - return null; - }; - - return { - has = func(a : T, equal : (T, T) -> Bool) { - let has = iterFind(iter, func b = equal(a, b)); - if (Option.isNull(has)) { - fail(iterToText(iter), "to have item", itemToText(a)); - }; - }; - }; - }; }; }; \ No newline at end of file diff --git a/src/lib.mo b/src/lib.mo index f1ad28b..2ed7e02 100644 --- a/src/lib.mo +++ b/src/lib.mo @@ -1,17 +1,17 @@ import Debug "mo:base/Debug"; module { - public func test(name: Text, fn: () -> ()) { + public func test(name : Text, fn : () -> ()) { Debug.print("mops:1:start " # name); fn(); Debug.print("mops:1:end " # name); }; - public func suite(name: Text, fn: () -> ()) { + public func suite(name : Text, fn : () -> ()) { test(name, fn); }; - public func skip(name: Text, fn: () -> ()) { + public func skip(name : Text, fn : () -> ()) { Debug.print("mops:1:skip " # name); }; }; \ No newline at end of file From dc7ad2e6f57a41912160fdb23ee837241382895e Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Thu, 14 Sep 2023 11:41:03 +0400 Subject: [PATCH 03/15] add expect.option isNull/isSome --- src/expect.mo | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/expect.mo b/src/expect.mo index d41b124..23bd0aa 100644 --- a/src/expect.mo +++ b/src/expect.mo @@ -141,22 +141,25 @@ module { }; }; }; - // option = func(a : ?T) : ExpectOption { - // return { - // isNull = func() { - // switch (a) { - // case (?v) { - // // ERROR: show is not defined for operand type Any - // fail(debug_show(v), "to be ==", "null"); - // }; - // case (null) {}; - // }; - // // if (a != null) { - // // fail(debug_show(a), "to be ==", "null"); - // // }; - // }; - // }; - // }; + option = class(a : ?T, toText : (T) -> Text) { + public func isNull() { + switch (a) { + case (?v) { + fail("?" # toText(v), "to be ==", "null"); + }; + case (null) {}; + }; + }; + + public func isSome() { + switch (a) { + case (?v) {}; + case (null) { + fail("null", "to be !=", "null"); + }; + }; + }; + }; num = func(a : Int) : ExpectNum { makeExpectNum({ equal = Int.equal; From 9830d2c4059fc8f1838d211659e89b33ede54ace Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Thu, 14 Sep 2023 11:41:33 +0400 Subject: [PATCH 04/15] add expect.array equal/notEqual/has/notHas --- src/expect.mo | 58 +++++++++++++++++++++++++++++-------- test/expect.test.mo | 69 ++++++++++++++++++++++++++++----------------- 2 files changed, 89 insertions(+), 38 deletions(-) diff --git a/src/expect.mo b/src/expect.mo index 23bd0aa..3397326 100644 --- a/src/expect.mo +++ b/src/expect.mo @@ -27,7 +27,7 @@ module { }; }; - public func fail(actual : Text, condition : Text, reference : Text) { + public func fail1(actual : Text, condition : Text, reference : Text) { // let prefix = "\1b[31m·\1b[0m"; // let prefix = "\1b[31m⚠\1b[0m"; // let prefix = "\1b[31m⌇\1b[0m"; @@ -45,6 +45,23 @@ module { Debug.trap(msg); }; + + public func fail2(actual : Text, condition : Text, reference : Text) { + let prefix = "\1b[31m•\1b[0m"; + var msg = "\n" # prefix # " received \1b[31m" # actual # "\1b[0m "; + if (condition != "") { + msg #= "\n" # prefix # " expected" # " \1b[30m" # condition # "\1b[0m"; + } + else { + msg #= prefix # ""; + }; + msg #= " " # reference # ""; + + Debug.trap(msg); + }; + + public let fail = fail1; + public func makeCompare(comp : (T, T) -> Bool, toText : (T) -> Text) : (T, T) -> () { return func(a : T, b : T) = compare(a, b, comp, toText, "=="); }; @@ -319,14 +336,14 @@ module { endsWith = bindCompare(val, func(a : Text, b) = Text.endsWith(a, #text b), func(a) = a); }; }; - array = func(ar : [T], itemToText : (T) -> Text) : { has : (T, (T, T) -> Bool) -> () } { - func arrayToText(ar : [T]) : Text { + array = class(arr : [T], itemToText : (T) -> Text, itemEqual : (T, T) -> Bool) { + func _arrayToText(arr : [T]) : Text { var text = "["; label l do { - for (i in ar.keys()) { - text #= itemToText(ar[i]); + for (i in arr.keys()) { + text #= itemToText(arr[i]); - if (i + 1 < ar.size()) { + if (i + 1 < arr.size()) { if (text.size() > 100) { text #= "..."; break l; @@ -339,12 +356,29 @@ module { return text; }; - return { - has = func(a : T, equal : (T, T) -> Bool) { - let has = Array.find(ar, func b = equal(a, b)); - if (Option.isNull(has)) { - fail(arrayToText(ar), "to have item", itemToText(a)); - }; + public func equal(other : [T]) { + if (not Array.equal(arr, other, itemEqual)) { + fail(_arrayToText(arr), "to be ==", _arrayToText(other)); + }; + }; + + public func notEqual(other : [T]) { + if (Array.equal(arr, other, itemEqual)) { + fail(_arrayToText(arr), "to be ==", _arrayToText(other)); + }; + }; + + public func has(a : T) { + let has = Array.find(arr, func b = itemEqual(a, b)); + if (Option.isNull(has)) { + fail(_arrayToText(arr), "to have item", itemToText(a)); + }; + }; + + public func notHas(a : T) { + let has = Array.find(arr, func b = itemEqual(a, b)); + if (Option.isSome(has)) { + fail(_arrayToText(arr), "to have item", itemToText(a)); }; }; }; diff --git a/test/expect.test.mo b/test/expect.test.mo index 286b969..44e6861 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -3,26 +3,18 @@ import Nat "mo:base/Nat"; import {expect; compare; makeCompare; fail} "../src/expect"; import {test; suite} "../src"; -type Custom = { - x : Nat; - y : Nat; -}; - -class expectCustom(a : Custom) { - func show(x : Custom) : Text = debug_show(x); - - public func equal(b : Custom) { - if (a != b) { - fail(show(a), "to be ==", show(b)); - }; - // compare(a, b, func(a, b) = a == b, show, "to be =="); - // makeCompare(func(a, b) = a == b, show)(a, b); - }; +test("bool", func() { + expect.bool(true).isTrue(); + expect.bool(false).isFalse(); + expect.bool(true).equal(true); + expect.bool(false).equal(false); + expect.bool(true).notEqual(false); +}); - public func greater(b : Custom) { - makeCompare(func(a, b) = a.x > b.x and a.y > b.y, show)(a, b); - }; -}; +test("option", func() { + expect.option(null, Nat.toText).isNull(); + expect.option(?1, Nat.toText).isSome(); +}); test("nat", func() { let myNat = 33; @@ -36,16 +28,41 @@ test("nat", func() { expect.nat(myNat).less(66); }); -// test("array", func() { -// expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText).has(1, Nat.equal); -// }); +test("array has", func() { + expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal).has(6); + let exAr = expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal); + exAr.has(6); + exAr.has(1); + exAr.has(0); +}); -test("array", func() { - expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText).has(6, Nat.equal); - // expect.iter([1,2,3,4,5,6,7,8,9,0].vals(), Nat.toText).has(21, Nat.equal); +test("array equal", func() { + expect.array([1,2,3,4], Nat.toText, Nat.equal).equal([1,2,3,4]); + expect.array([1,2,3,4], Nat.toText, Nat.equal).notEqual([1,2,2,4]); + expect.array([1,2,3,4], Nat.toText, Nat.equal).notEqual([1,2,3,4,5]); + expect.array([1,2,3,4], Nat.toText, Nat.equal).notEqual([1,2,3]); }); -test("custom", func() { +test("expect custom", func() { + type Custom = { + x : Nat; + y : Nat; + }; + + class expectCustom(a : Custom) { + func show(x : Custom) : Text = debug_show(x); + + public func equal(b : Custom) { + if (a != b) { + fail(show(a), "to be ==", show(b)); + }; + }; + + public func greater(b : Custom) { + makeCompare(func(a, b) = a.x > b.x and a.y > b.y, show)(a, b); + }; + }; + expectCustom({x = 1; y = 3}).equal({x = 1; y = 3}); expectCustom({x = 2; y = 4}).greater({x = 1; y = 3}); }); \ No newline at end of file From bdaccdfe2a36cad7dac0a650906a667b818a1089 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Thu, 14 Sep 2023 11:41:38 +0400 Subject: [PATCH 05/15] update README --- README.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 8901972..707dca9 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ Run `mops test` to run tests. import {test} "mo:test"; test("simple test", func() { - assert true; + assert true; }); test("test my number", func() { - assert 1 > 0; + assert 1 > 0; }); ``` @@ -36,13 +36,13 @@ Use `suite` to group your tests. import {test; suite} "mo:test"; suite("my test suite", func() { - test("simple test", func() { - assert true; - }); + test("simple test", func() { + assert true; + }); - test("test my number", func() { - assert 1 > 0; - }); + test("test my number", func() { + assert 1 > 0; + }); }); ``` @@ -53,11 +53,11 @@ Use `skip` to skip tests. import {test; skip} "mo:test"; skip("this test will never run", func() { - assert false; + assert false; }); test("this test will run", func() { - assert true; + assert true; }); ``` @@ -67,16 +67,16 @@ If there are `await`s in your tests, use functions from `mo:test/async`. ```motoko import {test; suite} "mo:test/async"; -await suite("my async test suite", func(): async () { - await test("async test", func(): async () { - let res = await myAsyncFn(); - assert Result.isOk(res); - }); - - test("should generate unique values", func(): async () { - let a = await generate(); - let b = await generate(); - assert a != b; - }); +await suite("my async test suite", func() : async () { + await test("async test", func() : async () { + let res = await myAsyncFn(); + assert Result.isOk(res); + }); + + test("should generate unique values", func() : async () { + let a = await generate(); + let b = await generate(); + assert a != b; + }); }); ``` From 1298796abbfcf918591fd2e14faee6a9b0c7bb7c Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Fri, 22 Sep 2023 11:42:08 +0400 Subject: [PATCH 06/15] refactor expect IntX, NatX; add expect.blob --- mops.toml | 2 +- src/async.mo | 4 + src/expect.mo | 386 ------------------------------------ src/expect/expect-array.mo | 53 +++++ src/expect/expect-blob.mo | 18 ++ src/expect/expect-bool.mo | 27 +++ src/expect/expect-char.mo | 15 ++ src/expect/expect-int.mo | 13 ++ src/expect/expect-option.mo | 61 ++++++ src/expect/expect-text.mo | 18 ++ src/expect/lib.mo | 52 +++++ src/expect/utils.mo | 62 ++++++ src/lib.mo | 4 + test/expect.test.mo | 54 ++++- 14 files changed, 372 insertions(+), 397 deletions(-) delete mode 100644 src/expect.mo create mode 100644 src/expect/expect-array.mo create mode 100644 src/expect/expect-blob.mo create mode 100644 src/expect/expect-bool.mo create mode 100644 src/expect/expect-char.mo create mode 100644 src/expect/expect-int.mo create mode 100644 src/expect/expect-option.mo create mode 100644 src/expect/expect-text.mo create mode 100644 src/expect/lib.mo create mode 100644 src/expect/utils.mo diff --git a/mops.toml b/mops.toml index 0978f62..03636bc 100644 --- a/mops.toml +++ b/mops.toml @@ -7,4 +7,4 @@ keywords = [ "test", "mops", "testing", "unit", "suite" ] license = "MIT" [dependencies] -base = "0.8.1" \ No newline at end of file +base = "0.10.0" \ No newline at end of file diff --git a/src/async.mo b/src/async.mo index 94e35b4..7aa56b3 100644 --- a/src/async.mo +++ b/src/async.mo @@ -1,4 +1,5 @@ import Debug "mo:base/Debug"; +import {expect = _expect; fail = _fail} "./expect"; module { public func test(name : Text, fn : () -> async ()) : async () { @@ -14,4 +15,7 @@ module { public func skip(name : Text, fn : () -> async ()) : async () { Debug.print("mops:1:skip " # name); }; + + public let expect = _expect; + public let fail = _fail; }; \ No newline at end of file diff --git a/src/expect.mo b/src/expect.mo deleted file mode 100644 index 3397326..0000000 --- a/src/expect.mo +++ /dev/null @@ -1,386 +0,0 @@ -import Debug "mo:base/Debug"; -import Nat "mo:base/Nat"; -import Nat8 "mo:base/Nat8"; -import Nat16 "mo:base/Nat16"; -import Nat32 "mo:base/Nat32"; -import Nat64 "mo:base/Nat64"; -import Int "mo:base/Int"; -import Int8 "mo:base/Int8"; -import Int16 "mo:base/Int16"; -import Int32 "mo:base/Int32"; -import Int64 "mo:base/Int64"; -import Float "mo:base/Float"; -import Char "mo:base/Char"; -import Text "mo:base/Text"; -import TrieMap "mo:base/TrieMap"; -import Hash "mo:base/Hash"; -import Array "mo:base/Array"; -import Option "mo:base/Option"; -import Bool "mo:base/Bool"; -import Iter "mo:base/Iter"; - -module { - public func compare(a : T, b : T, comp : (T, T) -> Bool, toText : (T) -> Text, operator : Text) { - let res = comp(a, b); - if (not res) { - fail(toText(b), operator, toText(a)); - }; - }; - - public func fail1(actual : Text, condition : Text, reference : Text) { - // let prefix = "\1b[31m·\1b[0m"; - // let prefix = "\1b[31m⚠\1b[0m"; - // let prefix = "\1b[31m⌇\1b[0m"; - // let prefix = "\1b[31m!\1b[0m"; - let prefix = "\1b[31m•\1b[0m"; - var msg = "\n" # prefix # " expected \1b[31m" # actual # "\1b[0m "; - if (condition != "") { - msg #= "\n" # prefix # " \1b[30m" # condition # "\1b[0m"; - } - else { - msg #= prefix # ""; - }; - msg #= " " # reference # ""; - - Debug.trap(msg); - }; - - - public func fail2(actual : Text, condition : Text, reference : Text) { - let prefix = "\1b[31m•\1b[0m"; - var msg = "\n" # prefix # " received \1b[31m" # actual # "\1b[0m "; - if (condition != "") { - msg #= "\n" # prefix # " expected" # " \1b[30m" # condition # "\1b[0m"; - } - else { - msg #= prefix # ""; - }; - msg #= " " # reference # ""; - - Debug.trap(msg); - }; - - public let fail = fail1; - - public func makeCompare(comp : (T, T) -> Bool, toText : (T) -> Text) : (T, T) -> () { - return func(a : T, b : T) = compare(a, b, comp, toText, "=="); - }; - - public func bindCompare(a : T, comp : (T, T) -> Bool, toText : (T) -> Text) : (T) -> () { - return func(b : T) = compare(a, b, comp, toText, "to be =="); - }; - - type ExpectBool = { - isTrue : () -> (); - isFalse : () -> (); - equal : Bool -> (); - notEqual : Bool -> (); - }; - - type ExpectOption = { - isNull : () -> (); - // isFalse : () -> (); - // equal : Bool -> (); - // notEqual : Bool -> (); - }; - - // type ExpectFloat = { - // equalWithin : (Float, Float) -> Bool; - // notEqualWithin : (Float, Float) -> Bool; - // less : (Float) -> Bool; - // lessOrEqual : (Float) -> Bool; - // greater : (Float) -> Bool; - // greaterOrEqual : (Float) -> Bool; - // }; - - type ExpectNum = { - equal : (T) -> (); - notEqual : (T) -> (); - less : (T) -> (); - lessOrEqual : (T) -> (); - greater : (T) -> (); - greaterOrEqual : (T) -> (); - }; - - type NumModule = { - equal : (T, T) -> Bool; - notEqual : (T, T) -> Bool; - less : (T, T) -> Bool; - lessOrEqual : (T, T) -> Bool; - greater : (T, T) -> Bool; - greaterOrEqual : (T, T) -> Bool; - toText : (T) -> Text; - }; - - func makeExpectNum(mod : NumModule) : T -> ExpectNum { - func makeNumCompare(a : T, comp : (T, T) -> Bool, toText : (T) -> Text, condition : Text) : (T) -> () { - return func(b : T) { - if (not comp(a, b)) { - fail(toText(a), condition, toText(b)); - }; - }; - }; - - return func(val : T) : ExpectNum { - return { - equal = makeNumCompare(val, mod.equal, mod.toText, "to be =="); - notEqual = makeNumCompare(val, mod.notEqual, mod.toText, "to be !="); - less = makeNumCompare(val, mod.less, mod.toText, "to be <"); - lessOrEqual = makeNumCompare(val, mod.lessOrEqual, mod.toText, "to be <="); - greater = makeNumCompare(val, mod.greater, mod.toText, "to be >"); - greaterOrEqual = makeNumCompare(val, mod.greaterOrEqual, mod.toText, "to be >="); - } - }; - }; - - public let expect = { - bool = func(a : Bool) : ExpectBool { - return { - isTrue = func() { - if (a != true) { - fail(Bool.toText(a), "to be ==", Bool.toText(true)); - }; - }; - isFalse = func() { - if (a != false) { - fail(Bool.toText(a), "to be ==", Bool.toText(false)); - }; - }; - equal = func(b : Bool) { - if (a != b) { - fail(Bool.toText(a), "to be ==", Bool.toText(b)); - }; - }; - notEqual = func(b : Bool) { - if (a == b) { - fail(Bool.toText(a), "to be !=", Bool.toText(b)); - }; - }; - }; - }; - option = class(a : ?T, toText : (T) -> Text) { - public func isNull() { - switch (a) { - case (?v) { - fail("?" # toText(v), "to be ==", "null"); - }; - case (null) {}; - }; - }; - - public func isSome() { - switch (a) { - case (?v) {}; - case (null) { - fail("null", "to be !=", "null"); - }; - }; - }; - }; - num = func(a : Int) : ExpectNum { - makeExpectNum({ - equal = Int.equal; - notEqual = Int.notEqual; - less = Int.less; - lessOrEqual = Int.lessOrEqual; - greater = Int.greater; - greaterOrEqual = Int.greaterOrEqual; - toText = Int.toText; - })(a); - }; - int = func(a : Int) : ExpectNum { - makeExpectNum({ - equal = Int.equal; - notEqual = Int.notEqual; - less = Int.less; - lessOrEqual = Int.lessOrEqual; - greater = Int.greater; - greaterOrEqual = Int.greaterOrEqual; - toText = Int.toText; - })(a); - }; - int8 = func(a : Int8) : ExpectNum { - makeExpectNum({ - equal = Int8.equal; - notEqual = Int8.notEqual; - less = Int8.less; - lessOrEqual = Int8.lessOrEqual; - greater = Int8.greater; - greaterOrEqual = Int8.greaterOrEqual; - toText = Int8.toText; - })(a); - }; - int16 = func(a : Int16) : ExpectNum { - makeExpectNum({ - equal = Int16.equal; - notEqual = Int16.notEqual; - less = Int16.less; - lessOrEqual = Int16.lessOrEqual; - greater = Int16.greater; - greaterOrEqual = Int16.greaterOrEqual; - toText = Int16.toText; - })(a); - }; - int32 = func(a : Int32) : ExpectNum { - makeExpectNum({ - equal = Int32.equal; - notEqual = Int32.notEqual; - less = Int32.less; - lessOrEqual = Int32.lessOrEqual; - greater = Int32.greater; - greaterOrEqual = Int32.greaterOrEqual; - toText = Int32.toText; - })(a); - }; - int64 = func(a : Int64) : ExpectNum { - makeExpectNum({ - equal = Int64.equal; - notEqual = Int64.notEqual; - less = Int64.less; - lessOrEqual = Int64.lessOrEqual; - greater = Int64.greater; - greaterOrEqual = Int64.greaterOrEqual; - toText = Int64.toText; - })(a); - }; - nat = func(a : Nat) : ExpectNum { - makeExpectNum({ - equal = Nat.equal; - notEqual = Nat.notEqual; - less = Nat.less; - lessOrEqual = Nat.lessOrEqual; - greater = Nat.greater; - greaterOrEqual = Nat.greaterOrEqual; - toText = Nat.toText; - })(a); - }; - nat8 = func(a : Nat8) : ExpectNum { - makeExpectNum({ - equal = Nat8.equal; - notEqual = Nat8.notEqual; - less = Nat8.less; - lessOrEqual = Nat8.lessOrEqual; - greater = Nat8.greater; - greaterOrEqual = Nat8.greaterOrEqual; - toText = Nat8.toText; - })(a); - }; - nat16 = func(a : Nat16) : ExpectNum { - makeExpectNum({ - equal = Nat16.equal; - notEqual = Nat16.notEqual; - less = Nat16.less; - lessOrEqual = Nat16.lessOrEqual; - greater = Nat16.greater; - greaterOrEqual = Nat16.greaterOrEqual; - toText = Nat16.toText; - })(a); - }; - nat32 = func(a : Nat32) : ExpectNum { - makeExpectNum({ - equal = Nat32.equal; - notEqual = Nat32.notEqual; - less = Nat32.less; - lessOrEqual = Nat32.lessOrEqual; - greater = Nat32.greater; - greaterOrEqual = Nat32.greaterOrEqual; - toText = Nat32.toText; - })(a); - }; - nat64 = func(a : Nat64) : ExpectNum { - makeExpectNum({ - equal = Nat64.equal; - notEqual = Nat64.notEqual; - less = Nat64.less; - lessOrEqual = Nat64.lessOrEqual; - greater = Nat64.greater; - greaterOrEqual = Nat64.greaterOrEqual; - toText = Nat64.toText; - })(a); - }; - // float = func(val : Float) : ExpectFloat { - // func equalWithin(b : Float, epsilon : Float) : Bool { - // Float.equalWithin(val, b, epsilon); - // }; - // { - // equalWithin = func(b : Float, epsilon : Float) : Bool { - // compare(val, b, equalWithin, Float.toText); - // }; - // notEqualWithin = bindCompare(val, Float.notEqualWithin, Float.toText); - // less = bindCompare(val, Float.less, Float.toText); - // lessOrEqual = bindCompare(val, Float.lessOrEqual, Float.toText); - // greater = bindCompare(val, Float.greater, Float.toText); - // greaterOrEqual = bindCompare(val, Float.greaterOrEqual, Float.toText); - // }; - // }; - char = func(val : Char) : ExpectNum { - { - equal = bindCompare(val, Char.equal, Char.toText); - notEqual = bindCompare(val, Char.notEqual, Char.toText); - less = bindCompare(val, Char.less, Char.toText); - lessOrEqual = bindCompare(val, Char.lessOrEqual, Char.toText); - greater = bindCompare(val, Char.greater, Char.toText); - greaterOrEqual = bindCompare(val, Char.greaterOrEqual, Char.toText); - }; - }; - text = func(val : Text) : ExpectNum { - { - equal = bindCompare(val, Text.equal, func(a) = a); - notEqual = bindCompare(val, Text.notEqual, func(a) = a); - less = bindCompare(val, Text.less, func(a) = a); - lessOrEqual = bindCompare(val, Text.lessOrEqual, func(a) = a); - greater = bindCompare(val, Text.greater, func(a) = a); - greaterOrEqual = bindCompare(val, Text.greaterOrEqual, func(a) = a); - contains = bindCompare(val, func(a : Text, b) = Text.contains(a, #text b), func(a) = a); - startsWith = bindCompare(val, func(a : Text, b) = Text.startsWith(a, #text b), func(a) = a); - endsWith = bindCompare(val, func(a : Text, b) = Text.endsWith(a, #text b), func(a) = a); - }; - }; - array = class(arr : [T], itemToText : (T) -> Text, itemEqual : (T, T) -> Bool) { - func _arrayToText(arr : [T]) : Text { - var text = "["; - label l do { - for (i in arr.keys()) { - text #= itemToText(arr[i]); - - if (i + 1 < arr.size()) { - if (text.size() > 100) { - text #= "..."; - break l; - }; - text #= ", "; - }; - }; - text #= "]"; - }; - return text; - }; - - public func equal(other : [T]) { - if (not Array.equal(arr, other, itemEqual)) { - fail(_arrayToText(arr), "to be ==", _arrayToText(other)); - }; - }; - - public func notEqual(other : [T]) { - if (Array.equal(arr, other, itemEqual)) { - fail(_arrayToText(arr), "to be ==", _arrayToText(other)); - }; - }; - - public func has(a : T) { - let has = Array.find(arr, func b = itemEqual(a, b)); - if (Option.isNull(has)) { - fail(_arrayToText(arr), "to have item", itemToText(a)); - }; - }; - - public func notHas(a : T) { - let has = Array.find(arr, func b = itemEqual(a, b)); - if (Option.isSome(has)) { - fail(_arrayToText(arr), "to have item", itemToText(a)); - }; - }; - }; - }; -}; \ No newline at end of file diff --git a/src/expect/expect-array.mo b/src/expect/expect-array.mo new file mode 100644 index 0000000..c8b5501 --- /dev/null +++ b/src/expect/expect-array.mo @@ -0,0 +1,53 @@ +import Array "mo:base/Array"; +import Option "mo:base/Option"; +import Debug "mo:base/Debug"; +import {fail} "./utils"; + +module { + public class ExpectArray(arr : [T], itemToText : (T) -> Text, itemEqual : (T, T) -> Bool) { + func _arrayToText(arr : [T]) : Text { + var text = "["; + label l do { + for (i in arr.keys()) { + text #= itemToText(arr[i]); + + if (i + 1 < arr.size()) { + if (text.size() > 100) { + text #= "..."; + break l; + }; + text #= ", "; + }; + }; + text #= "]"; + }; + return text; + }; + + public func equal(other : [T]) { + if (not Array.equal(arr, other, itemEqual)) { + fail(_arrayToText(arr), "", _arrayToText(other)); + }; + }; + + public func notEqual(other : [T]) { + if (Array.equal(arr, other, itemEqual)) { + fail(_arrayToText(arr), "", _arrayToText(other)); + }; + }; + + public func has(a : T) { + let has = Array.find(arr, func b = itemEqual(a, b)); + if (Option.isNull(has)) { + fail(_arrayToText(arr), "to have item", itemToText(a)); + }; + }; + + public func notHas(a : T) { + let has = Array.find(arr, func b = itemEqual(a, b)); + if (Option.isSome(has)) { + fail(_arrayToText(arr), "to not have item", itemToText(a)); + }; + }; + }; +}; \ No newline at end of file diff --git a/src/expect/expect-blob.mo b/src/expect/expect-blob.mo new file mode 100644 index 0000000..59e6c72 --- /dev/null +++ b/src/expect/expect-blob.mo @@ -0,0 +1,18 @@ +import Blob "mo:base/Blob"; +import Array "mo:base/Array"; +import Option "mo:base/Option"; +import Debug "mo:base/Debug"; +import {bindCompare} "./utils"; + +module { + public class ExpectBlob(val : Blob) { + func show(v : Blob) : Text = "" # debug_show(v) # ""; + + public let equal = bindCompare(val, Blob.equal, show, ""); + public let notEqual = bindCompare(val, Blob.notEqual, show, "!="); + public let less = bindCompare(val, Blob.less, show, "<"); + public let lessOrEqual = bindCompare(val, Blob.lessOrEqual, show, "<="); + public let greater = bindCompare(val, Blob.greater, show, ">"); + public let greaterOrEqual = bindCompare(val, Blob.greaterOrEqual, show, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-bool.mo b/src/expect/expect-bool.mo new file mode 100644 index 0000000..6a84faa --- /dev/null +++ b/src/expect/expect-bool.mo @@ -0,0 +1,27 @@ +import Bool "mo:base/Bool"; +import {fail} "./utils"; + +module { + public class ExpectBool(a : Bool) { + public func isTrue() { + if (a != true) { + fail(Bool.toText(a), "", Bool.toText(true)); + }; + }; + public func isFalse() { + if (a != false) { + fail(Bool.toText(a), "", Bool.toText(false)); + }; + }; + public func equal(b : Bool) { + if (a != b) { + fail(Bool.toText(a), "", Bool.toText(b)); + }; + }; + public func notEqual(b : Bool) { + if (a == b) { + fail(Bool.toText(a), "to be !=", Bool.toText(b)); + }; + }; + }; +}; \ No newline at end of file diff --git a/src/expect/expect-char.mo b/src/expect/expect-char.mo new file mode 100644 index 0000000..e1d5de3 --- /dev/null +++ b/src/expect/expect-char.mo @@ -0,0 +1,15 @@ +import Char "mo:base/Char"; +import {bindCompare} "./utils"; + +module { + public class ExpectChar(val : Char) { + func show(c : Char) : Text = "'" # Char.toText(c) # "'"; + + public let equal = bindCompare(val, Char.equal, show, ""); + public let notEqual = bindCompare(val, Char.notEqual, show, "!="); + public let less = bindCompare(val, Char.less, show, "<"); + public let lessOrEqual = bindCompare(val, Char.lessOrEqual, show, "<="); + public let greater = bindCompare(val, Char.greater, show, ">"); + public let greaterOrEqual = bindCompare(val, Char.greaterOrEqual, show, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-int.mo b/src/expect/expect-int.mo new file mode 100644 index 0000000..fcadfd6 --- /dev/null +++ b/src/expect/expect-int.mo @@ -0,0 +1,13 @@ +import Int "mo:base/Int"; +import {bindCompare} "./utils"; + +module { + public class ExpectInt(val : Int) { + public let equal = bindCompare(val, Int.equal, Int.toText, ""); + public let notEqual = bindCompare(val, Int.notEqual, Int.toText, "!="); + public let less = bindCompare(val, Int.less, Int.toText, "<"); + public let lessOrEqual = bindCompare(val, Int.lessOrEqual, Int.toText, "<="); + public let greater = bindCompare(val, Int.greater, Int.toText, ">"); + public let greaterOrEqual = bindCompare(val, Int.greaterOrEqual, Int.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-option.mo b/src/expect/expect-option.mo new file mode 100644 index 0000000..c80902d --- /dev/null +++ b/src/expect/expect-option.mo @@ -0,0 +1,61 @@ +import Option "mo:base/Option"; +import {fail} "./utils"; + +module { + public class ExpectOption(a : ?T, toText : (T) -> Text, equalFn : (T, T) -> Bool) { + func _show(opt : ?T) : Text { + switch (opt) { + case (?v) "?" # toText(v); + case (null) "null"; + }; + }; + + public func _equal(b : ?T) : Bool { + switch (a) { + case (?aVal) { + switch (b) { + case (?bVal) { + if (equalFn(aVal, bVal)) { + true; + } + else { + false; + }; + }; + case (null) { + false; + }; + }; + }; + case (null) { + if (Option.isNull(b)) { + true; + } + else { + false; + }; + }; + }; + }; + + public func equal(b : ?T) { + if (not _equal(b)) { + fail(_show(a), "", _show(b)); + }; + }; + + public func notEqual(b : ?T) { + if (_equal(b)) { + fail(_show(a), "!=", _show(b)); + }; + }; + + public func isNull() { + equal(null); + }; + + public func isSome() { + notEqual(null); + }; + }; +}; \ No newline at end of file diff --git a/src/expect/expect-text.mo b/src/expect/expect-text.mo new file mode 100644 index 0000000..a4cccec --- /dev/null +++ b/src/expect/expect-text.mo @@ -0,0 +1,18 @@ +import Text "mo:base/Text"; +import {bindCompare} "./utils"; + +module { + public class ExpectText(val : Text) { + func show(t : Text) : Text = "\"" # t # "\""; + + public let equal = bindCompare(val, Text.equal, show, ""); + public let notEqual = bindCompare(val, Text.notEqual, show, "!="); + public let less = bindCompare(val, Text.less, show, "<"); + public let lessOrEqual = bindCompare(val, Text.lessOrEqual, show, "<="); + public let greater = bindCompare(val, Text.greater, show, ">"); + public let greaterOrEqual = bindCompare(val, Text.greaterOrEqual, show, ">="); + public let contains = bindCompare(val, func(a : Text, b) = Text.contains(a, #text b), show, "to contain"); + public let startsWith = bindCompare(val, func(a : Text, b) = Text.startsWith(a, #text b), show, "to start with"); + public let endsWith = bindCompare(val, func(a : Text, b) = Text.endsWith(a, #text b), show, "to end with"); + }; +}; \ No newline at end of file diff --git a/src/expect/lib.mo b/src/expect/lib.mo new file mode 100644 index 0000000..05164c1 --- /dev/null +++ b/src/expect/lib.mo @@ -0,0 +1,52 @@ +import Debug "mo:base/Debug"; +import Nat "mo:base/Nat"; +import Nat8 "mo:base/Nat8"; +import Nat16 "mo:base/Nat16"; +import Nat32 "mo:base/Nat32"; +import Nat64 "mo:base/Nat64"; +import Int "mo:base/Int"; +import Int8 "mo:base/Int8"; +import Int16 "mo:base/Int16"; +import Int32 "mo:base/Int32"; +import Int64 "mo:base/Int64"; +import Float "mo:base/Float"; +import Char "mo:base/Char"; +import Text "mo:base/Text"; +import TrieMap "mo:base/TrieMap"; +import Hash "mo:base/Hash"; +import Array "mo:base/Array"; +import Option "mo:base/Option"; +import Bool "mo:base/Bool"; +import Iter "mo:base/Iter"; + +import {bindCompare; fail = _fail} "./utils"; +import ExpectInt "./expect-int"; +import ExpectChar "./expect-char"; +import ExpectText "./expect-text"; +import ExpectBool "./expect-bool"; +import ExpectArray "./expect-array"; +import ExpectBlob "./expect-blob"; +import ExpectOption "./expect-option"; + +module { + public let expect = { + bool = ExpectBool.ExpectBool; + option = ExpectOption.ExpectOption; + int = ExpectInt.ExpectInt; + int8 = func(val : Int8) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int8.toInt(val)); + int16 = func(val : Int16) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int16.toInt(val)); + int32 = func(val : Int32) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int32.toInt(val)); + int64 = func(val : Int64) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int64.toInt(val)); + nat = func(val : Nat) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(val); + nat8 = func(val : Nat8) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat8.toNat(val)); + nat16 = func(val : Nat16) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat16.toNat(val)); + nat32 = func(val : Nat32) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat32.toNat(val)); + nat64 = func(val : Nat64) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat64.toNat(val)); + char = ExpectChar.ExpectChar; + text = ExpectText.ExpectText; + array = ExpectArray.ExpectArray; + blob = ExpectBlob.ExpectBlob; + }; + + public let fail = _fail; +}; \ No newline at end of file diff --git a/src/expect/utils.mo b/src/expect/utils.mo new file mode 100644 index 0000000..cf6866e --- /dev/null +++ b/src/expect/utils.mo @@ -0,0 +1,62 @@ +import Debug "mo:base/Debug"; + +module { + public func fail1(actual : Text, condition : Text, reference : Text) { + // let prefix = "\1b[31m·\1b[0m"; + // let prefix = "\1b[31m⚠\1b[0m"; + // let prefix = "\1b[31m⌇\1b[0m"; + // let prefix = "\1b[31m!\1b[0m"; + let prefix = "\1b[31m•\1b[0m"; + var msg = "\n" # prefix # " expected \1b[31m" # actual # "\1b[0m "; + if (condition != "") { + msg #= "\n" # prefix # " \1b[30m" # condition # "\1b[0m"; + } + else { + msg #= prefix # ""; + }; + msg #= " " # reference # ""; + + Debug.trap(msg); + }; + + public func fail2(actual : Text, condition : Text, reference : Text) { + let prefix = "\1b[31m•\1b[0m"; + var msg = "\n" # prefix # " received \1b[31m" # actual # "\1b[0m "; + if (condition != "") { + msg #= "\n" # prefix # " expected" # " \1b[30m" # condition # "\1b[0m"; + } + else { + msg #= prefix # ""; + }; + msg #= " " # reference # ""; + + Debug.trap(msg); + }; + + public func fail3(actual : Text, condition : Text, reference : Text) { + let prefix = "\1b[31m!\1b[0m"; + var msg = "\n" # prefix # " \1b[30mreceived\1b[0m \1b[31m" # actual # "\1b[0m "; + if (condition != "") { + msg #= "\n" # prefix # " \1b[30mexpected\1b[0m" # " \1b[30m" # condition # "\1b[0m"; + } + else { + msg #= "\n" # prefix # " \1b[30mexpected\1b[0m"; + }; + msg #= " \1b[32m" # reference # "\1b[0m"; + + Debug.trap(msg); + }; + + public let fail = fail3; + + public func compare(a : T, b : T, comp : (T, T) -> Bool, toText : (T) -> Text, condition : Text) { + let res = comp(a, b); + if (not res) { + fail(toText(b), condition, toText(a)); + }; + }; + + public func bindCompare(a : T, comp : (T, T) -> Bool, toText : (T) -> Text, condition : Text) : (T) -> () { + return func(b : T) = compare(a, b, comp, toText, condition); + }; +}; \ No newline at end of file diff --git a/src/lib.mo b/src/lib.mo index 2ed7e02..dc8b787 100644 --- a/src/lib.mo +++ b/src/lib.mo @@ -1,4 +1,5 @@ import Debug "mo:base/Debug"; +import {expect = _expect; fail = _fail} "./expect"; module { public func test(name : Text, fn : () -> ()) { @@ -14,4 +15,7 @@ module { public func skip(name : Text, fn : () -> ()) { Debug.print("mops:1:skip " # name); }; + + public let expect = _expect; + public let fail = _fail; }; \ No newline at end of file diff --git a/test/expect.test.mo b/test/expect.test.mo index 44e6861..e28ea78 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -1,7 +1,7 @@ import Debug "mo:base/Debug"; import Nat "mo:base/Nat"; -import {expect; compare; makeCompare; fail} "../src/expect"; -import {test; suite} "../src"; +import Blob "mo:base/Blob"; +import {test; suite; expect; fail} "../src"; test("bool", func() { expect.bool(true).isTrue(); @@ -12,28 +12,54 @@ test("bool", func() { }); test("option", func() { - expect.option(null, Nat.toText).isNull(); - expect.option(?1, Nat.toText).isSome(); + expect.option(null, Nat.toText, Nat.equal).isNull(); + expect.option(?1, Nat.toText, Nat.equal).isSome(); + expect.option(?2, Nat.toText, Nat.equal).equal(?2); + expect.option(?3, Nat.toText, Nat.equal).notEqual(?44); + expect.option(?3, Nat.toText, Nat.equal).notEqual(null); + expect.option(null, Nat.toText, Nat.equal).equal(null); +}); + +test("char", func() { + expect.char('a').equal('a'); + expect.char('a').notEqual('A'); +}); + +test("text", func() { + expect.text("hello motoko").endsWith("motoko"); + expect.text("hello motoko").contains("mot"); }); test("nat", func() { let myNat = 33; expect.nat(myNat).notEqual(22); expect.nat(myNat).equal(33); - expect.bool(true).isTrue(); - expect.bool(false).isFalse(); - expect.bool(true).equal(true); - expect.bool(false).equal(false); - expect.bool(true).notEqual(false); expect.nat(myNat).less(66); }); +test("nat", func() { + let myNat : Nat = 22; + let myNat8 : Nat8 = 33; + let myInt : Int = -44; + let myFloat : Float = 1.313; + expect.int(myNat).equal(22); + expect.nat8(myNat8).equal(33); + expect.nat(myNat).equal(22); + expect.nat(myNat).less(66); + + expect.int(myNat).notEqual(221); + expect.nat8(myNat8).notEqual(331); + expect.nat(myNat).notEqual(221); + expect.nat8(myNat8).lessOrEqual(33); +}); + test("array has", func() { expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal).has(6); let exAr = expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal); exAr.has(6); exAr.has(1); exAr.has(0); + exAr.notHas(21); }); test("array equal", func() { @@ -43,6 +69,11 @@ test("array equal", func() { expect.array([1,2,3,4], Nat.toText, Nat.equal).notEqual([1,2,3]); }); +test("blob", func() { + expect.blob(Blob.fromArray([1,2,3,4])).equal(Blob.fromArray([1,2,3,4])); + // expect.blob(Blob.fromArray([1,2,3,4])).notEqual(Blob.fromArray([1,2,3,4])); +}); + test("expect custom", func() { type Custom = { x : Nat; @@ -59,7 +90,10 @@ test("expect custom", func() { }; public func greater(b : Custom) { - makeCompare(func(a, b) = a.x > b.x and a.y > b.y, show)(a, b); + let ok = a.x > b.x and a.y > b.y; + if (not ok) { + fail(show(a), ">=", show(b)); + }; }; }; From 1068291a1099a13539034bce7ec4e33711fcbb0c Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Sat, 23 Sep 2023 12:16:54 +0400 Subject: [PATCH 07/15] add expect.principal --- src/expect/expect-blob.mo | 2 +- src/expect/expect-principal.mo | 23 +++++++++++++++++++++++ src/expect/lib.mo | 2 ++ test/expect.test.mo | 10 +++++++++- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/expect/expect-principal.mo diff --git a/src/expect/expect-blob.mo b/src/expect/expect-blob.mo index 59e6c72..942d57d 100644 --- a/src/expect/expect-blob.mo +++ b/src/expect/expect-blob.mo @@ -6,7 +6,7 @@ import {bindCompare} "./utils"; module { public class ExpectBlob(val : Blob) { - func show(v : Blob) : Text = "" # debug_show(v) # ""; + func show(v : Blob) : Text = "blob \"" # debug_show(v) # "\""; public let equal = bindCompare(val, Blob.equal, show, ""); public let notEqual = bindCompare(val, Blob.notEqual, show, "!="); diff --git a/src/expect/expect-principal.mo b/src/expect/expect-principal.mo new file mode 100644 index 0000000..b1abaa2 --- /dev/null +++ b/src/expect/expect-principal.mo @@ -0,0 +1,23 @@ +import Principal "mo:base/Principal"; +import Array "mo:base/Array"; +import Option "mo:base/Option"; +import Debug "mo:base/Debug"; +import {bindCompare; fail} "./utils"; + +module { + public class ExpectPrincipal(val : Principal) { + func show(v : Principal) : Text = "principal \"" # Principal.toText(v) # "\""; + + public let equal = bindCompare(val, Principal.equal, show, ""); + public let notEqual = bindCompare(val, Principal.notEqual, show, "!="); + public let less = bindCompare(val, Principal.less, show, "<"); + public let lessOrEqual = bindCompare(val, Principal.lessOrEqual, show, "<="); + public let greater = bindCompare(val, Principal.greater, show, ">"); + public let greaterOrEqual = bindCompare(val, Principal.greaterOrEqual, show, ">="); + public func isAnonymous() { + if (not Principal.isAnonymous(val)) { + fail(show(val), "", show(Principal.fromBlob("\04"))); + }; + }; + }; +}; \ No newline at end of file diff --git a/src/expect/lib.mo b/src/expect/lib.mo index 05164c1..1b11156 100644 --- a/src/expect/lib.mo +++ b/src/expect/lib.mo @@ -27,6 +27,7 @@ import ExpectBool "./expect-bool"; import ExpectArray "./expect-array"; import ExpectBlob "./expect-blob"; import ExpectOption "./expect-option"; +import ExpectPrincipal "./expect-principal"; module { public let expect = { @@ -46,6 +47,7 @@ module { text = ExpectText.ExpectText; array = ExpectArray.ExpectArray; blob = ExpectBlob.ExpectBlob; + principal = ExpectPrincipal.ExpectPrincipal; }; public let fail = _fail; diff --git a/test/expect.test.mo b/test/expect.test.mo index e28ea78..66a6219 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -1,6 +1,7 @@ import Debug "mo:base/Debug"; import Nat "mo:base/Nat"; import Blob "mo:base/Blob"; +import Principal "mo:base/Principal"; import {test; suite; expect; fail} "../src"; test("bool", func() { @@ -37,7 +38,7 @@ test("nat", func() { expect.nat(myNat).less(66); }); -test("nat", func() { +test("intX, natX", func() { let myNat : Nat = 22; let myNat8 : Nat8 = 33; let myInt : Int = -44; @@ -48,6 +49,7 @@ test("nat", func() { expect.nat(myNat).less(66); expect.int(myNat).notEqual(221); + expect.int64(123123123123).notEqual(1231231231232); expect.nat8(myNat8).notEqual(331); expect.nat(myNat).notEqual(221); expect.nat8(myNat8).lessOrEqual(33); @@ -74,6 +76,12 @@ test("blob", func() { // expect.blob(Blob.fromArray([1,2,3,4])).notEqual(Blob.fromArray([1,2,3,4])); }); +test("principal", func() { + expect.principal(Principal.fromBlob(Blob.fromArray([1,2,3,4]))).equal(Principal.fromBlob(Blob.fromArray([1,2,3,4]))); + expect.principal(Principal.fromBlob("\04")).isAnonymous(); + expect.principal(Principal.fromBlob(Blob.fromArray([4]))).isAnonymous(); +}); + test("expect custom", func() { type Custom = { x : Nat; From ecfaac4e0c35ebab4ee0d7965359f955abe0a059 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Mon, 25 Sep 2023 10:11:38 +0400 Subject: [PATCH 08/15] add expect.array size --- src/expect/expect-array.mo | 22 ++++++++++++++++------ src/expect/lib.mo | 6 ++++-- test/expect.test.mo | 4 ++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/expect/expect-array.mo b/src/expect/expect-array.mo index c8b5501..bdf7b75 100644 --- a/src/expect/expect-array.mo +++ b/src/expect/expect-array.mo @@ -5,14 +5,14 @@ import {fail} "./utils"; module { public class ExpectArray(arr : [T], itemToText : (T) -> Text, itemEqual : (T, T) -> Bool) { - func _arrayToText(arr : [T]) : Text { + func _arrayToText(arr : [T], limit : Nat) : Text { var text = "["; label l do { for (i in arr.keys()) { text #= itemToText(arr[i]); if (i + 1 < arr.size()) { - if (text.size() > 100) { + if (text.size() > limit) { text #= "..."; break l; }; @@ -26,27 +26,37 @@ module { public func equal(other : [T]) { if (not Array.equal(arr, other, itemEqual)) { - fail(_arrayToText(arr), "", _arrayToText(other)); + fail(_arrayToText(arr, 100), "", _arrayToText(other, 100)); }; }; public func notEqual(other : [T]) { if (Array.equal(arr, other, itemEqual)) { - fail(_arrayToText(arr), "", _arrayToText(other)); + fail(_arrayToText(arr, 100), "", _arrayToText(other, 100)); }; }; public func has(a : T) { let has = Array.find(arr, func b = itemEqual(a, b)); if (Option.isNull(has)) { - fail(_arrayToText(arr), "to have item", itemToText(a)); + fail(_arrayToText(arr, 100), "to have item", itemToText(a)); }; }; public func notHas(a : T) { let has = Array.find(arr, func b = itemEqual(a, b)); if (Option.isSome(has)) { - fail(_arrayToText(arr), "to not have item", itemToText(a)); + fail(_arrayToText(arr, 100), "to not have item", itemToText(a)); + }; + }; + + public func size(n : Nat) { + if (arr.size() != n) { + fail( + "array size " # debug_show(arr.size()) # " - " # _arrayToText(arr, 50), + "array size", + debug_show(n) + ); }; }; }; diff --git a/src/expect/lib.mo b/src/expect/lib.mo index 1b11156..da59d2c 100644 --- a/src/expect/lib.mo +++ b/src/expect/lib.mo @@ -26,13 +26,13 @@ import ExpectText "./expect-text"; import ExpectBool "./expect-bool"; import ExpectArray "./expect-array"; import ExpectBlob "./expect-blob"; -import ExpectOption "./expect-option"; import ExpectPrincipal "./expect-principal"; +import ExpectOption "./expect-option"; +import ExpectRes "./expect-res"; module { public let expect = { bool = ExpectBool.ExpectBool; - option = ExpectOption.ExpectOption; int = ExpectInt.ExpectInt; int8 = func(val : Int8) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int8.toInt(val)); int16 = func(val : Int16) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int16.toInt(val)); @@ -48,6 +48,8 @@ module { array = ExpectArray.ExpectArray; blob = ExpectBlob.ExpectBlob; principal = ExpectPrincipal.ExpectPrincipal; + option = ExpectOption.ExpectOption; + res = ExpectRes.ExpectRes; }; public let fail = _fail; diff --git a/test/expect.test.mo b/test/expect.test.mo index 66a6219..63b97ef 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -64,6 +64,10 @@ test("array has", func() { exAr.notHas(21); }); +test("array size", func() { + expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal).size(11); +}); + test("array equal", func() { expect.array([1,2,3,4], Nat.toText, Nat.equal).equal([1,2,3,4]); expect.array([1,2,3,4], Nat.toText, Nat.equal).notEqual([1,2,2,4]); From 631aec0fcd8e297271d32e1dacdba5e62bd5adcb Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Mon, 25 Sep 2023 10:18:28 +0400 Subject: [PATCH 09/15] add expect.result --- src/expect/expect-result.mo | 24 ++++++++++++++++++++++++ src/expect/lib.mo | 4 ++-- test/expect.test.mo | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/expect/expect-result.mo diff --git a/src/expect/expect-result.mo b/src/expect/expect-result.mo new file mode 100644 index 0000000..c77c737 --- /dev/null +++ b/src/expect/expect-result.mo @@ -0,0 +1,24 @@ +import Result "mo:base/Result"; +import {fail} "./utils"; + +module { + public class ExpectResult(val : Result.Result, toText : (Result.Result) -> Text, equalFn : (Result.Result, Result.Result) -> Bool) { + public func isOk() { + if (Result.isErr(val)) { + fail(toText(val), "", "#ok(...)"); + }; + }; + + public func isErr() { + if (Result.isOk(val)) { + fail(toText(val), "", "#err(...)"); + }; + }; + + public func equal(other : Result.Result) { + if (not equalFn(val, other)) { + fail(toText(val), "", toText(other)); + }; + }; + }; +}; \ No newline at end of file diff --git a/src/expect/lib.mo b/src/expect/lib.mo index da59d2c..544c052 100644 --- a/src/expect/lib.mo +++ b/src/expect/lib.mo @@ -28,7 +28,7 @@ import ExpectArray "./expect-array"; import ExpectBlob "./expect-blob"; import ExpectPrincipal "./expect-principal"; import ExpectOption "./expect-option"; -import ExpectRes "./expect-res"; +import ExpectResult "./expect-result"; module { public let expect = { @@ -49,7 +49,7 @@ module { blob = ExpectBlob.ExpectBlob; principal = ExpectPrincipal.ExpectPrincipal; option = ExpectOption.ExpectOption; - res = ExpectRes.ExpectRes; + res = ExpectResult.ExpectResult; }; public let fail = _fail; diff --git a/test/expect.test.mo b/test/expect.test.mo index 63b97ef..b34ef9b 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -2,6 +2,7 @@ import Debug "mo:base/Debug"; import Nat "mo:base/Nat"; import Blob "mo:base/Blob"; import Principal "mo:base/Principal"; +import Result "mo:base/Result"; import {test; suite; expect; fail} "../src"; test("bool", func() { @@ -65,7 +66,7 @@ test("array has", func() { }); test("array size", func() { - expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal).size(11); + expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal).size(10); }); test("array equal", func() { @@ -86,6 +87,36 @@ test("principal", func() { expect.principal(Principal.fromBlob(Blob.fromArray([4]))).isAnonymous(); }); +test("result", func() { + type MyRes = Result.Result; + let ok : MyRes = #ok(22); + let err : MyRes = #err("error"); + + let expectOk = expect.res(ok, func(a) = debug_show(a), func(a, b) = a == b); + let expectErr = expect.res(err, func(a) = debug_show(a), func(a, b) = a == b); + + expectOk.isOk(); + expectOk.equal(#ok(22)); + + expectErr.isErr(); + expectErr.equal(#err("error")); +}); + +test("result opt ok", func() { + type MyRes = Result.Result; + let ok : MyRes = #ok(?22); + let err : MyRes = #err("error"); + + let expectOk = expect.res(ok, func(a) = debug_show(a), func(a, b) = a == b); + let expectErr = expect.res(err, func(a) = debug_show(a), func(a, b) = a == b); + + expectOk.isOk(); + expectOk.equal(#ok(?22)); + + expectErr.isErr(); + expectErr.equal(#err("error")); +}); + test("expect custom", func() { type Custom = { x : Nat; From 42415f3255a97193d081566752f62043211e4010 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Fri, 29 Sep 2023 10:19:08 +0400 Subject: [PATCH 10/15] add expect.call --- src/expect/expect-call.mo | 34 ++++++++++++++++++++++++++++++++++ src/expect/lib.mo | 4 +++- test/expect.test.mo | 16 +++++++++++----- 3 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/expect/expect-call.mo diff --git a/src/expect/expect-call.mo b/src/expect/expect-call.mo new file mode 100644 index 0000000..06de36d --- /dev/null +++ b/src/expect/expect-call.mo @@ -0,0 +1,34 @@ +import Debug "mo:base/Debug"; +import Error "mo:base/Error"; +import {bindCompare} "./utils"; + +module { + public class ExpectCall(fn : () -> async ()) { + func show(t : Text) : Text = "\"" # t # "\""; + + public func reject() : async () { + try { + await fn(); + } + catch (err) { + if (Error.code(err) == #canister_reject) { + return; + }; + }; + Debug.trap("expected to throw error"); + }; + + // unable to catch + // public func trap() : async () { + // try { + // await fn(); + // } + // catch (err) { + // if (Error.code(err) == #canister_error) { + // return; + // }; + // }; + // Debug.trap("expected to trap"); + // }; + }; +}; \ No newline at end of file diff --git a/src/expect/lib.mo b/src/expect/lib.mo index 544c052..8d235aa 100644 --- a/src/expect/lib.mo +++ b/src/expect/lib.mo @@ -29,6 +29,7 @@ import ExpectBlob "./expect-blob"; import ExpectPrincipal "./expect-principal"; import ExpectOption "./expect-option"; import ExpectResult "./expect-result"; +import ExpectCall "./expect-call"; module { public let expect = { @@ -49,7 +50,8 @@ module { blob = ExpectBlob.ExpectBlob; principal = ExpectPrincipal.ExpectPrincipal; option = ExpectOption.ExpectOption; - res = ExpectResult.ExpectResult; + result = ExpectResult.ExpectResult; + call = ExpectCall.ExpectCall; }; public let fail = _fail; diff --git a/test/expect.test.mo b/test/expect.test.mo index b34ef9b..784c808 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -3,6 +3,7 @@ import Nat "mo:base/Nat"; import Blob "mo:base/Blob"; import Principal "mo:base/Principal"; import Result "mo:base/Result"; +import Error "mo:base/Error"; import {test; suite; expect; fail} "../src"; test("bool", func() { @@ -92,8 +93,8 @@ test("result", func() { let ok : MyRes = #ok(22); let err : MyRes = #err("error"); - let expectOk = expect.res(ok, func(a) = debug_show(a), func(a, b) = a == b); - let expectErr = expect.res(err, func(a) = debug_show(a), func(a, b) = a == b); + let expectOk = expect.result(ok, func(a) = debug_show(a), func(a, b) = a == b); + let expectErr = expect.result(err, func(a) = debug_show(a), func(a, b) = a == b); expectOk.isOk(); expectOk.equal(#ok(22)); @@ -107,8 +108,8 @@ test("result opt ok", func() { let ok : MyRes = #ok(?22); let err : MyRes = #err("error"); - let expectOk = expect.res(ok, func(a) = debug_show(a), func(a, b) = a == b); - let expectErr = expect.res(err, func(a) = debug_show(a), func(a, b) = a == b); + let expectOk = expect.result(ok, func(a) = debug_show(a), func(a, b) = a == b); + let expectErr = expect.result(err, func(a) = debug_show(a), func(a, b) = a == b); expectOk.isOk(); expectOk.equal(#ok(?22)); @@ -142,4 +143,9 @@ test("expect custom", func() { expectCustom({x = 1; y = 3}).equal({x = 1; y = 3}); expectCustom({x = 2; y = 4}).greater({x = 1; y = 3}); -}); \ No newline at end of file +}); + +// test throw error +await expect.call(func() : async() { + throw Error.reject("error"); +}).reject(); \ No newline at end of file From a5de9c10419278a5d7b063def6d678ff5a5bbb32 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Fri, 29 Sep 2023 10:27:04 +0400 Subject: [PATCH 11/15] fix expect IntX, NatX --- src/expect/expect-int16.mo | 13 +++++++++++++ src/expect/expect-int32.mo | 13 +++++++++++++ src/expect/expect-int64.mo | 13 +++++++++++++ src/expect/expect-int8.mo | 13 +++++++++++++ src/expect/expect-nat.mo | 13 +++++++++++++ src/expect/expect-nat16.mo | 13 +++++++++++++ src/expect/expect-nat32.mo | 13 +++++++++++++ src/expect/expect-nat64.mo | 13 +++++++++++++ src/expect/expect-nat8.mo | 13 +++++++++++++ src/expect/lib.mo | 27 ++++++++++++++++++--------- test/expect.test.mo | 4 +++- 11 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 src/expect/expect-int16.mo create mode 100644 src/expect/expect-int32.mo create mode 100644 src/expect/expect-int64.mo create mode 100644 src/expect/expect-int8.mo create mode 100644 src/expect/expect-nat.mo create mode 100644 src/expect/expect-nat16.mo create mode 100644 src/expect/expect-nat32.mo create mode 100644 src/expect/expect-nat64.mo create mode 100644 src/expect/expect-nat8.mo diff --git a/src/expect/expect-int16.mo b/src/expect/expect-int16.mo new file mode 100644 index 0000000..07fe8e8 --- /dev/null +++ b/src/expect/expect-int16.mo @@ -0,0 +1,13 @@ +import Int16 "mo:base/Int16"; +import {bindCompare} "./utils"; + +module { + public class ExpectInt16(val : Int16) { + public let equal = bindCompare(val, Int16.equal, Int16.toText, ""); + public let notEqual = bindCompare(val, Int16.notEqual, Int16.toText, "!="); + public let less = bindCompare(val, Int16.less, Int16.toText, "<"); + public let lessOrEqual = bindCompare(val, Int16.lessOrEqual, Int16.toText, "<="); + public let greater = bindCompare(val, Int16.greater, Int16.toText, ">"); + public let greaterOrEqual = bindCompare(val, Int16.greaterOrEqual, Int16.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-int32.mo b/src/expect/expect-int32.mo new file mode 100644 index 0000000..689f94e --- /dev/null +++ b/src/expect/expect-int32.mo @@ -0,0 +1,13 @@ +import Int32 "mo:base/Int32"; +import {bindCompare} "./utils"; + +module { + public class ExpectInt32(val : Int32) { + public let equal = bindCompare(val, Int32.equal, Int32.toText, ""); + public let notEqual = bindCompare(val, Int32.notEqual, Int32.toText, "!="); + public let less = bindCompare(val, Int32.less, Int32.toText, "<"); + public let lessOrEqual = bindCompare(val, Int32.lessOrEqual, Int32.toText, "<="); + public let greater = bindCompare(val, Int32.greater, Int32.toText, ">"); + public let greaterOrEqual = bindCompare(val, Int32.greaterOrEqual, Int32.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-int64.mo b/src/expect/expect-int64.mo new file mode 100644 index 0000000..a6ed901 --- /dev/null +++ b/src/expect/expect-int64.mo @@ -0,0 +1,13 @@ +import Int64 "mo:base/Int64"; +import {bindCompare} "./utils"; + +module { + public class ExpectInt64(val : Int64) { + public let equal = bindCompare(val, Int64.equal, Int64.toText, ""); + public let notEqual = bindCompare(val, Int64.notEqual, Int64.toText, "!="); + public let less = bindCompare(val, Int64.less, Int64.toText, "<"); + public let lessOrEqual = bindCompare(val, Int64.lessOrEqual, Int64.toText, "<="); + public let greater = bindCompare(val, Int64.greater, Int64.toText, ">"); + public let greaterOrEqual = bindCompare(val, Int64.greaterOrEqual, Int64.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-int8.mo b/src/expect/expect-int8.mo new file mode 100644 index 0000000..3005136 --- /dev/null +++ b/src/expect/expect-int8.mo @@ -0,0 +1,13 @@ +import Int8 "mo:base/Int8"; +import {bindCompare} "./utils"; + +module { + public class ExpectInt8(val : Int8) { + public let equal = bindCompare(val, Int8.equal, Int8.toText, ""); + public let notEqual = bindCompare(val, Int8.notEqual, Int8.toText, "!="); + public let less = bindCompare(val, Int8.less, Int8.toText, "<"); + public let lessOrEqual = bindCompare(val, Int8.lessOrEqual, Int8.toText, "<="); + public let greater = bindCompare(val, Int8.greater, Int8.toText, ">"); + public let greaterOrEqual = bindCompare(val, Int8.greaterOrEqual, Int8.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-nat.mo b/src/expect/expect-nat.mo new file mode 100644 index 0000000..0dec231 --- /dev/null +++ b/src/expect/expect-nat.mo @@ -0,0 +1,13 @@ +import Nat "mo:base/Nat"; +import {bindCompare} "./utils"; + +module { + public class ExpectNat(val : Nat) { + public let equal = bindCompare(val, Nat.equal, Nat.toText, ""); + public let notEqual = bindCompare(val, Nat.notEqual, Nat.toText, "!="); + public let less = bindCompare(val, Nat.less, Nat.toText, "<"); + public let lessOrEqual = bindCompare(val, Nat.lessOrEqual, Nat.toText, "<="); + public let greater = bindCompare(val, Nat.greater, Nat.toText, ">"); + public let greaterOrEqual = bindCompare(val, Nat.greaterOrEqual, Nat.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-nat16.mo b/src/expect/expect-nat16.mo new file mode 100644 index 0000000..c9dee32 --- /dev/null +++ b/src/expect/expect-nat16.mo @@ -0,0 +1,13 @@ +import Nat16 "mo:base/Nat16"; +import {bindCompare} "./utils"; + +module { + public class ExpectNat16(val : Nat16) { + public let equal = bindCompare(val, Nat16.equal, Nat16.toText, ""); + public let notEqual = bindCompare(val, Nat16.notEqual, Nat16.toText, "!="); + public let less = bindCompare(val, Nat16.less, Nat16.toText, "<"); + public let lessOrEqual = bindCompare(val, Nat16.lessOrEqual, Nat16.toText, "<="); + public let greater = bindCompare(val, Nat16.greater, Nat16.toText, ">"); + public let greaterOrEqual = bindCompare(val, Nat16.greaterOrEqual, Nat16.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-nat32.mo b/src/expect/expect-nat32.mo new file mode 100644 index 0000000..0749b6f --- /dev/null +++ b/src/expect/expect-nat32.mo @@ -0,0 +1,13 @@ +import Nat32 "mo:base/Nat32"; +import {bindCompare} "./utils"; + +module { + public class ExpectNat32(val : Nat32) { + public let equal = bindCompare(val, Nat32.equal, Nat32.toText, ""); + public let notEqual = bindCompare(val, Nat32.notEqual, Nat32.toText, "!="); + public let less = bindCompare(val, Nat32.less, Nat32.toText, "<"); + public let lessOrEqual = bindCompare(val, Nat32.lessOrEqual, Nat32.toText, "<="); + public let greater = bindCompare(val, Nat32.greater, Nat32.toText, ">"); + public let greaterOrEqual = bindCompare(val, Nat32.greaterOrEqual, Nat32.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-nat64.mo b/src/expect/expect-nat64.mo new file mode 100644 index 0000000..c86a7e5 --- /dev/null +++ b/src/expect/expect-nat64.mo @@ -0,0 +1,13 @@ +import Nat64 "mo:base/Nat64"; +import {bindCompare} "./utils"; + +module { + public class ExpectNat64(val : Nat64) { + public let equal = bindCompare(val, Nat64.equal, Nat64.toText, ""); + public let notEqual = bindCompare(val, Nat64.notEqual, Nat64.toText, "!="); + public let less = bindCompare(val, Nat64.less, Nat64.toText, "<"); + public let lessOrEqual = bindCompare(val, Nat64.lessOrEqual, Nat64.toText, "<="); + public let greater = bindCompare(val, Nat64.greater, Nat64.toText, ">"); + public let greaterOrEqual = bindCompare(val, Nat64.greaterOrEqual, Nat64.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/expect-nat8.mo b/src/expect/expect-nat8.mo new file mode 100644 index 0000000..0286142 --- /dev/null +++ b/src/expect/expect-nat8.mo @@ -0,0 +1,13 @@ +import Nat8 "mo:base/Nat8"; +import {bindCompare} "./utils"; + +module { + public class ExpectNat8(val : Nat8) { + public let equal = bindCompare(val, Nat8.equal, Nat8.toText, ""); + public let notEqual = bindCompare(val, Nat8.notEqual, Nat8.toText, "!="); + public let less = bindCompare(val, Nat8.less, Nat8.toText, "<"); + public let lessOrEqual = bindCompare(val, Nat8.lessOrEqual, Nat8.toText, "<="); + public let greater = bindCompare(val, Nat8.greater, Nat8.toText, ">"); + public let greaterOrEqual = bindCompare(val, Nat8.greaterOrEqual, Nat8.toText, ">="); + }; +}; \ No newline at end of file diff --git a/src/expect/lib.mo b/src/expect/lib.mo index 8d235aa..73771fe 100644 --- a/src/expect/lib.mo +++ b/src/expect/lib.mo @@ -21,6 +21,15 @@ import Iter "mo:base/Iter"; import {bindCompare; fail = _fail} "./utils"; import ExpectInt "./expect-int"; +import ExpectInt8 "./expect-int8"; +import ExpectInt16 "./expect-int16"; +import ExpectInt32 "./expect-int32"; +import ExpectInt64 "./expect-int64"; +import ExpectNat "./expect-nat"; +import ExpectNat8 "./expect-nat8"; +import ExpectNat16 "./expect-nat16"; +import ExpectNat32 "./expect-nat32"; +import ExpectNat64 "./expect-nat64"; import ExpectChar "./expect-char"; import ExpectText "./expect-text"; import ExpectBool "./expect-bool"; @@ -35,15 +44,15 @@ module { public let expect = { bool = ExpectBool.ExpectBool; int = ExpectInt.ExpectInt; - int8 = func(val : Int8) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int8.toInt(val)); - int16 = func(val : Int16) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int16.toInt(val)); - int32 = func(val : Int32) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int32.toInt(val)); - int64 = func(val : Int64) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Int64.toInt(val)); - nat = func(val : Nat) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(val); - nat8 = func(val : Nat8) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat8.toNat(val)); - nat16 = func(val : Nat16) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat16.toNat(val)); - nat32 = func(val : Nat32) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat32.toNat(val)); - nat64 = func(val : Nat64) : ExpectInt.ExpectInt = ExpectInt.ExpectInt(Nat64.toNat(val)); + int8 = func(val : Int8) : ExpectInt8.ExpectInt8 = ExpectInt8.ExpectInt8(val); + int16 = func(val : Int16) : ExpectInt16.ExpectInt16 = ExpectInt16.ExpectInt16(val); + int32 = func(val : Int32) : ExpectInt32.ExpectInt32 = ExpectInt32.ExpectInt32(val); + int64 = func(val : Int64) : ExpectInt64.ExpectInt64 = ExpectInt64.ExpectInt64(val); + nat = func(val : Nat) : ExpectNat.ExpectNat = ExpectNat.ExpectNat(val); + nat8 = func(val : Nat8) : ExpectNat8.ExpectNat8 = ExpectNat8.ExpectNat8(val); + nat16 = func(val : Nat16) : ExpectNat16.ExpectNat16 = ExpectNat16.ExpectNat16(val); + nat32 = func(val : Nat32) : ExpectNat32.ExpectNat32 = ExpectNat32.ExpectNat32(val); + nat64 = func(val : Nat64) : ExpectNat64.ExpectNat64 = ExpectNat64.ExpectNat64(val); char = ExpectChar.ExpectChar; text = ExpectText.ExpectText; array = ExpectArray.ExpectArray; diff --git a/test/expect.test.mo b/test/expect.test.mo index 784c808..7f12334 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -44,6 +44,7 @@ test("intX, natX", func() { let myNat : Nat = 22; let myNat8 : Nat8 = 33; let myInt : Int = -44; + let myInt8 : Int8 = -44; let myFloat : Float = 1.313; expect.int(myNat).equal(22); expect.nat8(myNat8).equal(33); @@ -51,8 +52,9 @@ test("intX, natX", func() { expect.nat(myNat).less(66); expect.int(myNat).notEqual(221); + expect.int8(myInt8).equal(myInt8); expect.int64(123123123123).notEqual(1231231231232); - expect.nat8(myNat8).notEqual(331); + expect.nat8(myNat8).notEqual(32); expect.nat(myNat).notEqual(221); expect.nat8(myNat8).lessOrEqual(33); }); From ebe9747744304b7b277fbddb1ef7cbd1d12075eb Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Thu, 5 Oct 2023 10:54:05 +0400 Subject: [PATCH 12/15] expect docs progress --- README.md | 152 +++++++++++++++++++++++++++++++++ src/expect/expect-principal.mo | 5 ++ src/expect/expect-result.mo | 6 ++ src/expect/utils.mo | 38 +-------- test/expect.test.mo | 32 +++++-- 5 files changed, 191 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 707dca9..ded9f73 100644 --- a/README.md +++ b/README.md @@ -80,3 +80,155 @@ await suite("my async test suite", func() : async () { }); }); ``` + +# Expect + +```motoko +import {test; expect} "mo:test"; +``` + +Expect consists of a number of "matchers" that let you validate different things. + +Compared to `assert`, in case of fail `expect` shows you the details of the values. + +For example `assert`: +```motoko +assert myNat == 1; +// execution error, assertion failure +``` +We only know that `myNat` is not equal to `1`, but what is the actual value of `myNat`? +To know this, we have to add a new line `Debug.print(debug_show(myNat))`. + +but with `expect`: +```motoko +expect.nat(myNat).equal(1); +// execution error, explicit trap: +// ! received 22 +// ! expected 1 +``` +here we see the actual value of `myNat` + + +## `expect.nat` (nat8, nat16, nat32, nat64, int, int8, int16, int32, int64) + +```motoko +import {test; expect} "mo:test"; + +expect.nat(x).equal(10); // x == 10 +expect.nat(x).notEqual(10); // x != 10 +expect.nat(x).less(10); // x < 10 +expect.nat(x).lessOrEqual(10); // x <= 10 +expect.nat(x).greater(10); // x > 10 +expect.nat(x).greaterOrEqual(10); // x >= 10 + +expect.int(x).equal(10); // x == 10 (Int) +expect.int64(x).equal(10); // x == 10 (Int64) +expect.nat32(x).equal(10); // x == 10 (Nat32) +``` + +## `expect.char` + +```motoko +expect.char(c).equal('a'); // c == 'a' +expect.char(c).notEqual('a'); // c != 'a' +expect.char(c).less('a'); // c < 'a' +expect.char(c).lessOrEqual('a'); // c <= 'a' +expect.char(c).greater('a'); // c > 'a' +expect.char(c).greaterOrEqual('a'); // c >= 'a' +``` + +## `expect.text` + +```motoko +expect.text(foo).equal("bar"); // foo == "bar" +expect.text(foo).notEqual("bar"); // foo != "bar" +expect.text(foo).contains("bar"); // Text.contains(foo, #text("bar")) +expect.text(foo).startsWith("bar"); // Text.startsWith(foo, #text("bar")) +expect.text(foo).endsWith("bar"); // Text.endsWith(foo, #text("bar"))" + +expect.text(foo).less("bar"); // foo < "bar" +expect.text(foo).lessOrEqual("bar"); // foo <= "bar" +expect.text(foo).greater("bar"); // foo > "bar" +expect.text(foo).greaterOrEqual("bar"); // foo >= "bar" +``` + +## `expect.option` + +```motoko +// optional Nat +let optNat = ?10; +expect.option(optNat, Nat.toText, Nat.equal).equal(?10); // optNat == ?10 +expect.option(optNat, Nat.toText, Nat.equal).notEqual(?25); // optNat != ?25 +expect.option(optNat, Nat.toText, Nat.equal).isNull(); // optNat == null + + +// optional custom type +type MyType = { + x : Nat; + y : Nat; +}; + +func showMyType(a : MyType) : Text { + debug_show(a); +}; + +func equalMyType(a : MyType, b : MyType) : Bool { + a.x == b.x and a.y == b.y +}; + +let val = ?{x = 1; y = 2}; + +expect.option(v, showMyType, equalMyType).notEqual(null); +expect.option(v, showMyType, equalMyType).isSome(null); +expect.option(v, showMyType, equalMyType).equal(?{x = 1; y = 2}); +``` + +## `expect.result` + +```motoko +type MyRes = Result.Result; + +func show(a) = debug_show(a); +func equal(a, b) = a == b + +let ok : MyRes = #ok(22); +let err : MyRes = #err("error"); + +expect.result(ok, show, equal).isOk(); +expect.result(ok, show, equal).equal(#ok(22)); + +expect.result(err, show, equal).isErr(); +expect.result(err, show, equal).equal(#err("error")); +expect.result(err, show, equal).equal(#err("other error")); +``` + +## `expect.principal` + +```motoko +expect.principal(id).isAnonymous(); // Principal.isAnonymous(id) +expect.principal(id).notAnonymous(); // not Principal.isAnonymous(id) + +expect.principal(id).equal(id2); // id == id2 +expect.principal(id).notEqual(id2); // id != id2 + +expect.principal(id).less(id2); // id < id2 +expect.principal(id).lessOrEqual(id2); // id <= id2 +expect.principal(id).greater(id2); // id > id2 +expect.principal(id).greaterOrEqual(id2); // id >= id2 +``` + +## `expect.bool` +```motoko +``` + +## `expect.array` +```motoko +``` + +## `expect.blob` +```motoko +``` + +## `expect.call` +```motoko +``` \ No newline at end of file diff --git a/src/expect/expect-principal.mo b/src/expect/expect-principal.mo index b1abaa2..721acf3 100644 --- a/src/expect/expect-principal.mo +++ b/src/expect/expect-principal.mo @@ -19,5 +19,10 @@ module { fail(show(val), "", show(Principal.fromBlob("\04"))); }; }; + public func notAnonymous() { + if (Principal.isAnonymous(val)) { + fail(show(val), "!=", show(Principal.fromBlob("\04"))); + }; + }; }; }; \ No newline at end of file diff --git a/src/expect/expect-result.mo b/src/expect/expect-result.mo index c77c737..5478167 100644 --- a/src/expect/expect-result.mo +++ b/src/expect/expect-result.mo @@ -20,5 +20,11 @@ module { fail(toText(val), "", toText(other)); }; }; + + public func notEqual(other : Result.Result) { + if (equalFn(val, other)) { + fail(toText(val), "!=", toText(other)); + }; + }; }; }; \ No newline at end of file diff --git a/src/expect/utils.mo b/src/expect/utils.mo index cf6866e..7dadafa 100644 --- a/src/expect/utils.mo +++ b/src/expect/utils.mo @@ -1,39 +1,7 @@ import Debug "mo:base/Debug"; module { - public func fail1(actual : Text, condition : Text, reference : Text) { - // let prefix = "\1b[31m·\1b[0m"; - // let prefix = "\1b[31m⚠\1b[0m"; - // let prefix = "\1b[31m⌇\1b[0m"; - // let prefix = "\1b[31m!\1b[0m"; - let prefix = "\1b[31m•\1b[0m"; - var msg = "\n" # prefix # " expected \1b[31m" # actual # "\1b[0m "; - if (condition != "") { - msg #= "\n" # prefix # " \1b[30m" # condition # "\1b[0m"; - } - else { - msg #= prefix # ""; - }; - msg #= " " # reference # ""; - - Debug.trap(msg); - }; - - public func fail2(actual : Text, condition : Text, reference : Text) { - let prefix = "\1b[31m•\1b[0m"; - var msg = "\n" # prefix # " received \1b[31m" # actual # "\1b[0m "; - if (condition != "") { - msg #= "\n" # prefix # " expected" # " \1b[30m" # condition # "\1b[0m"; - } - else { - msg #= prefix # ""; - }; - msg #= " " # reference # ""; - - Debug.trap(msg); - }; - - public func fail3(actual : Text, condition : Text, reference : Text) { + public func fail(actual : Text, condition : Text, reference : Text) { let prefix = "\1b[31m!\1b[0m"; var msg = "\n" # prefix # " \1b[30mreceived\1b[0m \1b[31m" # actual # "\1b[0m "; if (condition != "") { @@ -47,12 +15,10 @@ module { Debug.trap(msg); }; - public let fail = fail3; - public func compare(a : T, b : T, comp : (T, T) -> Bool, toText : (T) -> Text, condition : Text) { let res = comp(a, b); if (not res) { - fail(toText(b), condition, toText(a)); + fail(toText(a), condition, toText(b)); }; }; diff --git a/test/expect.test.mo b/test/expect.test.mo index 7f12334..f6686e2 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -15,12 +15,31 @@ test("bool", func() { }); test("option", func() { - expect.option(null, Nat.toText, Nat.equal).isNull(); - expect.option(?1, Nat.toText, Nat.equal).isSome(); - expect.option(?2, Nat.toText, Nat.equal).equal(?2); - expect.option(?3, Nat.toText, Nat.equal).notEqual(?44); - expect.option(?3, Nat.toText, Nat.equal).notEqual(null); - expect.option(null, Nat.toText, Nat.equal).equal(null); + expect.option(null, Nat.toText, Nat.equal).isNull(); + expect.option(?1, Nat.toText, Nat.equal).isSome(); + expect.option(?2, Nat.toText, Nat.equal).equal(?2); + expect.option(?3, Nat.toText, Nat.equal).notEqual(?44); + expect.option(?3, Nat.toText, Nat.equal).notEqual(null); + expect.option(null, Nat.toText, Nat.equal).equal(null); +}); + +test("option custom type", func() { + type MyType = { + x : Nat; + y : Nat; + }; + let v = ?{x = 1; y = 2}; + + func showMyType(a : MyType) : Text { + debug_show(a); + }; + + func equalMyType(a : MyType, b : MyType) : Bool { + a.x == b.x and a.y == b.y + }; + + expect.option(v, showMyType, equalMyType).notEqual(null); + expect.option(v, showMyType, equalMyType).equal(?{x = 1; y = 2}); }); test("char", func() { @@ -103,6 +122,7 @@ test("result", func() { expectErr.isErr(); expectErr.equal(#err("error")); + expectErr.notEqual(#err("other error")); }); test("result opt ok", func() { From 04e6d55a37e754d925d1d3efef62b949e24fed69 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Tue, 10 Oct 2023 16:00:26 +0400 Subject: [PATCH 13/15] expect docs progress --- README.md | 34 +++++++++++++++++++++++++++++++++- src/expect/expect-array.mo | 8 ++++---- src/expect/expect-blob.mo | 26 ++++++++++++++++++-------- test/expect.test.mo | 23 ++++++++++++++--------- 4 files changed, 69 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ded9f73..1218ed6 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ func equalMyType(a : MyType, b : MyType) : Bool { let val = ?{x = 1; y = 2}; expect.option(v, showMyType, equalMyType).notEqual(null); -expect.option(v, showMyType, equalMyType).isSome(null); +expect.option(v, showMyType, equalMyType).isSome(); // != null expect.option(v, showMyType, equalMyType).equal(?{x = 1; y = 2}); ``` @@ -219,16 +219,48 @@ expect.principal(id).greaterOrEqual(id2); // id >= id2 ## `expect.bool` ```motoko +expect.bool(x).isTrue(); // a == true +expect.bool(x).isFalse(); // a == false +expect.bool(x).equal(b); // a == b +expect.bool(x).notEqual(b); // a != b ``` ## `expect.array` ```motoko +expect.array([1,2,3], Nat.toText, Nat.equal).equal([1,2,3]); +expect.array([1,2,3], Nat.toText, Nat.equal).notEqual([1,2]); + +expect.array([1,2,3], Nat.toText, Nat.equal).contains(3); // array contains element 3 +expect.array([1,2,3], Nat.toText, Nat.equal).notContains(10); // array does not contain element 10 + +expect.array([1,2,3,4], Nat.toText, Nat.equal).size(4); ``` ## `expect.blob` ```motoko +expect.blob(blob).size(4); // blob.size() == 4 +expect.blob(blob).equal(blob2); // blob == blob2 +expect.blob(blob).notEqual(blob2); // blob != blob2 + +expect.blob(blob).less(blob2); // blob < blob2 +expect.blob(blob).lessOrEqual(blob2); // blob <= blob2 +expect.blob(blob).greater(blob2); // blob > blob2 +expect.blob(blob).greaterOrEqual(blob2); // blob >= blob2 ``` ## `expect.call` + +_Does not catch traps._ + ```motoko +func myFunc() : async () { + throw Error.reject("error"); +}; + +func noop() : async () { + // do not throw an error +}; + +await expect.call(myFunc).reject(); // ok +await expect.call(noop).reject(); // fail ``` \ No newline at end of file diff --git a/src/expect/expect-array.mo b/src/expect/expect-array.mo index bdf7b75..efdf4ce 100644 --- a/src/expect/expect-array.mo +++ b/src/expect/expect-array.mo @@ -36,17 +36,17 @@ module { }; }; - public func has(a : T) { + public func contains(a : T) { let has = Array.find(arr, func b = itemEqual(a, b)); if (Option.isNull(has)) { - fail(_arrayToText(arr, 100), "to have item", itemToText(a)); + fail(_arrayToText(arr, 100), "to contain element", itemToText(a)); }; }; - public func notHas(a : T) { + public func notContains(a : T) { let has = Array.find(arr, func b = itemEqual(a, b)); if (Option.isSome(has)) { - fail(_arrayToText(arr, 100), "to not have item", itemToText(a)); + fail(_arrayToText(arr, 100), "to not contain element", itemToText(a)); }; }; diff --git a/src/expect/expect-blob.mo b/src/expect/expect-blob.mo index 942d57d..8a1eaeb 100644 --- a/src/expect/expect-blob.mo +++ b/src/expect/expect-blob.mo @@ -2,17 +2,27 @@ import Blob "mo:base/Blob"; import Array "mo:base/Array"; import Option "mo:base/Option"; import Debug "mo:base/Debug"; -import {bindCompare} "./utils"; +import {bindCompare; fail} "./utils"; module { - public class ExpectBlob(val : Blob) { + public class ExpectBlob(blob : Blob) { func show(v : Blob) : Text = "blob \"" # debug_show(v) # "\""; - public let equal = bindCompare(val, Blob.equal, show, ""); - public let notEqual = bindCompare(val, Blob.notEqual, show, "!="); - public let less = bindCompare(val, Blob.less, show, "<"); - public let lessOrEqual = bindCompare(val, Blob.lessOrEqual, show, "<="); - public let greater = bindCompare(val, Blob.greater, show, ">"); - public let greaterOrEqual = bindCompare(val, Blob.greaterOrEqual, show, ">="); + public let equal = bindCompare(blob, Blob.equal, show, ""); + public let notEqual = bindCompare(blob, Blob.notEqual, show, "!="); + public let less = bindCompare(blob, Blob.less, show, "<"); + public let lessOrEqual = bindCompare(blob, Blob.lessOrEqual, show, "<="); + public let greater = bindCompare(blob, Blob.greater, show, ">"); + public let greaterOrEqual = bindCompare(blob, Blob.greaterOrEqual, show, ">="); + + public func size(n : Nat) { + if (blob.size() != n) { + fail( + "blob size " # debug_show(blob.size()), + "blob size", + debug_show(n) + ); + }; + }; }; }; \ No newline at end of file diff --git a/test/expect.test.mo b/test/expect.test.mo index f6686e2..03a89bb 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -78,13 +78,15 @@ test("intX, natX", func() { expect.nat8(myNat8).lessOrEqual(33); }); -test("array has", func() { - expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal).has(6); +test("array contains", func() { + expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal).contains(6); let exAr = expect.array([1,2,3,4,5,6,7,8,9,0], Nat.toText, Nat.equal); - exAr.has(6); - exAr.has(1); - exAr.has(0); - exAr.notHas(21); + exAr.contains(6); + exAr.contains(1); + exAr.contains(0); + exAr.notContains(88); + exAr.notContains(21); + exAr.size(10); }); test("array size", func() { @@ -100,7 +102,8 @@ test("array equal", func() { test("blob", func() { expect.blob(Blob.fromArray([1,2,3,4])).equal(Blob.fromArray([1,2,3,4])); - // expect.blob(Blob.fromArray([1,2,3,4])).notEqual(Blob.fromArray([1,2,3,4])); + expect.blob(Blob.fromArray([1,2,3,4])).notEqual(Blob.fromArray([2,2,3,4])); + expect.blob(Blob.fromArray([1,2,3,4])).size(4); }); test("principal", func() { @@ -168,6 +171,8 @@ test("expect custom", func() { }); // test throw error -await expect.call(func() : async() { +func myFunc() : async () { throw Error.reject("error"); -}).reject(); \ No newline at end of file +}; + +await expect.call(myFunc).reject(); \ No newline at end of file From 58ba7ccf5c4738c8384f1b53c3b5b077c850fe53 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Tue, 10 Oct 2023 16:01:58 +0400 Subject: [PATCH 14/15] add unit test wokflow --- .github/workflows/mops-test.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/mops-test.yml diff --git a/.github/workflows/mops-test.yml b/.github/workflows/mops-test.yml new file mode 100644 index 0000000..92d6783 --- /dev/null +++ b/.github/workflows/mops-test.yml @@ -0,0 +1,30 @@ +name: mops test + +on: + push: + branches: + - main + - master + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: install moc + run: npx mocv use latest + + - name: install mops + run: npm i ic-mops -g + + - name: install mops packages + run: mops install + + - name: run tests + run: mops test \ No newline at end of file From c2216f69c7b3ad9a2d17320671c6e2a192ddf782 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Thu, 26 Oct 2023 16:09:41 +0400 Subject: [PATCH 15/15] refactor --- mops.toml | 2 +- src/expect/lib.mo | 18 +++++++++--------- test/expect.test.mo | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mops.toml b/mops.toml index 03636bc..0c93038 100644 --- a/mops.toml +++ b/mops.toml @@ -3,7 +3,7 @@ name = "test" version = "1.0.1" description = "Motoko testing library to run tests with mops" repository = "https://github.com/ZenVoich/test" -keywords = [ "test", "mops", "testing", "unit", "suite" ] +keywords = [ "test", "testing", "unit", "suite", "expect", "matchers", "mops" ] license = "MIT" [dependencies] diff --git a/src/expect/lib.mo b/src/expect/lib.mo index 73771fe..9471544 100644 --- a/src/expect/lib.mo +++ b/src/expect/lib.mo @@ -44,15 +44,15 @@ module { public let expect = { bool = ExpectBool.ExpectBool; int = ExpectInt.ExpectInt; - int8 = func(val : Int8) : ExpectInt8.ExpectInt8 = ExpectInt8.ExpectInt8(val); - int16 = func(val : Int16) : ExpectInt16.ExpectInt16 = ExpectInt16.ExpectInt16(val); - int32 = func(val : Int32) : ExpectInt32.ExpectInt32 = ExpectInt32.ExpectInt32(val); - int64 = func(val : Int64) : ExpectInt64.ExpectInt64 = ExpectInt64.ExpectInt64(val); - nat = func(val : Nat) : ExpectNat.ExpectNat = ExpectNat.ExpectNat(val); - nat8 = func(val : Nat8) : ExpectNat8.ExpectNat8 = ExpectNat8.ExpectNat8(val); - nat16 = func(val : Nat16) : ExpectNat16.ExpectNat16 = ExpectNat16.ExpectNat16(val); - nat32 = func(val : Nat32) : ExpectNat32.ExpectNat32 = ExpectNat32.ExpectNat32(val); - nat64 = func(val : Nat64) : ExpectNat64.ExpectNat64 = ExpectNat64.ExpectNat64(val); + int8 = ExpectInt8.ExpectInt8; + int16 = ExpectInt16.ExpectInt16; + int32 = ExpectInt32.ExpectInt32; + int64 = ExpectInt64.ExpectInt64; + nat = ExpectNat.ExpectNat; + nat8 = ExpectNat8.ExpectNat8; + nat16 = ExpectNat16.ExpectNat16; + nat32 = ExpectNat32.ExpectNat32; + nat64 = ExpectNat64.ExpectNat64; char = ExpectChar.ExpectChar; text = ExpectText.ExpectText; array = ExpectArray.ExpectArray; diff --git a/test/expect.test.mo b/test/expect.test.mo index 03a89bb..c3af535 100644 --- a/test/expect.test.mo +++ b/test/expect.test.mo @@ -108,6 +108,7 @@ test("blob", func() { test("principal", func() { expect.principal(Principal.fromBlob(Blob.fromArray([1,2,3,4]))).equal(Principal.fromBlob(Blob.fromArray([1,2,3,4]))); + expect.principal(Principal.fromBlob(Blob.fromArray([1,2,3,4]))).notEqual(Principal.fromBlob(Blob.fromArray([1,2,3,5]))); expect.principal(Principal.fromBlob("\04")).isAnonymous(); expect.principal(Principal.fromBlob(Blob.fromArray([4]))).isAnonymous(); });