motoko 0.0.32

A library for parsing and running Motoko in Rust
Documentation
1
{"name":"matchers","version":"master","files":{"Matchers.mo":{"content":"/// Composable assertions\n///\n/// This module contains functions for building and combining `Matcher`s that\n/// can be used to build up assertions for testing.\n/// ```motoko\n/// import M \"mo:matchers/Matchers\";\n/// import T \"mo:matchers/Testable\";\n///\n/// assertThat(5 + 5, M.equals(T.nat(10)));\n/// assertThat(5 + 5, M.allOf<Nat>([M.greaterThan(8), M.lessThan(12)]));\n/// assertThat([1, 2], M.array([M.equals(T.nat(1)), M.equals(T.nat(2))]));\n/// ```\nimport Debug \"mo:base/Debug\";\nimport Int \"mo:base/Int\";\nimport Nat \"mo:base/Nat\";\nimport Option \"mo:base/Option\";\nimport T \"Testable\";\n\nmodule {\n    /// A `Matcher` is a composable way of building up assertions for tests\n    public type Matcher<A> = {\n        matches : (item: A) -> Bool;\n        describeMismatch : (item : A, description : Description) -> ();\n    };\n\n    /// Matches an `item` against a matcher and traps with an error if the\n    /// matcher fails. This is primarily for experimentation and one-offs. For\n    /// writing an actual test suite you probably want to use the functions in\n    /// [Suite](Suite.html).\n    public func assertThat<A>(item : A, matcher : Matcher<A>) {\n        if (not matcher.matches(item)) {\n            let description = Description();\n            matcher.describeMismatch(item, description);\n            Debug.print(description.toText());\n            assert(false)\n        }\n    };\n\n    /// Matches an `item` against a matcher and returns `null` in case the\n    /// match succeeds and `?errorMessage` if it fails.\n    public func attempt<A>(item : A, matcher : Matcher<A>) : { #success; #fail : Text } {\n        if (matcher.matches(item)) {\n            #success\n        } else {\n            let description = Description();\n            matcher.describeMismatch(item, description);\n            #fail(description.toText())\n        }\n    };\n\n    /// Turns a `Matcher` for `A`s into a `Matcher` for `B`s by using `f` as an adapter.\n    // TODO Maybe call this adapt?\n    public func contramap<A, B>(matcher : Matcher<A>, f : B -> A) : Matcher<B> = {\n        matches = func (item : B) : Bool = matcher.matches(f(item));\n        describeMismatch = func (item : B, description : Description) =\n            matcher.describeMismatch(f(item), description);\n    };\n\n    /// `Matcher`s describe match failures by inserting them into a `Description`.\n    // TODO More complicated descriptions? Maybe a bit more structure than raw text?\n    public class Description() {\n        var message : Text = \"\";\n\n        public func appendText(text : Text) {\n            message := message # text;\n        };\n\n        public func toText() : Text = message;\n    };\n\n    /// Always matches, useful if you don’t care what the object under test is\n    public func anything<A>() : Matcher<A> = {\n        matches = func (item : A) : Bool = true;\n        describeMismatch = func (item : A, description : Description) = ();\n    };\n\n    /// Decorator that allows adding a custom failure description\n    public func describedAs<A>(msg : Text, matcher : Matcher<A>) : Matcher<A> = {\n        matches = func (item : A) : Bool = matcher.matches(item);\n        describeMismatch = func (item : A, description : Description) =\n            description.appendText(msg);\n    };\n\n    /// Matches values equal to an expected `Testable`\n    public func equals<A>(expected : T.TestableItem<A>) : Matcher<A> = {\n        matches = func (item : A) : Bool = expected.equals(expected.item, item);\n        describeMismatch = func (item : A, description : Description) =\n            description.appendText(expected.display(item) # \" was expected to be \" # expected.display(expected.item));\n    };\n\n    /// Matches values greater than `expected`\n    public func greaterThan<A <: Int>(expected : Int) : Matcher<A> = {\n        matches = func (item : A) : Bool = item > expected;\n        describeMismatch = func (item : A, description : Description) =\n            description.appendText(Int.toText(expected) # \" was expected to be greater than \" # Int.toText(item));\n    };\n\n    /// Matches values greater than or equal to `expected`\n    public func greaterThanOrEqual<A <: Int>(expected : Int) : Matcher<A> = {\n        matches = func (item : A) : Bool = item >= expected;\n        describeMismatch = func (item : A, description : Description) =\n            description.appendText(Int.toText(expected) # \" was expected to be greater than or equal to \" # Int.toText(item));\n    };\n\n    /// Matches values less than `expected`\n    public func lessThan<A <: Int>(expected : Int) : Matcher<A> = {\n        matches = func (item : A) : Bool = item < expected;\n        describeMismatch = func (item : A, description : Description) =\n            description.appendText(Int.toText(expected) # \" was expected to be less than \" # Int.toText(item));\n    };\n\n    /// Matches values less than or equal to `expected`\n    public func lessThanOrEqual<A <: Int>(expected : Int) : Matcher<A> = {\n        matches = func (item : A) : Bool = item <= expected;\n        describeMismatch = func (item : A, description : Description) =\n            description.appendText(Int.toText(expected) # \" was expected to be less than or equal to \" # Int.toText(item));\n    };\n\n    /// Matches values for being in inclusive range `[lower .. upper]`\n    public func inRange<A <: Int>(lower : Int, upper : Int) : Matcher<A> = {\n        matches = func (item : A) : Bool = lower <= item and item <= upper;\n        describeMismatch = func (item : A, description : Description) =\n            description.appendText(Int.toText(item) # \" was expected to be in range [\" # Int.toText(lower) # \" .. \" # Int.toText(upper) # \"]\");\n    };\n\n    /// Matches if all matchers match, short circuits (like `and`)\n    public func allOf<A>(matchers : [Matcher<A>]) : Matcher<A> = {\n        matches = func (item : A) : Bool {\n            for (matcher in matchers.vals()) {\n                if (not matcher.matches(item)) {\n                    return false;\n                }\n            };\n            return true;\n        };\n        describeMismatch = func (item : A, description : Description) {\n            var first = true;\n            for (matcher in matchers.vals()) {\n                if (not matcher.matches(item)) {\n                    if (first) {\n                        first := false;\n                    } else {\n                        description.appendText(\"\\nand \");\n                    };\n                    matcher.describeMismatch(item, description);\n                };\n            };\n        };\n    };\n\n    /// Matches if any matchers match, short circuits (like `or`)\n    public func anyOf<A>(matchers : [Matcher<A>]) : Matcher<A> = {\n        matches = func (item : A) : Bool {\n            for (matcher in matchers.vals()) {\n                if (matcher.matches(item)) {\n                    return true;\n                }\n            };\n            return false;\n        };\n        describeMismatch = func (item : A, description : Description) {\n            var first = true;\n            for (matcher in matchers.vals()) {\n                if (not matcher.matches(item)) {\n                    if (first) {\n                        first := false;\n                    } else {\n                        description.appendText(\"\\nor \");\n                    };\n                    matcher.describeMismatch(item, description);\n                };\n            };\n        };\n    };\n\n    /// Matches if the wrapped matcher doesn’t match and vice versa\n    public func not_<A>(matcher : Matcher<A>) : Matcher<A> = {\n        matches = func (item : A) : Bool = not matcher.matches(item);\n        describeMismatch = func (item : A, description : Description) {\n            // Do I need to return a `Matcher<Testable<A>>` instead?\n            // Would be a little unfortunate\n            description.appendText(\"Shouldn't have matched.\");\n        };\n    };\n\n    /// Test an array’s elements against an array of matchers\n    public func array<A>(matchers : [Matcher<A>]) : Matcher<[A]> = {\n        matches = func (items : [A]) : Bool {\n            if (items.size() != matchers.size()) {\n                return false;\n            };\n            for (ix in items.keys()) {\n                if (not matchers[ix].matches(items[ix])) {\n                    return false\n                }\n            };\n            return true;\n        };\n        describeMismatch = func (items : [A], description : Description) {\n            if (items.size() != matchers.size()) {\n                description.appendText(\n                    \"Length mismatch between \" #\n                    Nat.toText(items.size()) #\n                    \" items and \" #\n                    Nat.toText(matchers.size()) #\n                    \" matchers\"\n                );\n                return;\n            };\n            for (ix in items.keys()) {\n                if (not matchers[ix].matches(items[ix])) {\n                    description.appendText(\"At index \" # Nat.toText(ix) # \": \");\n                    matchers[ix].describeMismatch(items[ix], description);\n                    description.appendText(\"\\n\");\n                }\n            };\n        };\n    };\n\n    /// Tests that a value is not-null\n    public func isSome<A>() : Matcher<?A> = {\n        matches = func (item : ?A) : Bool = Option.isSome(item);\n        describeMismatch = func (item : ?A, description : Description) =\n            description.appendText(\"expected some value, but got `null`\");\n    };\n\n    /// Tests that a value is null\n    public func isNull<A>() : Matcher<T.TestableItem<?A>> = {\n        matches = func (testable : T.TestableItem<?A>) : Bool = Option.isNull(testable.item);\n        describeMismatch = func (testable : T.TestableItem<?A>, description : Description) =\n            Option.iterate<A>(testable.item, func (i) {\n                description.appendText(\"expected `null`, but got \" # testable.display(?i))\n            });\n    };\n\n}\n"},"Canister.mo":{"content":"/// Unit testing for canisters\n///\n/// The `Tester` class in this module can be used to define unit tests for canisters.\nimport Buffer \"mo:base/Buffer\";\nimport List \"mo:base/List\";\nimport Nat \"mo:base/Nat\";\nimport M \"Matchers\";\n\nmodule {\n\npublic type Protocol = { #start : Nat; #cont : [Text]; #done : [Text] };\npublic type TestResult = { #success; #fail : Text };\n\ntype Test = (Text, () -> async TestResult);\n\n/// Instantiate one of these on canister initialization. Then you can use it to\n/// register your tests and it will take care of running them.\n///\n/// Use `runAll` for simple setups and `run` once that stops working.\n///\n/// When using `run` the `Tester` will execute your tests in the right batch\n/// sizes. It will keep calling your `test` function over and over, so make sure\n/// to not do any work outside the registered tests.\n///\n/// ```motoko\n/// import Canister \"canister:YourCanisterNameHere\";\n/// import C \"mo:matchers/Canister\";\n/// import M \"mo:matchers/Matchers\";\n/// import T \"mo:matchers/Testable\";\n///\n/// actor {\n///     let it = C.Tester({ batchSize = 8 });\n///     public shared func test() : async Text {\n///\n///         it.should(\"greet me\", func () : async C.TestResult = async {\n///           let greeting = await Canister.greet(\"Christoph\");\n///           M.attempt(greeting, M.equals(T.text(\"Hello, Christoph!\")))\n///         });\n///\n///         it.shouldFailTo(\"greet him-whose-name-shall-not-be-spoken\", func () : async () = async {\n///           let greeting = await Canister.greet(\"Voldemort\");\n///           ignore greeting\n///         });\n///\n///         await it.runAll()\n///         // await it.run()\n///     }\n/// }\n/// ```\npublic class Tester(options : { batchSize : Nat }) {\n    var tests : List.List<Test> = List.nil();\n    var running : Bool = false;\n\n    /// Registers a test. You can use `attempt` to use a `Matcher` to\n    /// produce a `TestResult`.\n    public func should(name : Text, test : () -> async TestResult) {\n        if(running) return;\n        tests := List.push((name, test), tests);\n    };\n\n    /// Registers a test that should throw an exception.\n    public func shouldFailTo(name : Text, test : () -> async ()) {\n        if(running) return;\n        tests := List.push((name, func () : async TestResult = async {\n          try {\n            let testResult = await test();\n            #fail(\"Should've failed, but didn't\")\n          } catch _ {\n              #success\n          }\n        }), tests)\n    };\n\n    /// Runs all your tests in one go and returns a summary Text. If calling\n    /// this runs out of gas, try using `run` with a configured `batchSize`.\n    public func runAll() : async Text {\n        running := true;\n        var allTests = List.reverse(tests);\n        var result = \"\";\n        var failed = 0;\n        var testCount = List.size(allTests);\n        label l loop {\n            switch allTests {\n                case null break l;\n                case (?((name, test), tl)) {\n                    allTests := tl;\n                    try {\n                        result #= switch (await test()) {\n                            case (#success) {\n                                \"\\\"\" # name # \"\\\"\" # \" succeeded.\\n\"\n                            };\n                            case (#fail(msg)) {\n                                failed += 1;\n                                \"\\\"\" # name # \"\\\"\" # \" failed: \" # msg # \"\\n\"\n                            };\n                        };\n                    } catch _ {\n                        failed += 1;\n                        result #= \"\\\"\" # name # \"\\\"\" # \"failed with an unexpected trap.\" # \"\\n\";\n                    }\n                };\n            }\n        };\n\n        if (failed == 0) {\n            result #= \"Success! \"\n        } else {\n            result #= \"Failure! \"\n        };\n        result # Nat.toText(testCount - failed) # \"/\" # Nat.toText(testCount) # \" succeeded.\"\n    };\n\n    /// You must call this as the last thing in your unit test.\n    public func run() : async Protocol {\n        if (not running) {\n            running := true;\n            tests := List.reverse(tests);\n            return #start(List.size(tests))\n        };\n        let results : Buffer.Buffer<Text> = Buffer.Buffer(options.batchSize);\n        var capacity = options.batchSize;\n        while(capacity > 0) {\n            capacity -= 1;\n            switch tests {\n                case null {\n                    return #done(results.toArray())\n                };\n                case (?((name, test), tl)) {\n                    tests := tl;\n                    try {\n                        let testResult = await test();\n                        results.add(switch testResult {\n                            case (#success) {\n                                \"\\\"\" # name # \"\\\"\" # \" succeeded.\\n\"\n                            };\n                            case (#fail(msg)) {\n                                \"\\\"\" # name # \"\\\"\" # \" failed: \" # msg # \"\\n\"\n                            };\n                        });\n                    } catch _ {\n                        results.add(\"\\\"\" # name # \"\\\"\" # \"failed with an unexpected trap.\" # \"\\n\");\n                    }\n                }\n            }\n        };\n        return if(List.isNil(tests)) {\n            #done(results.toArray());\n        } else {\n            #cont(results.toArray());\n        };\n    }\n}\n}\n"},"matchers/Hashmap.mo":{"content":"/// Matchers for Hashmaps\n///\n/// This module contains utility matchers that make it easier\n/// to write assertions that involve Hashmaps.\n\nimport HM \"mo:base/HashMap\";\nimport M \"../Matchers\";\nimport Option \"mo:base/Option\";\nimport T \"../Testable\";\n\nmodule {\n\n    /// Tests that a HashMap contains a key\n    public func hasKey<K, V>(key : T.TestableItem<K>) : M.Matcher<HM.HashMap<K, V>> = {\n        matches = func (map : HM.HashMap<K, V>) : Bool = Option.isSome(map.get(key.item));\n        describeMismatch = func (map : HM.HashMap<K, V>, description : M.Description) {\n            description.appendText(\"Missing key \" # key.display(key.item))\n        };\n    };\n\n    /// Tests that a HashMap matches at a given key\n    public func atKey<K, V>(key : T.TestableItem<K>, matcher : M.Matcher<V>) : M.Matcher<HM.HashMap<K, V>> = {\n        matches = func (map : HM.HashMap<K, V>) : Bool =\n            Option.getMapped(map.get(key.item), matcher.matches, false);\n        describeMismatch = func (map : HM.HashMap<K, V>, description : M.Description) {\n            switch (map.get(key.item)) {\n                case null {\n                    description.appendText(\"Missing key \" # key.display(key.item))\n                };\n                case (?v) {\n                    matcher.describeMismatch(v, description)\n                };\n            }\n        };\n    };\n}\n"},"Suite.mo":{"content":"/// A simple test runner\n///\n/// The functions in this module let you build up trees of tests, and run them.\n///\n/// ```motoko\n/// import M \"mo:matchers/Matchers\";\n/// import T \"mo:matchers/Testable\";\n/// import Suite \"mo:matchers/Suite\";\n///\n/// let suite = Suite.suite(\"My test suite\", [\n///     Suite.suite(\"Nat tests\", [\n///         Suite.test(\"10 is 10\", 10, M.equals(T.nat(10))),\n///         Suite.test(\"5 is greater than three\", 5, M.greaterThan<Nat>(3)),\n///     ])\n/// ]);\n/// Suite.run(suite);\n/// ```\n\nimport Array \"mo:base/Array\";\nimport Debug \"mo:base/Debug\";\nimport Matchers \"Matchers\";\nimport Nat \"mo:base/Nat\";\n\nmodule {\n\n    type Failure = {\n        names : [Text];\n        error : Matchers.Description;\n    };\n\n    func joinWith(xs : [Text], sep : Text) : Text {\n        let size = xs.size();\n\n        if (size == 0) return \"\";\n        if (size == 1) return xs[0];\n\n        var result = xs[0];\n        var i = 0;\n        label l loop {\n            i += 1;\n            if (i >= size) { break l; };\n            result #= sep # xs[i]\n        };\n        result\n    };\n\n    func displayFailure(failure : Failure) : Text =\n      \"\\n\" # joinWith(failure.names, \"/\") # \" failed:\\n\" # failure.error.toText();\n\n    /// A collection of tests to be run together\n    // TODO: Maybe this should be a Forest rather than a Tree?\n    public type Suite = {\n        #node : { name : Text; children : [Suite] };\n        #test : { name : Text; test: () -> ?Matchers.Description };\n    };\n\n    func prependPath(name : Text) : Failure -> Failure = func (failure : Failure) : Failure = {\n        names = Array.append([name], failure.names);\n        error = failure.error;\n    };\n\n    func runInner(suite : Suite) : [Failure] {\n        switch suite {\n            case (#node({ name; children })) {\n                let childFailures = Array.flatten (Array.map(children, runInner));\n                Array.map(childFailures, prependPath(name))\n            };\n            case (#test({ name; test })) {\n                switch(test()) {\n                    case null {\n                        []\n                    };\n                    case (?err) {\n                        [{ names = [name]; error = err }]\n                    }\n                }\n            };\n        }\n    };\n\n    /// Runs a given suite of tests. Will exit with a non-zero exit code in case any of the tests fail.\n    public func run(suite : Suite) {\n        let failures = runInner(suite);\n        if (failures.size() == 0) {\n            Debug.print(\"All tests passed.\");\n        } else {\n            for (failure in failures.vals()) {\n                Debug.print(displayFailure(failure))\n            };\n            Debug.print(\"\\n\" # Nat.toText(failures.size()) # \" tests failed.\");\n\n            // Is there a more graceful way to `exit(1)` here?\n            assert(false)\n        }\n    };\n\n    /// Constructs a test suite from a name and an Array of\n    public func suite(suiteName : Text, suiteChildren : [Suite]) : Suite {\n        #node({ name = suiteName; children = suiteChildren })\n    };\n\n    /// Constructs a single test by matching the given `item` against a `matcher`.\n    public func test<A>(testName : Text, item : A, matcher : Matchers.Matcher<A>) : Suite {\n        testLazy(testName, func(): A = item, matcher)\n    };\n\n    /// Like `test`, but accepts a thunk `mkItem` that creates the value to match against.\n    /// Use this to delay the evaluation of the to be matched value until the tests actually run.\n    public func testLazy<A>(testName : Text, mkItem : () -> A, matcher : Matchers.Matcher<A>) : Suite {\n        #test({\n            name = testName;\n            test = func () : ?Matchers.Description {\n                let item = mkItem();\n                if (matcher.matches(item)) {\n                    null\n                } else {\n                    let description = Matchers.Description();\n                    matcher.describeMismatch(item, description);\n                    ?(description)\n                };\n            }\n        })\n    };\n}\n"},"Testable.mo":{"content":"/// Things we can compare in tests\n///\n/// This module contains the `Testable<A>` abstraction, which bundles\n/// `toText` and `equals` for a type `A` so we can use them as \"expected\"\n/// values in tests.\n/// It also contains a few helpers to build `Testable`'s for compound types\n/// like Arrays and Optionals. If you want to test your own objects or control\n/// how things are printed and compared in your own tests you'll need to create\n/// your own `Testable`'s.\n/// ```motoko\n/// import T \"mo:matchers/Testable\";\n///\n/// type Person = { name : Text, surname : ?Text };\n/// // Helper\n/// let optText : Testable<(?Text)> = T.optionalTestable(T.textTestable)\n/// let testablePerson : Testable<Person> = {\n///    display = func (person : Person) : Text =\n///        person.name # \" \" #\n///        optText.display(person.surname)\n///    equals = func (person1 : Person, person2 : Person) : Bool =\n///        person1.name == person2.name and\n///        optText.equals(person1.surname, person2.surname)\n/// }\n/// ```\nimport Array \"mo:base/Array\";\nimport Bool \"mo:base/Bool\";\nimport Int \"mo:base/Int\";\nimport List \"mo:base/List\";\nimport Nat \"mo:base/Nat\";\nimport Nat8 \"mo:base/Nat8\";\nimport Nat16 \"mo:base/Nat16\";\nimport Nat32 \"mo:base/Nat32\";\nimport Nat64 \"mo:base/Nat64\";\nimport Result \"mo:base/Result\";\nimport Prim \"mo:prim\";\n\nmodule {\n    /// Packs up all the functions we need to compare and display values under test\n    public type Testable<A> = {\n        display : A -> Text;\n        equals : (A, A) -> Bool\n    };\n\n    /// A value combined with its `Testable`\n    public type TestableItem<A> = {\n        display : A -> Text;\n        equals : (A, A) -> Bool;\n        item : A;\n    };\n\n    public let textTestable : Testable<Text> = {\n        // TODO Actually escape the text here\n        display = func (text : Text) : Text { \"\\\"\" # text # \"\\\"\" };\n        equals = func (t1 : Text, t2 : Text) : Bool { t1 == t2 }\n    };\n\n    public func text(t : Text) : TestableItem<Text> = {\n        item = t;\n        display = textTestable.display;\n        equals = textTestable.equals;\n    };\n\n    public let natTestable : Testable<Nat> = {\n        display = func (nat : Nat) : Text = Nat.toText(nat);\n        equals = func (n1 : Nat, n2 : Nat) : Bool = n1 == n2\n    };\n\n    public func nat(n : Nat) : TestableItem<Nat> = {\n        item = n;\n        display = natTestable.display;\n        equals = natTestable.equals;\n    };\n\n    public let nat8Testable : Testable<Nat8> = {\n        display = func (nat : Nat8) : Text {Nat8.toText(nat)};\n        equals = func (n1 : Nat8, n2 : Nat8) : Bool {n1 == n2};\n    };\n\n    public func nat8(n : Nat8) : TestableItem<Nat8> = {\n        item = n;\n        display = nat8Testable.display;\n        equals = nat8Testable.equals;\n    };\n\n    public let nat16Testable : Testable<Nat16> = {\n        display = func (nat : Nat16) : Text {Nat16.toText(nat)};\n        equals = func (n1 : Nat16, n2 : Nat16) : Bool {n1 == n2};\n    };\n\n    public func nat16(n : Nat16) : TestableItem<Nat16> = {\n        item = n;\n        display = nat16Testable.display;\n        equals = nat16Testable.equals;\n    };\n\n    public let nat32Testable : Testable<Nat32> = {\n        display = func (nat : Nat32) : Text {Nat32.toText(nat)};\n        equals = func (n1 : Nat32, n2 : Nat32) : Bool {n1 == n2};\n    };\n\n    public func nat32(n : Nat32) : TestableItem<Nat32> = {\n        item = n;\n        display = nat32Testable.display;\n        equals = nat32Testable.equals;\n    };\n\n\n    public let nat64Testable : Testable<Nat64> = {\n        display = func (nat : Nat64) : Text {Nat64.toText(nat)};\n        equals = func (n1 : Nat64, n2 : Nat64) : Bool {n1 == n2};\n    };\n\n    public func nat64(n : Nat64) : TestableItem<Nat64> = {\n        item = n;\n        display = nat64Testable.display;\n        equals = nat64Testable.equals;\n    };\n\n    public let intTestable : Testable<Int> = {\n        display = func (n : Int) : Text = Int.toText(n);\n        equals = func (n1 : Int, n2 : Int) : Bool = n1 == n2\n    };\n\n    public func int(n : Int) : TestableItem<Int> = {\n        item = n;\n        display = intTestable.display;\n        equals = intTestable.equals;\n    };\n\n    public let boolTestable : Testable<Bool> = {\n        display = func (n : Bool) : Text = Bool.toText(n);\n        equals = func (n1 : Bool, n2 : Bool) : Bool = n1 == n2\n    };\n\n    public func bool(n : Bool) : TestableItem<Bool> = {\n        item = n;\n        display = boolTestable.display;\n        equals = boolTestable.equals;\n    };\n\n    public let charTestable : Testable<Char> = {\n        display = func (n : Char) : Text { \"'\" # Prim.charToText(n) # \"'\" };\n        equals = func (n1 : Char, n2 : Char) : Bool { n1 == n2 };\n    };\n\n    public func char(n : Char) : TestableItem<Char> {\n        {\n            item = n;\n            display = charTestable.display;\n            equals = charTestable.equals;\n        }\n    };\n\n    public func arrayTestable<A>(testableA : Testable<A>) : Testable<[A]> {\n        {\n            display = func (xs : [A]) : Text =\n                \"[\" # joinWith(Array.map<A, Text>(xs, testableA.display), \", \") # \"]\";\n            equals = func (xs1 : [A], xs2 : [A]) : Bool =\n                Array.equal(xs1, xs2, testableA.equals)\n        }\n    };\n\n    public func array<A>(testableA : Testable<A>, xs : [A]) : TestableItem<[A]> {\n        let testableAs = arrayTestable(testableA);\n        {\n            item = xs;\n            display = testableAs.display;\n            equals = testableAs.equals;\n        };\n    };\n\n    public func listTestable<A>(testableA : Testable<A>) : Testable<List.List<A>> = {\n        display = func (xs : List.List<A>) : Text =\n          // TODO fix leading comma\n            \"[\" #\n            List.foldLeft(xs, \"\", func(acc : Text, x : A) : Text =\n                acc # \", \" # testableA.display(x)\n            ) #\n            \"]\";\n        equals = func (xs1 : List.List<A>, xs2 : List.List<A>) : Bool =\n            List.equal(xs1, xs2, testableA.equals)\n    };\n\n    public func list<A>(testableA : Testable<A>, xs : List.List<A>) : TestableItem<List.List<A>> {\n        let testableAs = listTestable(testableA);\n        {\n            item = xs;\n            display = testableAs.display;\n            equals = testableAs.equals;\n        };\n    };\n\n    public func optionalTestable<A>(testableA : Testable<A>) : Testable<?A> {\n        {\n            display = func (x : ?A) : Text = switch(x) {\n                case null { \"null\" };\n                case (?a) { \"(?\" # testableA.display(a) # \")\" };\n            };\n            equals = func (x1 : ?A, x2 : ?A) : Bool = switch(x1) {\n                case null switch(x2) {\n                    case null { true };\n                    case _ { false };\n                };\n                case (?x1) switch(x2) {\n                    case null { false };\n                    case (?x2) { testableA.equals(x1, x2) };\n                };\n            };\n        }\n    };\n\n    public func optional<A>(testableA : Testable<A>, x : ?A) : TestableItem<?A> {\n        let testableOA = optionalTestable(testableA);\n        {\n            item = x;\n            display = testableOA.display;\n            equals = testableOA.equals;\n        };\n    };\n\n    public func resultTestable<R, E>(\n        rTestable : Testable<R>,\n        eTestable : Testable<E>\n    ) : Testable<Result.Result<R, E>> = {\n        display = func (r : Result.Result<R, E>) : Text = switch r {\n            case (#ok(ok)) {\n                \"#ok(\" # rTestable.display(ok) # \")\"\n            };\n            case (#err(err)) {\n                \"#err(\" # eTestable.display(err) # \")\"\n            };\n        };\n        equals = func (r1 : Result.Result<R, E>, r2 : Result.Result<R, E>) : Bool = switch (r1, r2) {\n            case (#ok(ok1), #ok(ok2)) {\n                rTestable.equals(ok1, ok2)\n            };\n            case (#err(err1), #err(err2)) {\n                eTestable.equals(err1, err2)\n            };\n            case (_) { false };\n        };\n    };\n\n    public func result<R, E>(\n      rTestable : Testable<R>,\n      eTestable : Testable<E>,\n      x : Result.Result<R, E>\n    ) : TestableItem<Result.Result<R, E>> {\n        let resTestable = resultTestable(rTestable, eTestable);\n        {\n            display = resTestable.display;\n            equals = resTestable.equals;\n            item = x;\n        }\n    };\n\n    public func tuple2Testable<A, B>(ta : Testable<A>, tb : Testable<B>) : Testable<(A, B)> {\n      {\n          display = func ((a, b) : (A, B)) : Text =\n              \"(\" # ta.display(a) # \", \" # tb.display(b) # \")\";\n          equals = func((a1, b1) : (A, B), (a2, b2) : (A, B)) : Bool =\n              ta.equals(a1, a2) and tb.equals(b1, b2);\n      }\n    };\n\n    public func tuple2<A, B>(ta : Testable<A>, tb : Testable<B>, x : (A, B)) : TestableItem<(A, B)> {\n      let testableTAB = tuple2Testable(ta, tb);\n      {\n          item = x;\n          display = testableTAB.display;\n          equals = testableTAB.equals;\n      }\n    };\n\n    func joinWith(xs : [Text], sep : Text) : Text {\n        let size = xs.size();\n\n        if (size == 0) return \"\";\n        if (size == 1) return xs[0];\n\n        var result = xs[0];\n        var i = 0;\n        label l loop {\n            i += 1;\n            if (i >= size) { break l; };\n            result #= sep # xs[i]\n        };\n        result\n    };\n}\n"}}}