diff --git a/mathics/builtin/attributes.py b/mathics/builtin/attributes.py index bc5cd6c34..f3c5e6eeb 100644 --- a/mathics/builtin/attributes.py +++ b/mathics/builtin/attributes.py @@ -203,29 +203,6 @@ class Flat(Predefined): 'Flat' is taken into account in pattern matching: >> f[a, b, c] /. f[a, b] -> d = f[d, c] - - #> SetAttributes[{u, v}, Flat] - #> u[x_] := {x} - #> u[] - = u[] - #> u[a] - = {a} - #> u[a, b] - : Iteration limit of 1000 exceeded. - = $Aborted - #> u[a, b, c] - : Iteration limit of 1000 exceeded. - = $Aborted - #> v[x_] := x - #> v[] - = v[] - #> v[a] - = a - #> v[a, b] (* in Mathematica: Iteration limit of 4096 exceeded. *) - = v[a, b] - #> v[a, b, c] (* in Mathematica: Iteration limit of 4096 exceeded. *) - : Iteration limit of 1000 exceeded. - = $Aborted """ summary_text = "attribute for associative symbols" diff --git a/mathics/builtin/compilation.py b/mathics/builtin/compilation.py index 1d03e027f..c25fa7c6b 100644 --- a/mathics/builtin/compilation.py +++ b/mathics/builtin/compilation.py @@ -57,32 +57,12 @@ class Compile(Builtin): = CompiledFunction[{x}, Sin[x], -CompiledCode-] >> cf[1.4] = 0.98545 - #> cf[1/2] - = 0.479426 - #> cf[4] - = -0.756802 - #> cf[x] - : Invalid argument x should be Integer, Real or boolean. - = CompiledFunction[{x}, Sin[x], -CompiledCode-][x] - #> cf = Compile[{{x, _Real}, {x, _Integer}}, Sin[x + y]] - : Duplicate parameter x found in {{x, _Real}, {x, _Integer}}. - = Compile[{{x, _Real}, {x, _Integer}}, Sin[x + y]] - #> cf = Compile[{{x, _Real}, {y, _Integer}}, Sin[x + z]] - = CompiledFunction[{x, y}, Sin[x + z], -PythonizedCode-] - #> cf = Compile[{{x, _Real}, {y, _Integer}}, Sin[x + y]] - = CompiledFunction[{x, y}, Sin[x + y], -CompiledCode-] - #> cf[1, 2] - = 0.14112 - #> cf[x + y] - = CompiledFunction[{x, y}, Sin[x + y], -CompiledCode-][x + y] Compile supports basic flow control: >> cf = Compile[{{x, _Real}, {y, _Integer}}, If[x == 0.0 && y <= 0, 0.0, Sin[x ^ y] + 1 / Min[x, 0.5]] + 0.5] = CompiledFunction[{x, y}, ..., -CompiledCode-] >> cf[3.5, 2] = 2.18888 - #> cf[0, -2] - = 0.5 Loops and variable assignments are supported usinv Python builtin "compile" function: >> Compile[{{a, _Integer}, {b, _Integer}}, While[b != 0, {a, b} = {b, Mod[a, b]}]; a] (* GCD of a, b *) diff --git a/mathics/builtin/datentime.py b/mathics/builtin/datentime.py index af7c8a527..0f31fef91 100644 --- a/mathics/builtin/datentime.py +++ b/mathics/builtin/datentime.py @@ -375,10 +375,6 @@ class AbsoluteTime(_DateFormat): >> AbsoluteTime[{"6-6-91", {"Day", "Month", "YearShort"}}] = 2885155200 - - ## Mathematica Bug - Mathics gets it right - #> AbsoluteTime[1000] - = 1000 """ summary_text = "get absolute time in seconds" @@ -834,10 +830,6 @@ class DateList(_DateFormat): : The interpretation of 1/10/1991 is ambiguous. = {1991, 1, 10, 0, 0, 0.} - #> DateList["7/8/9"] - : The interpretation of 7/8/9 is ambiguous. - = {2009, 7, 8, 0, 0, 0.} - >> DateList[{"31/10/91", {"Day", "Month", "YearShort"}}] = {1991, 10, 31, 0, 0, 0.} @@ -912,22 +904,6 @@ class DateString(_DateFormat): Non-integer values are accepted too: >> DateString[{1991, 6, 6.5}] = Thu 6 Jun 1991 12:00:00 - - ## Check Leading 0 - #> DateString[{1979, 3, 14}, {"DayName", " ", "MonthShort", "-", "YearShort"}] - = Wednesday 3-79 - - #> DateString[{"DayName", " ", "Month", "/", "YearShort"}] - = ... - - ## Assumed separators - #> DateString[{"06/06/1991", {"Month", "Day", "Year"}}] - = Thu 6 Jun 1991 00:00:00 - - ## Specified separators - #> DateString[{"06/06/1991", {"Month", "/", "Day", "/", "Year"}}] - = Thu 6 Jun 1991 00:00:00 - """ attributes = A_READ_PROTECTED | A_PROTECTED diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index d60aed079..cadc86314 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -1450,9 +1450,6 @@ class Text(Inset): >> Graphics[{Text["First", {0, 0}], Text["Second", {1, 1}]}, Axes->True, PlotRange->{{-2, 2}, {-2, 2}}] = -Graphics- - - #> Graphics[{Text[x, {0,0}]}] - = -Graphics- """ summary_text = "arbitrary text or other expressions in 2D or 3D" diff --git a/mathics/builtin/patterns.py b/mathics/builtin/patterns.py index 0642e1347..2c26e0a0e 100644 --- a/mathics/builtin/patterns.py +++ b/mathics/builtin/patterns.py @@ -327,9 +327,6 @@ class ReplaceAll(BinaryOperator): >> ReplaceAll[{a -> 1}][{a, b}] = {1, b} - #> a + b /. x_ + y_ -> {x, y} - = {a, b} - ReplaceAll replaces the shallowest levels first: >> ReplaceAll[x[1], {x[1] -> y, 1 -> 2}] = y @@ -761,9 +758,6 @@ class Alternatives(BinaryOperator, PatternObject): Alternatives can also be used for string expressions >> StringReplace["0123 3210", "1" | "2" -> "X"] = 0XX3 3XX0 - - #> StringReplace["h1d9a f483", DigitCharacter | WhitespaceCharacter -> ""] - = hdaf """ arg_counts = None @@ -829,9 +823,6 @@ class Except(PatternObject): Except can also be used for string expressions: >> StringReplace["Hello world!", Except[LetterCharacter] -> ""] = Helloworld - - #> StringReplace["abc DEF 123!", Except[LetterCharacter, WordCharacter] -> "0"] - = abc DEF 000! """ arg_counts = [1, 2] @@ -1091,15 +1082,6 @@ class Optional(BinaryOperator, PatternObject): >> Default[h, k_] := k >> h[a] /. h[x_, y_.] -> {x, y} = {a, 2} - - #> a:b:c - = a : b : c - #> FullForm[a:b:c] - = Optional[Pattern[a, b], c] - #> (a:b):c - = a : b : c - #> a:(b:c) - = a : (b : c) """ arg_counts = [1, 2] @@ -1235,9 +1217,6 @@ class Blank(_Blank): 'Blank' only matches a single expression: >> MatchQ[f[1, 2], f[_]] = False - - #> StringReplace["hello world!", _ -> "x"] - = xxxxxxxxxxxx """ rules = { @@ -1293,14 +1272,6 @@ class BlankSequence(_Blank): 'Sequence' object: >> f[1, 2, 3] /. f[x__] -> x = Sequence[1, 2, 3] - - #> f[a, b, c, d] /. f[x__, c, y__] -> {{x},{y}} - = {{a, b}, {d}} - #> a + b + c + d /. Plus[x__, c] -> {x} - = {a, b, d} - - #> StringReplace[{"ab", "abc", "abcd"}, "b" ~~ __ -> "x"] - = {ab, ax, ax} """ rules = { @@ -1350,21 +1321,6 @@ class BlankNullSequence(_Blank): empty sequence: >> MatchQ[f[], f[___]] = True - - ## This test hits infinite recursion - ## - ##The value captured by a named 'BlankNullSequence' pattern is a - ##'Sequence' object, which can have no elements: - ##>> f[] /. f[x___] -> x - ## = Sequence[] - - #> ___symbol - = ___symbol - #> ___symbol //FullForm - = BlankNullSequence[symbol] - - #> StringReplace[{"ab", "abc", "abcd"}, "b" ~~ ___ -> "x"] - = {ax, ax, ax} """ rules = { @@ -1414,16 +1370,6 @@ class Repeated(PostfixOperator, PatternObject): = {{}, a, {a, b}, a, {a, a, a, a}} >> f[x, 0, 0, 0] /. f[x, s:0..] -> s = Sequence[0, 0, 0] - - #> 1.. // FullForm - = Repeated[1] - #> 8^^1.. // FullForm (* Mathematica gets this wrong *) - = Repeated[1] - - #> StringReplace["010110110001010", "01".. -> "a"] - = a1a100a0 - #> StringMatchQ[#, "a" ~~ ("b"..) ~~ "a"] &/@ {"aa", "aba", "abba"} - = {False, True, True} """ arg_counts = [1, 2] @@ -1502,14 +1448,6 @@ class RepeatedNull(Repeated): = RepeatedNull[Pattern[a, BlankNullSequence[Integer]]] >> f[x] /. f[x, 0...] -> t = t - - #> 1... // FullForm - = RepeatedNull[1] - #> 8^^1... // FullForm (* Mathematica gets this wrong *) - = RepeatedNull[1] - - #> StringMatchQ[#, "a" ~~ ("b"...) ~~ "a"] &/@ {"aa", "aba", "abba"} - = {True, True, True} """ operator = "..." @@ -1666,26 +1604,6 @@ class OptionsPattern(PatternObject): Options might be given in nested lists: >> f[x, {{{n->4}}}] = x ^ 4 - - #> {opt -> b} /. OptionsPattern[{}] -> t - = t - - #> Clear[f] - #> Options[f] = {Power -> 2}; - #> f[x_, OptionsPattern[f]] := x ^ OptionValue[Power] - #> f[10] - = 100 - #> f[10, Power -> 3] - = 1000 - #> Clear[f] - - #> Options[f] = {Power -> 2}; - #> f[x_, OptionsPattern[]] := x ^ OptionValue[Power] - #> f[10] - = 100 - #> f[10, Power -> 3] - = 1000 - #> Clear[f] """ arg_counts = [0, 1] diff --git a/test/builtin/test_attributes.py b/test/builtin/test_attributes.py index 65ee4020c..f1bdbd073 100644 --- a/test/builtin/test_attributes.py +++ b/test/builtin/test_attributes.py @@ -4,7 +4,7 @@ """ import os -from test.helper import check_evaluation +from test.helper import check_evaluation, session import pytest @@ -226,3 +226,84 @@ def test_Attributes_wrong_args(str_expr, arg_count): f"SetAttributes called with {arg_count} arguments; 2 arguments are expected.", ), ) + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ("CleanAll[u];CleanAll[v];", None, None, None), + ("SetAttributes[{u, v}, Flat];u[x_] := {x};u[]", None, "u[]", None), + ("u[a]", None, "{a}", None), + ("v[x_] := x;v[]", None, "v[]", None), + ("v[a]", None, "a", None), + ( + "v[a, b]", + None, + "v[a, b]", + "in Mathematica: Iteration limit of 4096 exceeded.", + ), + ("CleanAll[u];CleanAll[v];", None, None, None), + ], +) +def test_private_doctests_attributes(str_expr, msgs, str_expected, fail_msg): + """ """ + 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"), + [ + ("CleanAll[u];CleanAll[v];", None, None, None), + ( + "SetAttributes[{u, v}, Flat];u[x_] := {x};u[a, b]", + ("Iteration limit of 1000 exceeded.",), + "$Aborted", + None, + ), + ("u[a, b, c]", ("Iteration limit of 1000 exceeded.",), "$Aborted", None), + ( + "v[x_] := x;v[a,b,c]", + ("Iteration limit of 1000 exceeded.",), + "$Aborted", + "in Mathematica: Iteration limit of 4096 exceeded.", + ), + ("CleanAll[u];CleanAll[v];", None, None, None), + ], +) +def test_private_doctests_attributes_with_exceptions( + str_expr, msgs, str_expected, fail_msg +): + """These tests check the behavior of $RecursionLimit and $IterationLimit""" + + # Here we do not use the session object to check the messages + # produced by the exceptions. If $RecursionLimit / $IterationLimit + # are reached during the evaluation using a MathicsSession object, + # an exception is raised. On the other hand, using the `Evaluation.evaluate` + # method, the exception is handled. + # + # TODO: Maybe it makes sense to clone this exception handling in + # the check_evaluation function. + # + 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 diff --git a/test/builtin/test_compilation.py b/test/builtin/test_compilation.py new file mode 100644 index 000000000..29dfa4825 --- /dev/null +++ b/test/builtin/test_compilation.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +""" +Unit tests from mathics.builtin.compilation. +""" + +import sys +import time +from test.helper import check_evaluation, evaluate + +import pytest + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ( + "cf = Compile[{{x, _Real}}, Sin[x]]", + None, + "CompiledFunction[{x}, Sin[x], -CompiledCode-]", + None, + ), + ("cf[1/2]", None, "0.479426", None), + ("cf[4]", None, "-0.756802", None), + ( + "cf[x]", + ("Invalid argument x should be Integer, Real or boolean.",), + "CompiledFunction[{x}, Sin[x], -CompiledCode-][x]", + None, + ), + ( + "cf = Compile[{{x, _Real}, {x, _Integer}}, Sin[x + y]]", + ("Duplicate parameter x found in {{x, _Real}, {x, _Integer}}.",), + "Compile[{{x, _Real}, {x, _Integer}}, Sin[x + y]]", + None, + ), + ( + "cf = Compile[{{x, _Real}, {y, _Integer}}, Sin[x + z]]", + None, + "CompiledFunction[{x, y}, Sin[x + z], -PythonizedCode-]", + None, + ), + ( + "cf = Compile[{{x, _Real}, {y, _Integer}}, Sin[x + y]]", + None, + "CompiledFunction[{x, y}, Sin[x + y], -CompiledCode-]", + None, + ), + ("cf[1, 2]", None, "0.14112", None), + ( + "cf[x + y]", + None, + "CompiledFunction[{x, y}, Sin[x + y], -CompiledCode-][x + y]", + None, + ), + ( + "cf = Compile[{{x, _Real}, {y, _Integer}}, If[x == 0.0 && y <= 0, 0.0, Sin[x ^ y] + 1 / Min[x, 0.5]] + 0.5];cf[0, -2]", + None, + "0.5", + None, + ), + ("ClearAll[cf];", None, None, None), + ], +) +def test_private_doctests_compilation(str_expr, msgs, str_expected, fail_msg): + """ """ + 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_datentime.py b/test/builtin/test_datentime.py index ac9053f74..0fc894483 100644 --- a/test/builtin/test_datentime.py +++ b/test/builtin/test_datentime.py @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +""" +Unit tests from mathics.builtin.datetime. +""" + import sys import time from test.helper import check_evaluation, evaluate @@ -69,3 +73,52 @@ def test_datestring(): ('DateString["2000-12-1", "Year"]', "2000"), ): check_evaluation(str_expr, str_expected, hold_expected=True) + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ("AbsoluteTime[1000]", None, "1000", "Mathematica Bug - Mathics gets it right"), + ( + 'DateList["7/8/9"]', + ("The interpretation of 7/8/9 is ambiguous.",), + "{2009, 7, 8, 0, 0, 0.}", + None, + ), + ( + 'DateString[{1979, 3, 14}, {"DayName", " ", "MonthShort", "-", "YearShort"}]', + None, + "Wednesday 3-79", + "Check Leading 0", + ), + ( + 'DateString[{"DayName", " ", "Month", "/", "YearShort"}]==DateString[Now[[1]], {"DayName", " ", "Month", "/", "YearShort"}]', + None, + "True", + None, + ), + ( + 'DateString[{"06/06/1991", {"Month", "Day", "Year"}}]', + None, + "Thu 6 Jun 1991 00:00:00", + "Assumed separators", + ), + ( + 'DateString[{"06/06/1991", {"Month", "/", "Day", "/", "Year"}}]', + None, + "Thu 6 Jun 1991 00:00:00", + "Specified separators", + ), + ], +) +def test_private_doctests_datetime(str_expr, msgs, str_expected, fail_msg): + """ """ + 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_patterns.py b/test/builtin/test_patterns.py index 8e9edcb16..3bdd932e0 100644 --- a/test/builtin/test_patterns.py +++ b/test/builtin/test_patterns.py @@ -5,6 +5,8 @@ from test.helper import check_evaluation +import pytest + # Clear all the variables @@ -30,3 +32,104 @@ def test_replace_all(): ), ): check_evaluation(str_expr, str_expected, message) + + +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ("a + b /. x_ + y_ -> {x, y}", None, "{a, b}", None), + ( + 'StringReplace["h1d9a f483", DigitCharacter | WhitespaceCharacter -> ""]', + None, + "hdaf", + None, + ), + ( + 'StringReplace["abc DEF 123!", Except[LetterCharacter, WordCharacter] -> "0"]', + None, + "abc DEF 000!", + None, + ), + ("a:b:c", None, "a : b : c", None), + ("FullForm[a:b:c]", None, "Optional[Pattern[a, b], c]", None), + ("(a:b):c", None, "a : b : c", None), + ("a:(b:c)", None, "a : (b : c)", None), + ('StringReplace["hello world!", _ -> "x"]', None, "xxxxxxxxxxxx", None), + ("f[a, b, c, d] /. f[x__, c, y__] -> {{x},{y}}", None, "{{a, b}, {d}}", None), + ("a + b + c + d /. Plus[x__, c] -> {x}", None, "{a, b, d}", None), + ( + 'StringReplace[{"ab", "abc", "abcd"}, "b" ~~ __ -> "x"]', + None, + "{ab, ax, ax}", + None, + ), + ## This test hits infinite recursion + ## + ##The value captured by a named 'BlankNullSequence' pattern is a + ##'Sequence' object, which can have no elements: + ## ('f[] /. f[x___] -> x', None, + ## 'Sequence[]', None), + ("___symbol", None, "___symbol", None), + ("___symbol //FullForm", None, "BlankNullSequence[symbol]", None), + ( + 'StringReplace[{"ab", "abc", "abcd"}, "b" ~~ ___ -> "x"]', + None, + "{ax, ax, ax}", + None, + ), + ("1.. // FullForm", None, "Repeated[1]", None), + ( + "8^^1.. // FullForm (* Mathematica gets this wrong *)", + None, + "Repeated[1]", + None, + ), + ('StringReplace["010110110001010", "01".. -> "a"]', None, "a1a100a0", None), + ( + 'StringMatchQ[#, "a" ~~ ("b"..) ~~ "a"] &/@ {"aa", "aba", "abba"}', + None, + "{False, True, True}", + None, + ), + ("1... // FullForm", None, "RepeatedNull[1]", None), + ( + "8^^1... // FullForm (* Mathematica gets this wrong *)", + None, + "RepeatedNull[1]", + None, + ), + ( + 'StringMatchQ[#, "a" ~~ ("b"...) ~~ "a"] &/@ {"aa", "aba", "abba"}', + None, + "{True, True, True}", + None, + ), + ("{opt -> b} /. OptionsPattern[{}] -> t", None, "t", None), + ("Clear[f]", None, None, None), + ( + "Options[f] = {Power -> 2}; f[x_, OptionsPattern[f]] := x ^ OptionValue[Power];", + None, + None, + None, + ), + ("f[10]", None, "100", None), + ("f[10, Power -> 3]", None, "1000", None), + ("Clear[f]", None, None, None), + ("Options[f] = {Power -> 2};", None, None, None), + ("f[x_, OptionsPattern[]] := x ^ OptionValue[Power];", None, None, None), + ("f[10]", None, "100", None), + ("f[10, Power -> 3]", None, "1000", None), + ("Clear[f]", None, None, None), + ], +) +def test_private_doctests_pattern(str_expr, msgs, str_expected, fail_msg): + """ """ + check_evaluation( + str_expr, + str_expected, + to_string_expr=True, + to_string_expected=True, + hold_expected=True, + failure_message=fail_msg, + expected_messages=msgs, + )