From 67e9b85708b623388c278f913f7719d5599e4138 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 15 Oct 2023 21:49:00 -0300 Subject: [PATCH] move private doctests to pytests for exp_structure and fuctional (#927) and another --- mathics/builtin/exp_structure/general.py | 4 - mathics/builtin/exp_structure/size_and_sig.py | 11 -- mathics/builtin/functional/application.py | 16 -- .../builtin/functional/apply_fns_to_lists.py | 48 ----- .../functional/functional_iteration.py | 16 -- test/builtin/test_exp_structure.py | 68 +++++++ test/builtin/test_functional.py | 171 ++++++++++++++++++ 7 files changed, 239 insertions(+), 95 deletions(-) create mode 100644 test/builtin/test_exp_structure.py create mode 100644 test/builtin/test_functional.py diff --git a/mathics/builtin/exp_structure/general.py b/mathics/builtin/exp_structure/general.py index aa40b57c5..f18663edd 100644 --- a/mathics/builtin/exp_structure/general.py +++ b/mathics/builtin/exp_structure/general.py @@ -364,10 +364,6 @@ class Operate(Builtin): With $n$=0, 'Operate' acts like 'Apply': >> Operate[p, f[a][b][c], 0] = p[f[a][b][c]] - - #> Operate[p, f, -1] - : Non-negative integer expected at position 3 in Operate[p, f, -1]. - = Operate[p, f, -1] """ summary_text = "apply a function to the head of an expression" diff --git a/mathics/builtin/exp_structure/size_and_sig.py b/mathics/builtin/exp_structure/size_and_sig.py index 24b9cd121..e54bc37a7 100644 --- a/mathics/builtin/exp_structure/size_and_sig.py +++ b/mathics/builtin/exp_structure/size_and_sig.py @@ -165,17 +165,6 @@ class LeafCount(Builtin): >> LeafCount[100!] = 1 - - #> LeafCount[f[a, b][x, y]] - = 5 - - #> NestList[# /. s[x_][y_][z_] -> x[z][y[z]] &, s[s][s][s[s]][s][s], 4]; - #> LeafCount /@ % - = {7, 8, 8, 11, 11} - - #> LeafCount[1 / 3, 1 + I] - : LeafCount called with 2 arguments; 1 argument is expected. - = LeafCount[1 / 3, 1 + I] """ messages = { diff --git a/mathics/builtin/functional/application.py b/mathics/builtin/functional/application.py index db092937b..8275c615a 100644 --- a/mathics/builtin/functional/application.py +++ b/mathics/builtin/functional/application.py @@ -58,13 +58,6 @@ class Function(PostfixOperator): >> g[#] & [h[#]] & [5] = g[h[5]] - #> g[x_,y_] := x+y - #> g[Sequence@@Slot/@Range[2]]&[1,2] - = #1 + #2 - #> Evaluate[g[Sequence@@Slot/@Range[2]]]&[1,2] - = 3 - - In the evaluation process, the attributes associated with an Expression are \ determined by its Head. If the Head is also a non-atomic Expression, in general,\ no Attribute is assumed. In particular, it is what happens when the head \ @@ -180,12 +173,6 @@ class Slot(Builtin): Recursive pure functions can be written using '#0': >> If[#1<=1, 1, #1 #0[#1-1]]& [10] = 3628800 - - #> # // InputForm - = #1 - - #> #0 // InputForm - = #0 """ attributes = A_N_HOLD_ALL | A_PROTECTED @@ -216,9 +203,6 @@ class SlotSequence(Builtin): >> FullForm[##] = SlotSequence[1] - - #> ## // InputForm - = ##1 """ attributes = A_N_HOLD_ALL | A_PROTECTED diff --git a/mathics/builtin/functional/apply_fns_to_lists.py b/mathics/builtin/functional/apply_fns_to_lists.py index 171901e02..b34a954ff 100644 --- a/mathics/builtin/functional/apply_fns_to_lists.py +++ b/mathics/builtin/functional/apply_fns_to_lists.py @@ -66,10 +66,6 @@ class Apply(BinaryOperator): Convert all operations to lists: >> Apply[List, a + b * c ^ e * f[g], {0, Infinity}] = {a, {b, {g}, {c, e}}} - - #> Apply[f, {a, b, c}, x+y] - : Level specification x + y is not of the form n, {n}, or {m, n}. - = Apply[f, {a, b, c}, x + y] """ summary_text = "apply a function to a list, at specified levels" @@ -130,10 +126,6 @@ class Map(BinaryOperator): Include heads: >> Map[f, a + b + c, Heads->True] = f[Plus][f[a], f[b], f[c]] - - #> Map[f, expr, a+b, Heads->True] - : Level specification a + b is not of the form n, {n}, or {m, n}. - = Map[f, expr, a + b, Heads -> True] """ summary_text = "map a function over a list, at specified levels" @@ -284,10 +276,6 @@ class MapIndexed(Builtin): Thus, mapping 'Extract' on the indices given by 'MapIndexed' re-constructs the original expression: >> MapIndexed[Extract[expr, #2] &, listified, {-1}, Heads -> True] = a + b f[g] c ^ e - - #> MapIndexed[f, {1, 2}, a+b] - : Level specification a + b is not of the form n, {n}, or {m, n}. - = MapIndexed[f, {1, 2}, a + b] """ summary_text = "map a function, including index information" @@ -338,31 +326,6 @@ class MapThread(Builtin): >> MapThread[f, {{{a, b}, {c, d}}, {{e, f}, {g, h}}}, 2] = {{f[a, e], f[b, f]}, {f[c, g], f[d, h]}} - - #> MapThread[f, {{a, b}, {c, d}}, {1}] - : Non-negative machine-sized integer expected at position 3 in MapThread[f, {{a, b}, {c, d}}, {1}]. - = MapThread[f, {{a, b}, {c, d}}, {1}] - - #> MapThread[f, {{a, b}, {c, d}}, 2] - : Object {a, b} at position {2, 1} in MapThread[f, {{a, b}, {c, d}}, 2] has only 1 of required 2 dimensions. - = MapThread[f, {{a, b}, {c, d}}, 2] - - #> MapThread[f, {{a}, {b, c}}] - : Incompatible dimensions of objects at positions {2, 1} and {2, 2} of MapThread[f, {{a}, {b, c}}]; dimensions are 1 and 2. - = MapThread[f, {{a}, {b, c}}] - - #> MapThread[f, {}] - = {} - - #> MapThread[f, {a, b}, 0] - = f[a, b] - #> MapThread[f, {a, b}, 1] - : Object a at position {2, 1} in MapThread[f, {a, b}, 1] has only 0 of required 1 dimensions. - = MapThread[f, {a, b}, 1] - - ## Behaviour extends MMA - #> MapThread[f, {{{a, b}, {c}}, {{d, e}, {f}}}, 2] - = {{f[a, d], f[b, e]}, {f[c, f]}} """ summary_text = "map a function across corresponding elements in multiple lists" @@ -450,17 +413,6 @@ class Scan(Builtin): | 1 | 2 | 3 - - #> Scan[Print, f[g[h[x]]], 2] - | h[x] - | g[h[x]] - - #> Scan[Print][{1, 2}] - | 1 - | 2 - - #> Scan[Return, {1, 2}] - = 1 """ summary_text = "scan over every element of a list, applying a function" diff --git a/mathics/builtin/functional/functional_iteration.py b/mathics/builtin/functional/functional_iteration.py index fba768326..01adb3353 100644 --- a/mathics/builtin/functional/functional_iteration.py +++ b/mathics/builtin/functional/functional_iteration.py @@ -32,14 +32,6 @@ class FixedPoint(Builtin): >> FixedPoint[#+1 &, 1, 20] = 21 - - #> FixedPoint[f, x, 0] - = x - #> FixedPoint[f, x, -1] - : Non-negative integer expected. - = FixedPoint[f, x, -1] - #> FixedPoint[Cos, 1.0, Infinity] - = 0.739085 """ options = { @@ -116,14 +108,6 @@ class FixedPointList(Builtin): = {14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 1} >> ListLinePlot[list] = -Graphics- - - #> FixedPointList[f, x, 0] - = {x} - #> FixedPointList[f, x, -1] - : Non-negative integer expected. - = FixedPointList[f, x, -1] - #> Last[FixedPointList[Cos, 1.0, Infinity]] - = 0.739085 """ summary_text = "nest until a fixed point is reached return a list " diff --git a/test/builtin/test_exp_structure.py b/test/builtin/test_exp_structure.py new file mode 100644 index 000000000..d80594d56 --- /dev/null +++ b/test/builtin/test_exp_structure.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +Unit tests for mathics.builtin.exp_structure +""" + +import sys +import time +from test.helper import check_evaluation, evaluate + +import pytest + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ("ClearAll[f,a,b,x,y];", None, "Null", None), + ("LeafCount[f[a, b][x, y]]", None, "5", None), + ( + "data=NestList[# /. s[x_][y_][z_] -> x[z][y[z]] &, s[s][s][s[s]][s][s], 4];", + None, + "Null", + None, + ), + ("LeafCount /@ data", None, "{7, 8, 8, 11, 11}", None), + ("Clear[data];", None, "Null", None), + ( + "LeafCount[1 / 3, 1 + I]", + ("LeafCount called with 2 arguments; 1 argument is expected.",), + "LeafCount[1 / 3, 1 + I]", + None, + ), + ], +) +def test_private_doctests_exp_size_and_sig(str_expr, msgs, str_expected, fail_msg): + """exp_structure.size_and_sig""" + check_evaluation( + str_expr, + str_expected, + to_string_expr=True, + to_string_expected=True, + hold_expected=True, + failure_message=fail_msg, + expected_messages=msgs, + ) + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ( + "Operate[p, f, -1]", + ("Non-negative integer expected at position 3 in Operate[p, f, -1].",), + "Operate[p, f, -1]", + None, + ), + ], +) +def test_private_doctests_general(str_expr, msgs, str_expected, fail_msg): + """exp_structure.general""" + check_evaluation( + str_expr, + str_expected, + to_string_expr=True, + to_string_expected=True, + hold_expected=True, + failure_message=fail_msg, + expected_messages=msgs, + ) diff --git a/test/builtin/test_functional.py b/test/builtin/test_functional.py new file mode 100644 index 000000000..2522cdc38 --- /dev/null +++ b/test/builtin/test_functional.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +""" +Unit tests for mathics.builtin.functional +""" + +import sys +import time +from test.helper import check_evaluation, evaluate, session + +import pytest + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ("ClearAll[f, g, h,x,y,a,b,c];", None, None, None), + ( + "Apply[f, {a, b, c}, x+y]", + ("Level specification x + y is not of the form n, {n}, or {m, n}.",), + "Apply[f, {a, b, c}, x + y]", + None, + ), + ( + "Map[f, expr, a+b, Heads->True]", + ("Level specification a + b is not of the form n, {n}, or {m, n}.",), + "Map[f, expr, a + b, Heads -> True]", + None, + ), + ( + "MapIndexed[f, {1, 2}, a+b]", + ("Level specification a + b is not of the form n, {n}, or {m, n}.",), + "MapIndexed[f, {1, 2}, a + b]", + None, + ), + ( + "MapThread[f, {{a, b}, {c, d}}, {1}]", + ( + "Non-negative machine-sized integer expected at position 3 in MapThread[f, {{a, b}, {c, d}}, {1}].", + ), + "MapThread[f, {{a, b}, {c, d}}, {1}]", + None, + ), + ( + "MapThread[f, {{a, b}, {c, d}}, 2]", + ( + "Object {a, b} at position {2, 1} in MapThread[f, {{a, b}, {c, d}}, 2] has only 1 of required 2 dimensions.", + ), + "MapThread[f, {{a, b}, {c, d}}, 2]", + None, + ), + ( + "MapThread[f, {{a}, {b, c}}]", + ( + "Incompatible dimensions of objects at positions {2, 1} and {2, 2} of MapThread[f, {{a}, {b, c}}]; dimensions are 1 and 2.", + ), + "MapThread[f, {{a}, {b, c}}]", + None, + ), + ("MapThread[f, {}]", None, "{}", None), + ("MapThread[f, {a, b}, 0]", None, "f[a, b]", None), + ( + "MapThread[f, {a, b}, 1]", + ( + "Object a at position {2, 1} in MapThread[f, {a, b}, 1] has only 0 of required 1 dimensions.", + ), + "MapThread[f, {a, b}, 1]", + None, + ), + ( + "MapThread[f, {{{a, b}, {c}}, {{d, e}, {f}}}, 2]", + None, + "{{f[a, d], f[b, e]}, {f[c, f]}}", + "Behaviour extends MMA", + ), + ( + "Scan[Print, f[g[h[x]]], 2]", + ( + "h[x]", + "g[h[x]]", + ), + None, + None, + ), + ( + "Scan[Print][{1, 2}]", + ( + "1", + "2", + ), + None, + None, + ), + ("Scan[Return, {1, 2}]", None, "1", None), + ], +) +def test_private_doctests_apply_fns_to_lists(str_expr, msgs, str_expected, fail_msg): + """functional.apply_fns_to_lists""" + + def eval_expr(expr_str): + query = session.evaluation.parse(expr_str) + res = session.evaluation.evaluate(query) + session.evaluation.stopped = False + return res + + res = eval_expr(str_expr) + if msgs is None: + assert len(res.out) == 0 + else: + assert len(res.out) == len(msgs) + for li1, li2 in zip(res.out, msgs): + assert li1.text == li2 + + assert res.result == str_expected + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ("g[x_,y_] := x+y;g[Sequence@@Slot/@Range[2]]&[1,2]", None, "#1 + #2", None), + ("Evaluate[g[Sequence@@Slot/@Range[2]]]&[1,2]", None, "3", None), + ("# // InputForm", None, "#1", None), + ("#0 // InputForm", None, "#0", None), + ("## // InputForm", None, "##1", None), + ("Clear[g];", None, "Null", None), + ], +) +def test_private_doctests_application(str_expr, msgs, str_expected, fail_msg): + """functional.application""" + check_evaluation( + str_expr, + str_expected, + to_string_expr=True, + to_string_expected=True, + hold_expected=True, + failure_message=fail_msg, + expected_messages=msgs, + ) + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ("FixedPoint[f, x, 0]", None, "x", None), + ( + "FixedPoint[f, x, -1]", + ("Non-negative integer expected.",), + "FixedPoint[f, x, -1]", + None, + ), + ("FixedPoint[Cos, 1.0, Infinity]", None, "0.739085", None), + ("FixedPointList[f, x, 0]", None, "{x}", None), + ( + "FixedPointList[f, x, -1]", + ("Non-negative integer expected.",), + "FixedPointList[f, x, -1]", + None, + ), + ("Last[FixedPointList[Cos, 1.0, Infinity]]", None, "0.739085", None), + ], +) +def test_private_doctests_functional_iteration(str_expr, msgs, str_expected, fail_msg): + """functional.functional_iteration""" + check_evaluation( + str_expr, + str_expected, + to_string_expr=True, + to_string_expected=True, + hold_expected=True, + failure_message=fail_msg, + expected_messages=msgs, + )