From 08c6e9c142e619ac5175b6a13342ba2f2c571ddd Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 10 Nov 2022 11:54:21 -0800 Subject: TST: Skip tests that are not currently supported in wasm --- numpy/f2py/tests/test_abstract_interface.py | 3 +++ numpy/f2py/tests/util.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_abstract_interface.py b/numpy/f2py/tests/test_abstract_interface.py index 29e4b0647..42902913e 100644 --- a/numpy/f2py/tests/test_abstract_interface.py +++ b/numpy/f2py/tests/test_abstract_interface.py @@ -1,9 +1,12 @@ from pathlib import Path +import pytest import textwrap from . import util from numpy.f2py import crackfortran +from numpy.testing import IS_WASM +@pytest.mark.skipif(IS_WASM, reason="Cannot start subprocess") class TestAbstractInterface(util.F2PyTest): sources = [util.getpath("tests", "src", "abstract_interface", "foo.f90")] diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index ad8c7a37e..1534c4e7d 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -20,7 +20,7 @@ import numpy from pathlib import Path from numpy.compat import asbytes, asstr -from numpy.testing import temppath +from numpy.testing import temppath, IS_WASM from importlib import import_module # @@ -187,6 +187,9 @@ def _get_compiler_status(): return _compiler_status _compiler_status = (False, False, False) + if IS_WASM: + # Can't run compiler from inside WASM. + return _compiler_status # XXX: this is really ugly. But I don't know how to invoke Distutils # in a safer way... -- cgit v1.2.1 From fe73a8498417d762a2102d759fa4c88613b398ef Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Mon, 26 Dec 2022 04:50:55 +0530 Subject: BUG: Use whole file for encoding checks with `charset_normalizer` [f2py] (#22872) * BUG: Use whole file for encoding checks [f2py] * DOC: Add a code comment Co-authored-by: melissawm * TST: Add a conditional unicode f2py test * MAINT: Add chardet as a test requirement * ENH: Cleanup and switch f2py to charset_normalizer * MAINT: Remove chardet for charset_normalizer * TST: Simplify UTF-8 encoding [f2py] Co-authored-by: melissawm --- numpy/f2py/tests/src/crackfortran/unicode_comment.f90 | 4 ++++ numpy/f2py/tests/test_crackfortran.py | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 numpy/f2py/tests/src/crackfortran/unicode_comment.f90 (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/src/crackfortran/unicode_comment.f90 b/numpy/f2py/tests/src/crackfortran/unicode_comment.f90 new file mode 100644 index 000000000..13515ce98 --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/unicode_comment.f90 @@ -0,0 +1,4 @@ +subroutine foo(x) + real(8), intent(in) :: x + ! Écrit à l'écran la valeur de x +end subroutine diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index dcf8760db..73ac4e276 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -1,4 +1,6 @@ +import importlib import codecs +import unicodedata import pytest import numpy as np from numpy.f2py.crackfortran import markinnerspaces @@ -257,13 +259,20 @@ class TestFortranReader(util.F2PyTest): def test_input_encoding(self, tmp_path, encoding): # gh-635 f_path = tmp_path / f"input_with_{encoding}_encoding.f90" - # explicit BOM is required for UTF8 - bom = {'utf-8': codecs.BOM_UTF8}.get(encoding, b'') with f_path.open('w', encoding=encoding) as ff: - ff.write(bom.decode(encoding) + - """ + ff.write(""" subroutine foo() end subroutine foo """) mod = crackfortran.crackfortran([str(f_path)]) assert mod[0]['name'] == 'foo' + +class TestUnicodeComment(util.F2PyTest): + sources = [util.getpath("tests", "src", "crackfortran", "unicode_comment.f90")] + + @pytest.mark.skipif( + (importlib.util.find_spec("charset_normalizer") is None), + reason="test requires charset_normalizer which is not installed", + ) + def test_encoding_comment(self): + self.module.foo(3) -- cgit v1.2.1 From 608864613b801b9c85573186a9d07eeac5e7e465 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Thu, 26 Jan 2023 14:29:13 -0500 Subject: TST: Rebase F2Py test modules on Cygwin. Let's see if this fixes the 8-50 fork failures. --- numpy/f2py/tests/util.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 1534c4e7d..0f558c6dd 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -30,6 +30,9 @@ from importlib import import_module _module_dir = None _module_num = 5403 +if sys.platform == "cygwin": + _module_list = [] + def _cleanup(): global _module_dir @@ -147,6 +150,19 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None): for fn in dst_sources: os.unlink(fn) + # Rebase + if sys.platform == "cygwin": + # If someone starts deleting modules after import, this will + # need to change to record how big each module is, rather than + # relying on rebase being able to find that from the files. + _module_list.extend( + glob.glob(os.path.join(d, "{:s}*".format(module_name))) + ) + subprocess.check_call( + ["/usr/bin/rebase", "--database", "--oblivious", "--verbose"] + + _module_list + ) + # Import return import_module(module_name) -- cgit v1.2.1 From 33709afdbbc47b7adb7dd06a730246d8c02f724f Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 27 Jan 2023 08:40:01 -0500 Subject: FIX: Add glob import for test module rebase. Forgot to check this earlier. --- numpy/f2py/tests/util.py | 1 + 1 file changed, 1 insertion(+) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 0f558c6dd..032c50bfd 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -14,6 +14,7 @@ import shutil import atexit import textwrap import re +import glob import pytest import contextlib import numpy -- cgit v1.2.1 From 015ecf60a0402074c37821f8019f10fce78143ba Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 27 Jan 2023 21:26:16 -0500 Subject: Revert "TST: Rebase F2Py test modules on Cygwin." This reverts commit 608864613b801b9c85573186a9d07eeac5e7e465. --- numpy/f2py/tests/util.py | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 032c50bfd..94029d51d 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -31,9 +31,6 @@ from importlib import import_module _module_dir = None _module_num = 5403 -if sys.platform == "cygwin": - _module_list = [] - def _cleanup(): global _module_dir @@ -151,19 +148,6 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None): for fn in dst_sources: os.unlink(fn) - # Rebase - if sys.platform == "cygwin": - # If someone starts deleting modules after import, this will - # need to change to record how big each module is, rather than - # relying on rebase being able to find that from the files. - _module_list.extend( - glob.glob(os.path.join(d, "{:s}*".format(module_name))) - ) - subprocess.check_call( - ["/usr/bin/rebase", "--database", "--oblivious", "--verbose"] - + _module_list - ) - # Import return import_module(module_name) -- cgit v1.2.1 From dbeaf074e310e9a3657cac52fc3b5d90598a435b Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 27 Jan 2023 21:28:55 -0500 Subject: Revert "FIX: Add glob import for test module rebase." This reverts commit 33709afdbbc47b7adb7dd06a730246d8c02f724f. --- numpy/f2py/tests/util.py | 1 - 1 file changed, 1 deletion(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 94029d51d..1534c4e7d 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -14,7 +14,6 @@ import shutil import atexit import textwrap import re -import glob import pytest import contextlib import numpy -- cgit v1.2.1 From 2fa4441529747b2ff9c712f5c102267888da1f80 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sun, 29 Jan 2023 11:50:51 -0500 Subject: TST: Rebase F2Py-built extension modules. Also adjust CI so they don't immediately collide with NumPy. I forgot to do that last time, which caused problems. --- numpy/f2py/tests/util.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 1534c4e7d..f4d19dd1c 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -6,6 +6,7 @@ Utility functions for - determining paths to tests """ +import glob import os import sys import subprocess @@ -30,6 +31,9 @@ from importlib import import_module _module_dir = None _module_num = 5403 +if sys.platform == "cygwin": + _module_list = [] + def _cleanup(): global _module_dir @@ -147,6 +151,21 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None): for fn in dst_sources: os.unlink(fn) + # Rebase (Cygwin-only) + if sys.platform == "cygwin": + # If someone starts deleting modules after import, this will + # need to change to record how big each module is, rather than + # relying on rebase being able to find that from the files. + _module_list.extend( + glob.glob(os.path.join(d, "{:s}*".format(module_name))) + ) + subprocess.check_call( + ["/usr/bin/rebase", "--database", "--oblivious", "--verbose"] + + _module_list + ) + + + # Import return import_module(module_name) -- cgit v1.2.1 From 2293a623b9deebdd284336e223ac175a7baa77b0 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 30 Jan 2023 08:24:02 -0500 Subject: CI: Rebase numpy DLLs in runtests.py. This assumes NumPy is rebased before tests run, but does not assume the locations are in the database. --- numpy/f2py/tests/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index f4d19dd1c..26fa7e49d 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -32,7 +32,8 @@ _module_dir = None _module_num = 5403 if sys.platform == "cygwin": - _module_list = [] + NUMPY_INSTALL_ROOT = Path(__file__).parent.parent.parent + _module_list = list(NUMPY_INSTALL_ROOT.glob("**/*.dll")) def _cleanup(): -- cgit v1.2.1 From c7661e8ee47f8c94bb5ba060bb4af49468dfda40 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Thu, 9 Feb 2023 11:30:54 +0100 Subject: TST: Comment out spurious print in f2py test Matti was wondering where it came from, so lets comment it out. --- numpy/f2py/tests/test_character.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_character.py b/numpy/f2py/tests/test_character.py index b54b4d981..528e78fc6 100644 --- a/numpy/f2py/tests/test_character.py +++ b/numpy/f2py/tests/test_character.py @@ -457,9 +457,10 @@ class TestMiscCharacter(util.F2PyTest): character(len=*), intent(in) :: x(:) !f2py intent(out) x integer :: i - do i=1, size(x) - print*, "x(",i,")=", x(i) - end do + ! Uncomment for debug printing: + !do i=1, size(x) + ! print*, "x(",i,")=", x(i) + !end do end subroutine {fprefix}_gh4519 pure function {fprefix}_gh3425(x) result (y) -- cgit v1.2.1 From 8daec0ecf11f9d2633d133f2d2b7d6c39f157f4b Mon Sep 17 00:00:00 2001 From: Alexander Heger <2sn@users.noreply.github.com> Date: Sun, 12 Feb 2023 00:26:42 +1100 Subject: BUG: fix for f2py string scalars (#23194) in previous version, any string scalar was converted to a string array of dimension len, i.e., a definition character(len=N) :: X effectively became character(len=NNN), dimension(NNN) :: X from the point of few of the numpy (python) interface: X.shape == (NNN,) X.dtype == '|SNNN' Closes gh-23192 --- numpy/f2py/tests/src/string/scalar_string.f90 | 7 +++++++ numpy/f2py/tests/test_character.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 numpy/f2py/tests/src/string/scalar_string.f90 (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/src/string/scalar_string.f90 b/numpy/f2py/tests/src/string/scalar_string.f90 new file mode 100644 index 000000000..d847668bb --- /dev/null +++ b/numpy/f2py/tests/src/string/scalar_string.f90 @@ -0,0 +1,7 @@ +MODULE string_test + + character(len=8) :: string + + character(len=12), dimension(5,7) :: strarr + +END MODULE string_test diff --git a/numpy/f2py/tests/test_character.py b/numpy/f2py/tests/test_character.py index 528e78fc6..5f9805158 100644 --- a/numpy/f2py/tests/test_character.py +++ b/numpy/f2py/tests/test_character.py @@ -569,3 +569,23 @@ class TestMiscCharacter(util.F2PyTest): assert_equal(len(a), 2) assert_raises(Exception, lambda: f(b'c')) + + +class TestStringScalarArr(util.F2PyTest): + sources = [util.getpath("tests", "src", "string", "scalar_string.f90")] + + @pytest.mark.slow + def test_char(self): + out = self.module.string_test.string + expected = () + assert out.shape == expected + expected = '|S8' + assert out.dtype == expected + + @pytest.mark.slow + def test_char_arr(self): + out = self.module.string_test.strarr + expected = (5,7) + assert out.shape == expected + expected = '|S12' + assert out.dtype == expected -- cgit v1.2.1 From af21ac6a95766168cb2fa18092f89be6c42c1fd4 Mon Sep 17 00:00:00 2001 From: molsonkiko <46202915+molsonkiko@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:01:45 -0700 Subject: add nameargspattern backtracking test --- numpy/f2py/tests/test_crackfortran.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index 73ac4e276..fc3ab561e 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -1,5 +1,6 @@ import importlib import codecs +import time import unicodedata import pytest import numpy as np @@ -276,3 +277,19 @@ class TestUnicodeComment(util.F2PyTest): ) def test_encoding_comment(self): self.module.foo(3) + +class TestNameArgsPatternBacktracking: + def test_nameargspattern_backtracking(): + last_time = 0. + trials_per_count = 32 + start_reps, end_reps = 10, 16 + for ii in range(start_reps, end_reps): + atbindat = '@)@bind@(@' * ii + total_time = 0 + for _ in range(trials_per_count): + t0 = time.perf_counter() + crackfortran.nameargspattern.search(atbindat) + total_time += (time.perf_counter() - t0) + if ii > start_reps: + assert total_time < 1.9 * last_time, f'Going from {ii - 1} to {ii} approximately doubled time' + last_time = total_time \ No newline at end of file -- cgit v1.2.1 From 39c380ccf12a15cf560260dac461649c26bd0e5e Mon Sep 17 00:00:00 2001 From: molsonkiko <46202915+molsonkiko@users.noreply.github.com> Date: Sun, 26 Mar 2023 13:09:14 -0700 Subject: initial fix for nameargspattern regex --- numpy/f2py/tests/test_crackfortran.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index fc3ab561e..b577eaf38 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -279,7 +279,9 @@ class TestUnicodeComment(util.F2PyTest): self.module.foo(3) class TestNameArgsPatternBacktracking: - def test_nameargspattern_backtracking(): + def test_nameargspattern_backtracking(self): + '''address ReDOS vulnerability: + https://github.com/numpy/numpy/issues/23338''' last_time = 0. trials_per_count = 32 start_reps, end_reps = 10, 16 @@ -291,5 +293,10 @@ class TestNameArgsPatternBacktracking: crackfortran.nameargspattern.search(atbindat) total_time += (time.perf_counter() - t0) if ii > start_reps: - assert total_time < 1.9 * last_time, f'Going from {ii - 1} to {ii} approximately doubled time' + # the hallmark of exponentially catastrophic backtracking + # is that runtime doubles for every added instance of + # the problematic pattern. + assert total_time < 1.9 * last_time + # also try to rule out non-exponential but still bad cases + assert total_time < 1 last_time = total_time \ No newline at end of file -- cgit v1.2.1 From fd0f29de9365dcceec26d6a2a7539f61b0b037cf Mon Sep 17 00:00:00 2001 From: molsonkiko <46202915+molsonkiko@users.noreply.github.com> Date: Sun, 26 Mar 2023 14:15:36 -0700 Subject: update test for less arbitrary time requirement --- numpy/f2py/tests/test_crackfortran.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index b577eaf38..67693b019 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -298,5 +298,6 @@ class TestNameArgsPatternBacktracking: # the problematic pattern. assert total_time < 1.9 * last_time # also try to rule out non-exponential but still bad cases - assert total_time < 1 + # arbitrarily, we should set a hard limit of 10ms as too slow + assert total_time < trials_per_count * 0.01 last_time = total_time \ No newline at end of file -- cgit v1.2.1 From 09c23ef73c839d3a7f31e755f40f2f06b9791b7f Mon Sep 17 00:00:00 2001 From: molsonkiko <46202915+molsonkiko@users.noreply.github.com> Date: Sun, 26 Mar 2023 17:45:01 -0700 Subject: make regex still match cases where OG fix failed My first replacement regex would have failed to match cases like '@)@bind foo bar baz@(@@)@' which should apparently be matched. Added a test to make sure the regex does this. --- numpy/f2py/tests/test_crackfortran.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index 67693b019..449251435 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -4,7 +4,7 @@ import time import unicodedata import pytest import numpy as np -from numpy.f2py.crackfortran import markinnerspaces +from numpy.f2py.crackfortran import markinnerspaces, nameargspattern from . import util from numpy.f2py import crackfortran import textwrap @@ -279,19 +279,32 @@ class TestUnicodeComment(util.F2PyTest): self.module.foo(3) class TestNameArgsPatternBacktracking: - def test_nameargspattern_backtracking(self): + @pytest.mark.parametrize( + ['adversary'], + [ + ('@)@bind@(@',), + ('@)@bind @(@',), + ('@)@bind foo bar baz@(@',) + ] + ) + def test_nameargspattern_backtracking(self, adversary): '''address ReDOS vulnerability: https://github.com/numpy/numpy/issues/23338''' last_time = 0. - trials_per_count = 32 - start_reps, end_reps = 10, 16 + trials_per_count = 128 + start_reps, end_reps = 15, 25 for ii in range(start_reps, end_reps): - atbindat = '@)@bind@(@' * ii + repeated_adversary = adversary * ii total_time = 0 for _ in range(trials_per_count): t0 = time.perf_counter() - crackfortran.nameargspattern.search(atbindat) + mtch = nameargspattern.search(repeated_adversary) total_time += (time.perf_counter() - t0) + assert not mtch + # if the adversary is capped with @)@, it becomes acceptable. + # that should still be true. + good_version_of_adversary = repeated_adversary + '@)@' + assert nameargspattern.search(good_version_of_adversary) if ii > start_reps: # the hallmark of exponentially catastrophic backtracking # is that runtime doubles for every added instance of -- cgit v1.2.1 From 988283a7a18ad15f00db28902c643f653dfd7278 Mon Sep 17 00:00:00 2001 From: molsonkiko <46202915+molsonkiko@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:32:40 -0700 Subject: make time tests more resilient to random noise --- numpy/f2py/tests/test_crackfortran.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index 449251435..23965087d 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -290,18 +290,23 @@ class TestNameArgsPatternBacktracking: def test_nameargspattern_backtracking(self, adversary): '''address ReDOS vulnerability: https://github.com/numpy/numpy/issues/23338''' - last_time = 0. + last_median = 0. trials_per_count = 128 start_reps, end_reps = 15, 25 + times_median_doubled = 0 for ii in range(start_reps, end_reps): repeated_adversary = adversary * ii - total_time = 0 + times = [] for _ in range(trials_per_count): t0 = time.perf_counter() mtch = nameargspattern.search(repeated_adversary) - total_time += (time.perf_counter() - t0) + times.append(time.perf_counter() - t0) + # We should use a measure of time that's resilient to outliers. + # Times jump around a lot due to the CPU's scheduler. + median = np.median(times) assert not mtch - # if the adversary is capped with @)@, it becomes acceptable. + # if the adversary is capped with @)@, it becomes acceptable + # according to the old version of the regex. # that should still be true. good_version_of_adversary = repeated_adversary + '@)@' assert nameargspattern.search(good_version_of_adversary) @@ -309,8 +314,12 @@ class TestNameArgsPatternBacktracking: # the hallmark of exponentially catastrophic backtracking # is that runtime doubles for every added instance of # the problematic pattern. - assert total_time < 1.9 * last_time + times_median_doubled += median > 2 * last_median # also try to rule out non-exponential but still bad cases # arbitrarily, we should set a hard limit of 10ms as too slow - assert total_time < trials_per_count * 0.01 - last_time = total_time \ No newline at end of file + assert median < trials_per_count * 0.01 + last_median = median + # we accept that maybe the median might double once, due to + # the CPU scheduler acting weird or whatever. More than that + # seems suspicious. + assert times_median_doubled < 2 \ No newline at end of file -- cgit v1.2.1 From 204927f66d2f71da3f4f676769da5684cf85f427 Mon Sep 17 00:00:00 2001 From: Bob Eldering Date: Wed, 22 Mar 2023 16:48:56 +0100 Subject: BUG: Fix bug in parsing F77 style string arrays. Example problematic variable: CHARACTER WORDARR(3)*8 This would be wrapped by an array with shape (3, 8) and dtype |S1, instead of the desired shape (3,) and dtype |S8. See #23356. --- numpy/f2py/tests/src/string/scalar_string.f90 | 2 ++ numpy/f2py/tests/test_character.py | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/src/string/scalar_string.f90 b/numpy/f2py/tests/src/string/scalar_string.f90 index d847668bb..f8f076172 100644 --- a/numpy/f2py/tests/src/string/scalar_string.f90 +++ b/numpy/f2py/tests/src/string/scalar_string.f90 @@ -1,7 +1,9 @@ MODULE string_test character(len=8) :: string + character string77 * 8 character(len=12), dimension(5,7) :: strarr + character strarr77(5,7) * 12 END MODULE string_test diff --git a/numpy/f2py/tests/test_character.py b/numpy/f2py/tests/test_character.py index 5f9805158..0bb0f4290 100644 --- a/numpy/f2py/tests/test_character.py +++ b/numpy/f2py/tests/test_character.py @@ -576,16 +576,18 @@ class TestStringScalarArr(util.F2PyTest): @pytest.mark.slow def test_char(self): - out = self.module.string_test.string - expected = () - assert out.shape == expected - expected = '|S8' - assert out.dtype == expected + for out in (self.module.string_test.string, + self.module.string_test.string77): + expected = () + assert out.shape == expected + expected = '|S8' + assert out.dtype == expected @pytest.mark.slow def test_char_arr(self): - out = self.module.string_test.strarr - expected = (5,7) - assert out.shape == expected - expected = '|S12' - assert out.dtype == expected + for out in (self.module.string_test.strarr, + self.module.string_test.strarr77): + expected = (5,7) + assert out.shape == expected + expected = '|S12' + assert out.dtype == expected -- cgit v1.2.1 From 7aa6d6dabe0b4b3e5bf378ec684778b46167cce4 Mon Sep 17 00:00:00 2001 From: Derek Homeier Date: Fri, 17 Jun 2022 03:15:06 +0200 Subject: BUG: include macOS arm64 `machine()` value in `_selected_real_kind_func` --- numpy/f2py/tests/test_kind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_kind.py b/numpy/f2py/tests/test_kind.py index f0cb61fb6..5411ab45e 100644 --- a/numpy/f2py/tests/test_kind.py +++ b/numpy/f2py/tests/test_kind.py @@ -20,7 +20,7 @@ class TestKind(util.F2PyTest): i ), f"selectedintkind({i}): expected {selected_int_kind(i)!r} but got {selectedintkind(i)!r}" - for i in range(20): + for i in range(40): assert selectedrealkind(i) == selected_real_kind( i ), f"selectedrealkind({i}): expected {selected_real_kind(i)!r} but got {selectedrealkind(i)!r}" -- cgit v1.2.1 From 0bee070d11ad4d14402f03f85650dbf5391735ca Mon Sep 17 00:00:00 2001 From: Derek Homeier Date: Fri, 17 Jun 2022 18:39:44 +0200 Subject: DNM: test some more `selected_real_kind` results [skip azurepipelines] --- numpy/f2py/tests/test_kind.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_kind.py b/numpy/f2py/tests/test_kind.py index 5411ab45e..69b85aaad 100644 --- a/numpy/f2py/tests/test_kind.py +++ b/numpy/f2py/tests/test_kind.py @@ -1,5 +1,6 @@ import os import pytest +import platform from numpy.f2py.crackfortran import ( _selected_int_kind_func as selected_int_kind, @@ -11,8 +12,8 @@ from . import util class TestKind(util.F2PyTest): sources = [util.getpath("tests", "src", "kind", "foo.f90")] - def test_all(self): - selectedrealkind = self.module.selectedrealkind + def test_int(self): + """Test `int` kind_func for integers up to 10**40.""" selectedintkind = self.module.selectedintkind for i in range(40): @@ -20,7 +21,27 @@ class TestKind(util.F2PyTest): i ), f"selectedintkind({i}): expected {selected_int_kind(i)!r} but got {selectedintkind(i)!r}" - for i in range(40): + def test_real(self): + """ + Test (processor-dependent) `real` kind_func for real numbers + of up to 31 digits precision (extended/quadruple). + """ + selectedrealkind = self.module.selectedrealkind + + for i in range(32): + assert selectedrealkind(i) == selected_real_kind( + i + ), f"selectedrealkind({i}): expected {selected_real_kind(i)!r} but got {selectedrealkind(i)!r}" + + @pytest.mark.xfail(platform.machine().lower().startswith("ppc"), + reason="Some PowerPC may not support full IEEE 754 precision") + def test_quad_precision(self): + """ + Test kind_func for quadruple precision [`real(16)`] of 32+ digits . + """ + selectedrealkind = self.module.selectedrealkind + + for i in range(32, 40): assert selectedrealkind(i) == selected_real_kind( i ), f"selectedrealkind({i}): expected {selected_real_kind(i)!r} but got {selectedrealkind(i)!r}" -- cgit v1.2.1 From f96b8daea959db191ff84b80dbc1ea948722fbaa Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 16 Apr 2023 18:18:02 +0000 Subject: TST: Add a test for gh-23598 --- numpy/f2py/tests/src/crackfortran/gh23598.f90 | 4 ++++ numpy/f2py/tests/test_crackfortran.py | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 numpy/f2py/tests/src/crackfortran/gh23598.f90 (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/src/crackfortran/gh23598.f90 b/numpy/f2py/tests/src/crackfortran/gh23598.f90 new file mode 100644 index 000000000..e0dffb5ef --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/gh23598.f90 @@ -0,0 +1,4 @@ +integer function intproduct(a, b) result(res) + integer, intent(in) :: a, b + res = a*b +end function diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index 23965087d..886fc596e 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -322,4 +322,12 @@ class TestNameArgsPatternBacktracking: # we accept that maybe the median might double once, due to # the CPU scheduler acting weird or whatever. More than that # seems suspicious. - assert times_median_doubled < 2 \ No newline at end of file + assert times_median_doubled < 2 + + +class TestFunctionReturn(util.F2PyTest): + sources = [util.getpath("tests", "src", "crackfortran", "gh23598.f90")] + + def test_function_rettype(self): + # gh-23598 + assert self.module.intproduct(3, 4) == 12 -- cgit v1.2.1 From c7ff4118a345c966e2a0fc688e054e3fd9191e99 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Mon, 17 Apr 2023 00:59:45 +0000 Subject: TST: Add a test for the f2py function wrapper file --- numpy/f2py/tests/src/crackfortran/gh23598Warn.f90 | 11 +++++++++++ numpy/f2py/tests/test_f2py2e.py | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 numpy/f2py/tests/src/crackfortran/gh23598Warn.f90 (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90 b/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90 new file mode 100644 index 000000000..3b44efc5e --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90 @@ -0,0 +1,11 @@ +module test_bug + implicit none + private + public :: intproduct + +contains + integer function intproduct(a, b) result(res) + integer, intent(in) :: a, b + res = a*b + end function +end module diff --git a/numpy/f2py/tests/test_f2py2e.py b/numpy/f2py/tests/test_f2py2e.py index 2c10f046f..5f7b56a68 100644 --- a/numpy/f2py/tests/test_f2py2e.py +++ b/numpy/f2py/tests/test_f2py2e.py @@ -62,6 +62,15 @@ def hello_world_f90(tmpdir_factory): return fn +@pytest.fixture(scope="session") +def gh23598_warn(tmpdir_factory): + """F90 file for testing warnings in gh23598""" + fdat = util.getpath("tests", "src", "crackfortran", "gh23598Warn.f90").read_text() + fn = tmpdir_factory.getbasetemp() / "gh23598Warn.f90" + fn.write_text(fdat, encoding="ascii") + return fn + + @pytest.fixture(scope="session") def hello_world_f77(tmpdir_factory): """Generates a single f77 file for testing""" @@ -91,6 +100,19 @@ def f2cmap_f90(tmpdir_factory): return fn +def test_gh23598_warn(capfd, gh23598_warn, monkeypatch): + foutl = get_io_paths(gh23598_warn, mname="test") + ipath = foutl.f90inp + monkeypatch.setattr( + sys, "argv", + f'f2py {ipath} -m test'.split()) + + with util.switchdir(ipath.parent): + f2pycli() # Generate files + wrapper = foutl.wrap90.read_text() + assert "intproductf2pywrap, intpr" not in wrapper + + def test_gen_pyf(capfd, hello_world_f90, monkeypatch): """Ensures that a signature file is generated via the CLI CLI :: -h -- cgit v1.2.1 From 46cc9556224ecba0fddd5f78be74289a5ffd64b4 Mon Sep 17 00:00:00 2001 From: molsonkiko <46202915+molsonkiko@users.noreply.github.com> Date: Tue, 25 Apr 2023 11:46:56 -0700 Subject: TST: Remove crackfortran.nameargspattern time test that failed randomly (#23662) also made the threshold for rejecting a regex as too slow much more lenient. 200ms should be enough time even for a bad CPU on a bad day. a bad regex should fail with near certainty --- numpy/f2py/tests/test_crackfortran.py | 40 +++++++++++++---------------------- 1 file changed, 15 insertions(+), 25 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index 23965087d..39555df05 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -290,36 +290,26 @@ class TestNameArgsPatternBacktracking: def test_nameargspattern_backtracking(self, adversary): '''address ReDOS vulnerability: https://github.com/numpy/numpy/issues/23338''' - last_median = 0. - trials_per_count = 128 + trials_per_batch = 12 + batches_per_regex = 4 start_reps, end_reps = 15, 25 - times_median_doubled = 0 for ii in range(start_reps, end_reps): repeated_adversary = adversary * ii - times = [] - for _ in range(trials_per_count): - t0 = time.perf_counter() - mtch = nameargspattern.search(repeated_adversary) - times.append(time.perf_counter() - t0) - # We should use a measure of time that's resilient to outliers. - # Times jump around a lot due to the CPU's scheduler. - median = np.median(times) + # test times in small batches. + # this gives us more chances to catch a bad regex + # while still catching it before too long if it is bad + for _ in range(batches_per_regex): + times = [] + for _ in range(trials_per_batch): + t0 = time.perf_counter() + mtch = nameargspattern.search(repeated_adversary) + times.append(time.perf_counter() - t0) + # our pattern should be much faster than 0.2s per search + # it's unlikely that a bad regex will pass even on fast CPUs + assert np.median(times) < 0.2 assert not mtch # if the adversary is capped with @)@, it becomes acceptable # according to the old version of the regex. # that should still be true. good_version_of_adversary = repeated_adversary + '@)@' - assert nameargspattern.search(good_version_of_adversary) - if ii > start_reps: - # the hallmark of exponentially catastrophic backtracking - # is that runtime doubles for every added instance of - # the problematic pattern. - times_median_doubled += median > 2 * last_median - # also try to rule out non-exponential but still bad cases - # arbitrarily, we should set a hard limit of 10ms as too slow - assert median < trials_per_count * 0.01 - last_median = median - # we accept that maybe the median might double once, due to - # the CPU scheduler acting weird or whatever. More than that - # seems suspicious. - assert times_median_doubled < 2 \ No newline at end of file + assert nameargspattern.search(good_version_of_adversary) \ No newline at end of file -- cgit v1.2.1 From aa5b9d6665709d5ca8a098c2e4f9ce2f5c8a25b7 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 16 Apr 2023 17:12:53 +0000 Subject: TST: Add a test for gh-23533 --- numpy/f2py/tests/src/crackfortran/gh23533.f | 5 +++++ numpy/f2py/tests/test_crackfortran.py | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 numpy/f2py/tests/src/crackfortran/gh23533.f (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/src/crackfortran/gh23533.f b/numpy/f2py/tests/src/crackfortran/gh23533.f new file mode 100644 index 000000000..db522afa7 --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/gh23533.f @@ -0,0 +1,5 @@ + SUBROUTINE EXAMPLE( ) + IF( .TRUE. ) THEN + CALL DO_SOMETHING() + END IF ! ** .TRUE. ** + END diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index dc0f7e27a..c20e2c2cf 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -135,6 +135,7 @@ class TestMarkinnerspaces: assert markinnerspaces("a 'b c' 'd e'") == "a 'b@_@c' 'd@_@e'" assert markinnerspaces(r'a "b c" "d e"') == r'a "b@_@c" "d@_@e"' + class TestDimSpec(util.F2PyTest): """This test suite tests various expressions that are used as dimension specifications. @@ -244,6 +245,7 @@ class TestModuleDeclaration: assert len(mod) == 1 assert mod[0]["vars"]["abar"]["="] == "bar('abar')" + class TestEval(util.F2PyTest): def test_eval_scalar(self): eval_scalar = crackfortran._eval_scalar @@ -268,6 +270,7 @@ class TestFortranReader(util.F2PyTest): mod = crackfortran.crackfortran([str(f_path)]) assert mod[0]['name'] == 'foo' + class TestUnicodeComment(util.F2PyTest): sources = [util.getpath("tests", "src", "crackfortran", "unicode_comment.f90")] @@ -278,6 +281,7 @@ class TestUnicodeComment(util.F2PyTest): def test_encoding_comment(self): self.module.foo(3) + class TestNameArgsPatternBacktracking: @pytest.mark.parametrize( ['adversary'], @@ -313,6 +317,19 @@ class TestNameArgsPatternBacktracking: # that should still be true. good_version_of_adversary = repeated_adversary + '@)@' assert nameargspattern.search(good_version_of_adversary) + if ii > start_reps: + # the hallmark of exponentially catastrophic backtracking + # is that runtime doubles for every added instance of + # the problematic pattern. + times_median_doubled += median > 2 * last_median + # also try to rule out non-exponential but still bad cases + # arbitrarily, we should set a hard limit of 10ms as too slow + assert median < trials_per_count * 0.01 + last_median = median + # we accept that maybe the median might double once, due to + # the CPU scheduler acting weird or whatever. More than that + # seems suspicious. + assert times_median_doubled < 2 class TestFunctionReturn(util.F2PyTest): @@ -321,3 +338,13 @@ class TestFunctionReturn(util.F2PyTest): def test_function_rettype(self): # gh-23598 assert self.module.intproduct(3, 4) == 12 + + +class TestFortranGroupCounters(util.F2PyTest): + def test_end_if_comment(self): + # gh-23533 + fpath = util.getpath("tests", "src", "crackfortran", "gh23533.f") + try: + crackfortran.crackfortran([str(fpath)]) + except Exception as exc: + assert False, f"'crackfortran.crackfortran' raised an exception {exc}" -- cgit v1.2.1 From 60f28c52e508892482941acbe7809f015511baf0 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Fri, 12 May 2023 17:54:05 +0000 Subject: MAINT: Fix merge error --- numpy/f2py/tests/test_crackfortran.py | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'numpy/f2py/tests') diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index c20e2c2cf..49bfc13af 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -317,19 +317,6 @@ class TestNameArgsPatternBacktracking: # that should still be true. good_version_of_adversary = repeated_adversary + '@)@' assert nameargspattern.search(good_version_of_adversary) - if ii > start_reps: - # the hallmark of exponentially catastrophic backtracking - # is that runtime doubles for every added instance of - # the problematic pattern. - times_median_doubled += median > 2 * last_median - # also try to rule out non-exponential but still bad cases - # arbitrarily, we should set a hard limit of 10ms as too slow - assert median < trials_per_count * 0.01 - last_median = median - # we accept that maybe the median might double once, due to - # the CPU scheduler acting weird or whatever. More than that - # seems suspicious. - assert times_median_doubled < 2 class TestFunctionReturn(util.F2PyTest): -- cgit v1.2.1