From d7ad0b2629bc64fb9b5f06d07347c630bc38a729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=A9v=C3=A9?= Date: Sat, 9 Apr 2022 06:04:16 +0100 Subject: [PATCH] feat: Adds Dynamic.sort & improves output of failing dynamic tests (#1411) * feat: Adds Dynamic.sort * test: Adds tests for Dynamic.sort * refactor: Makes dynamic test handler display diff of expected vs actual instead of displaying "true" : "true" * test: Removes complex implementation of list-equal-unordered Replaces it with sort + equal. While this is less "correct", having complex untested functions within test files is undesirable. This implementation is "good enough" for lists of integers. --- core/List.carp | 28 ++++++++++++++++++++++------ core/Test.carp | 27 +++++++++++++++++---------- test/dynamic_map.carp | 15 ++------------- test/list.carp | 20 ++++++++++++++++++++ 4 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 test/list.carp diff --git a/core/List.carp b/core/List.carp index 43a6950e..f50df7a7 100644 --- a/core/List.carp +++ b/core/List.carp @@ -17,9 +17,9 @@ Example: (= 'a (cadr x)) car (= 'd (cadr x)) cdr (macro-error "`cxr` expects either `a` or `d` symbols, got " (cadr x))) - (if (= 1 (car x)) - (cxr (cddr x) pair) - (cxr (cons (- (car x) 1) (cdr x)) pair))))) + (if (= 1 (car x)) + (cxr (cddr x) pair) + (cxr (cons (- (car x) 1) (cdr x)) pair))))) (doc nthcdr "takes the `n`th tail or `cdr` of the list `pair`.") (defndynamic nthcdr [n pair] @@ -313,6 +313,23 @@ Example: (let [r (walk-replace-finder pairs x)] (if (empty? r) x (cadr r)))) form)) + (doc sort "Sorts a list using the provided predicate. It is not a stable sort. +Example: +``` +(sort '(1 3 4 2 5 4) <) ; => (1 2 3 4 4 5) +(sort '(1 3 4 2 5 4) >) ; => (5 4 4 3 2 1) +(sort '(\"one\" \"two---\" \"three\" \"four\") (fn [a b] (< (String.length a) (String.length b)))) ; => (\"one\" \"four\" \"three\" \"two---\") +```") + (defndynamic sort [l compare] + (if (nil? l) + '() + (let [x (car l) + xs (cdr l) + lower-filtered (filter (fn [y] (compare y x)) xs) + lower-sorted (Dynamic.sort lower-filtered compare) + higher-filtered (filter (fn [y] (not (compare y x))) xs) + higher-sorted (Dynamic.sort higher-filtered compare)] + (append lower-sorted (append (list x) higher-sorted))))) (defmodule List (doc pairs "makes a list of pairs out of a list `l`. If the number of @@ -367,6 +384,5 @@ Returns `nil` on failure") (let [res (List.find-index (cdr l) pred)] (if (nil? res) res - (inc res))))) - ) -) + (inc res))))))) + diff --git a/core/Test.carp b/core/Test.carp index 878b3e0a..9744ae05 100644 --- a/core/Test.carp +++ b/core/Test.carp @@ -18,9 +18,9 @@ Example: (deftype State [passed Int, failed Int]) (hidden State) (use Color.Id) - (hidden handler) - (defn handler [state expected actual descr what op] - (if (op expected actual) + (hidden display-test) + (defn display-test [state expected actual descr what is-success] + (if is-success (do (IO.colorize (Green) &(str* @"Test '" @descr @"' passed\n")) (State.update-passed (State.copy state) &Int.inc)) @@ -33,6 +33,14 @@ Example: (IO.color (Reset)) (State.update-failed (State.copy state) &Int.inc)))) + (hidden handler) + (defn handler [state expected actual descr what op] + (display-test state expected actual descr what (op expected actual))) + + (hidden dynhandler) + (defndynamic dynhandler [state expected actual descr what op] + (list 'Test.display-test state (str expected) (str actual) descr what (op expected actual))) + (doc assert-op "Assert that op returns true when given x and y.") (defn assert-op [state x y descr op] (handler state x y descr "value" op)) @@ -73,13 +81,13 @@ Example: (defn assert-error [state x descr] (assert-true state (Result.error? x) descr)) - (doc assert-dynamic-equal "Assert that the dynamic expressions `x` and `y` are equal.") - (defmacro assert-dynamic-equal [state x y descr] - `(Test.assert-equal %state true %(= (eval x) (eval y)) %descr)) - (doc assert-dynamic-op "Assert that the dynamic expressions `x` and `y` are equal.") (defmacro assert-dynamic-op [state x y descr op] - `(Test.assert-equal %state true %(op (eval x) (eval y)) %descr)) + (dynhandler state (eval x) (eval y) descr "value" op)) + + (doc assert-dynamic-equal "Assert that the dynamic expressions `x` and `y` are equal.") + (defmacro assert-dynamic-equal [state x y descr] + (dynhandler state (eval x) (eval y) descr "value" =)) (doc reset "Reset test state.") (defn reset [state] @@ -170,8 +178,7 @@ Example: `@(Test.State.failed %name) (cons-last `(Test.print-test-results %name) - `(do %@(with-test-internal name forms)))) - )) + `(do %@(with-test-internal name forms)))))) (defmacro deftest [name :rest forms] (eval diff --git a/test/dynamic_map.carp b/test/dynamic_map.carp index 5af3a75e..64c8d2a7 100644 --- a/test/dynamic_map.carp +++ b/test/dynamic_map.carp @@ -3,19 +3,8 @@ (doc list-equal-unordered "Checks two lists have the same values, not necessarily in the same order") (defndynamic list-equal-unordered [xs ys] - (if (not (= (length xs) (length ys))) - false - (car (reduce (fn [state x] - (let [keep-going (car state) - l (cadr state)] - (if (not keep-going) - '(false ()) - (let [index (List.find-index l (curry = x))] - (if (nil? index) - '(false ()) - (list true (List.remove-nth l index))))))) - (list true ys) - xs)))) + (= (Dynamic.sort xs <) + (Dynamic.sort ys <))) (deftest test (assert-dynamic-equal test diff --git a/test/list.carp b/test/list.carp new file mode 100644 index 00000000..9b8d6ba1 --- /dev/null +++ b/test/list.carp @@ -0,0 +1,20 @@ +(load-and-use Test) + +(deftest test + (assert-dynamic-equal test + '(1 2 3 3 4) + (Dynamic.sort '(3 4 3 1 2) <) + "Dynamic.sort sorts from lower to higher") + + (assert-dynamic-equal test + '(4 3 3 2 1) + (Dynamic.sort '(3 4 3 1 2) >) + "Dynamic.sort sorts from higher to lower") + + (assert-dynamic-equal test + '("one" "four" "three" "two---") + (Dynamic.sort '("one" "two---" "three" "four") + (fn [a b] + (< (String.length a) (String.length b)))) + "Dynamic.sort sorts using predicate")) +