From de554a9f38c4312a188bfb96c062a0c64d26bb71 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 12:30:12 +0800 Subject: [PATCH 01/13] add place holder --- tests/util/test_testing.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/util/test_testing.py diff --git a/tests/util/test_testing.py b/tests/util/test_testing.py new file mode 100644 index 00000000000..43af665a2e3 --- /dev/null +++ b/tests/util/test_testing.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from pymatgen.util.testing import PymatgenTest + + +class TestPymatgenTestTestCase(PymatgenTest): + """Baseline inspector for migration side effects. + + This is not a functional test but a utility to verify behaviors specific to + unittest.TestCase. It ensures we're aware the side effects from the migration. + """ From e2b4c06623e1eeab8e777f1a5892575c7232165c Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 14:08:13 +0800 Subject: [PATCH 02/13] add migration warning and guide --- src/pymatgen/util/testing/__init__.py | 12 +++++ tests/util/test_testing.py | 11 ---- tests/util/test_testing_migrate.py | 72 +++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 11 deletions(-) delete mode 100644 tests/util/test_testing.py create mode 100644 tests/util/test_testing_migrate.py diff --git a/src/pymatgen/util/testing/__init__.py b/src/pymatgen/util/testing/__init__.py index acf83e32c93..9fc69b03004 100644 --- a/src/pymatgen/util/testing/__init__.py +++ b/src/pymatgen/util/testing/__init__.py @@ -10,6 +10,7 @@ import json import pickle # use pickle, not cPickle so that we get the traceback in case of errors import string +import warnings from pathlib import Path from typing import TYPE_CHECKING from unittest import TestCase @@ -41,6 +42,17 @@ class PymatgenTest(TestCase): # dict of lazily-loaded test structures (initialized to None) TEST_STRUCTURES: ClassVar[dict[str | Path, Structure | None]] = dict.fromkeys(STRUCTURES_DIR.glob("*")) + @classmethod + def setUpClass(cls): + """Issue a FutureWarning, see PR 4209.""" + warnings.warn( + "PymatgenTest is scheduled for migration to pytest after 2026-01-01. " + "Please adapt your tests accordingly.", + FutureWarning, + stacklevel=2, + ) + super().setUpClass() + @pytest.fixture(autouse=True) # make all tests run a in a temporary directory accessible via self.tmp_path def _tmp_dir(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: # https://pytest.org/en/latest/how-to/unittest.html#using-autouse-fixtures-and-accessing-other-fixtures diff --git a/tests/util/test_testing.py b/tests/util/test_testing.py deleted file mode 100644 index 43af665a2e3..00000000000 --- a/tests/util/test_testing.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import annotations - -from pymatgen.util.testing import PymatgenTest - - -class TestPymatgenTestTestCase(PymatgenTest): - """Baseline inspector for migration side effects. - - This is not a functional test but a utility to verify behaviors specific to - unittest.TestCase. It ensures we're aware the side effects from the migration. - """ diff --git a/tests/util/test_testing_migrate.py b/tests/util/test_testing_migrate.py new file mode 100644 index 00000000000..d7ce59774f3 --- /dev/null +++ b/tests/util/test_testing_migrate.py @@ -0,0 +1,72 @@ +"""This is not a functional test but a utility to verify behaviors specific to +`unittest.TestCase`. It ensures we're aware the side effects from the migration. + +TODO: remove this test module after migration (2026-01-01), see PR 4209. + +`unittest.TestCase`-specific features and brief migration guide: +- Setup/teardown methods (`setUp`, `setUpClass`, `tearDown`, `tearDownClass`): + 1. Recommended approach in pytest: Use fixtures. + Documentation: https://docs.pytest.org/en/stable/reference/fixtures.html#fixture + OR + 2. Use pytest's xUnit-style setup/teardown functions: + `[setup/teardown]_[class/method/function]`. + Documentation: https://docs.pytest.org/en/stable/how-to/xunit_setup.html + +- Assertion methods (`assertTrue`, `assertFalse`, `assertEqual`, etc.): + Replace with direct Python `assert` statements. +""" + +from __future__ import annotations + +import pytest + +from pymatgen.util.testing import PymatgenTest + + +@pytest.mark.filterwarnings("ignore:PymatgenTest is scheduled for migration to pytest") +class TestPymatgenTestTestCase(PymatgenTest): + """Baseline inspector for migration side effects.""" + + def test_pmg_test_migration_warning(self): + """Test PymatgenTest migration warning.""" + with pytest.warns(FutureWarning, match="PymatgenTest is scheduled for migration to pytest after 2026-01-01"): + self.setUpClass() # invoke the setup phase + + def test_unittest_testcase_specific_funcs(self): + """Make sure TestCase-specific methods are available until migration.""" + # Testing setUp and tearDown methods + self.setUp() + self.tearDown() + + # Testing class-level setUp and tearDown methods + self.setUpClass() + self.tearDownClass() + + # Test the assertion methods + self.assertTrue(True) # noqa: PT009, FBT003 + self.assertFalse(False) # noqa: PT009, FBT003 + self.assertEqual(1, 1) # noqa: PT009 + + +class TestPymatgenTestPytest: + def test_unittest_testcase_specific_funcs(self): + """Test unittest.TestCase-specific methods for migration to pytest.""" + # Testing setUp and tearDown methods + with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): + self.setUp() + with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): + self.tearDown() + + # Testing class-level setUp and tearDown methods + with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): + self.setUpClass() + with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): + self.tearDownClass() + + # Test the assertion methods + with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): + self.assertTrue(True) # noqa: PT009, FBT003 + with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): + self.assertFalse(False) # noqa: PT009, FBT003 + with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): + self.assertEqual(1, 1) # noqa: PT009 From 255876ad2faef9cddca32334d55977d4dc42e51f Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 14:45:22 +0800 Subject: [PATCH 03/13] clean up assert no warning in test --- tests/core/test_units.py | 4 ++-- tests/io/vasp/test_inputs.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/core/test_units.py b/tests/core/test_units.py index 88e06d92517..b94197b5645 100644 --- a/tests/core/test_units.py +++ b/tests/core/test_units.py @@ -110,14 +110,14 @@ def test_memory(self): assert mega_0 == mega_1 == mega_2 == mega_3 def test_deprecated_memory(self): - # TODO: remove after 2025-01-01 + """TODO: remove entire test method after 2025-01-01""" for unit in ("Kb", "kb", "Mb", "mb", "Gb", "gb", "Tb", "tb"): with pytest.warns(DeprecationWarning, match=f"Unit {unit} is deprecated"): Memory(1, unit) with warnings.catch_warnings(): - warnings.simplefilter("error") for unit in ("KB", "MB", "GB", "TB"): + warnings.filterwarnings("error", message=f"Unit {unit} is deprecated", category=DeprecationWarning) Memory(1, unit) def test_unitized(self): diff --git a/tests/io/vasp/test_inputs.py b/tests/io/vasp/test_inputs.py index 1616ff4e5e7..bd4d4eeec7d 100644 --- a/tests/io/vasp/test_inputs.py +++ b/tests/io/vasp/test_inputs.py @@ -208,9 +208,8 @@ def test_from_file_potcar_overwrite_elements(self): copyfile(f"{VASP_IN_DIR}/POSCAR_C2", tmp_poscar_path := f"{self.tmp_path}/POSCAR") copyfile(f"{VASP_IN_DIR}/POTCAR_C2.gz", f"{self.tmp_path}/POTCAR.gz") - with warnings.catch_warnings(record=True) as record: - _poscar = Poscar.from_file(tmp_poscar_path) - assert not any("Elements in POSCAR would be overwritten" in str(warning.message) for warning in record) + warnings.filterwarnings("error", message="Elements in POSCAR would be overwritten") + _poscar = Poscar.from_file(tmp_poscar_path) def test_from_str_default_names(self): """Similar to test_from_file_bad_potcar, ensure "default_names" @@ -237,18 +236,19 @@ def test_from_str_default_names(self): assert poscar.site_symbols == ["Si", "O"] # Assert no warning if using the same elements (or superset) - with warnings.catch_warnings(record=True) as record: + with warnings.catch_warnings(): + warnings.filterwarnings("error", message="Elements in POSCAR would be overwritten") + poscar = Poscar.from_str(poscar_str, default_names=["Si", "F"]) assert poscar.site_symbols == ["Si", "F"] poscar = Poscar.from_str(poscar_str, default_names=["Si", "F", "O"]) assert poscar.site_symbols == ["Si", "F"] - assert not any("Elements in POSCAR would be overwritten" in str(warning.message) for warning in record) # Make sure it could be bypassed (by using None, when not check_for_potcar) - with warnings.catch_warnings(record=True) as record: + with warnings.catch_warnings(): + warnings.filterwarnings("error", message="Elements in POSCAR would be overwritten") _poscar = Poscar.from_str(poscar_str, default_names=None) - assert not any("Elements in POSCAR would be overwritten" in str(warning.message) for warning in record) def test_from_str_default_names_vasp4(self): """Poscar.from_str with default_names given could also be used to From 166a50f82b47561f6f966c0686c822f839095a20 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 15:24:54 +0800 Subject: [PATCH 04/13] clean up warning filters --- src/pymatgen/analysis/elasticity/elastic.py | 3 +-- src/pymatgen/entries/correction_calculator.py | 2 +- src/pymatgen/transformations/advanced_transformations.py | 2 +- tests/command_line/test_bader_caller.py | 4 ---- tests/core/test_units.py | 2 +- 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/pymatgen/analysis/elasticity/elastic.py b/src/pymatgen/analysis/elasticity/elastic.py index 4a236e24d3f..d67d6d87c22 100644 --- a/src/pymatgen/analysis/elasticity/elastic.py +++ b/src/pymatgen/analysis/elasticity/elastic.py @@ -479,8 +479,7 @@ def from_pseudoinverse(cls, strains, stresses) -> Self: "questionable results from vasp data, use with caution." ) stresses = np.array([Stress(stress).voigt for stress in stresses]) - with warnings.catch_warnings(): - strains = np.array([Strain(strain).voigt for strain in strains]) + strains = np.array([Strain(strain).voigt for strain in strains]) voigt_fit = np.transpose(np.dot(np.linalg.pinv(strains), stresses)) return cls.from_voigt(voigt_fit) diff --git a/src/pymatgen/entries/correction_calculator.py b/src/pymatgen/entries/correction_calculator.py index 0053b7e5d23..a196ac52b76 100644 --- a/src/pymatgen/entries/correction_calculator.py +++ b/src/pymatgen/entries/correction_calculator.py @@ -226,7 +226,7 @@ def compute_corrections(self, exp_entries: list, calc_entries: dict) -> dict: with warnings.catch_warnings(): # numpy raises warning if the entire array is nan values - warnings.simplefilter("ignore", category=RuntimeWarning) + warnings.filterwarnings("ignore", message="Mean of empty slice", category=RuntimeWarning) mean_uncert = np.nanmean(sigma) sigma = np.where(np.isnan(sigma), mean_uncert, sigma) diff --git a/src/pymatgen/transformations/advanced_transformations.py b/src/pymatgen/transformations/advanced_transformations.py index 2537241b653..1a71eb1b95b 100644 --- a/src/pymatgen/transformations/advanced_transformations.py +++ b/src/pymatgen/transformations/advanced_transformations.py @@ -923,7 +923,7 @@ def find_codopant( for sym in symbols: try: with warnings.catch_warnings(): - warnings.simplefilter("ignore") + warnings.filterwarnings("ignore", message=r"No (default )?ionic radius for .+") sp = Species(sym, oxidation_state) radius = sp.ionic_radius if radius is not None: diff --git a/tests/command_line/test_bader_caller.py b/tests/command_line/test_bader_caller.py index ad39f8c6b61..90ec2df89d1 100644 --- a/tests/command_line/test_bader_caller.py +++ b/tests/command_line/test_bader_caller.py @@ -1,6 +1,5 @@ from __future__ import annotations -import warnings from shutil import which import numpy as np @@ -17,9 +16,6 @@ @pytest.mark.skipif(not which("bader"), reason="bader executable not present") class TestBaderAnalysis(PymatgenTest): - def setUp(self): - warnings.catch_warnings() - def test_init(self): # test with reference file analysis = BaderAnalysis( diff --git a/tests/core/test_units.py b/tests/core/test_units.py index b94197b5645..7334f08f6c3 100644 --- a/tests/core/test_units.py +++ b/tests/core/test_units.py @@ -117,7 +117,7 @@ def test_deprecated_memory(self): with warnings.catch_warnings(): for unit in ("KB", "MB", "GB", "TB"): - warnings.filterwarnings("error", message=f"Unit {unit} is deprecated", category=DeprecationWarning) + warnings.filterwarnings("error", f"Unit {unit} is deprecated", DeprecationWarning) Memory(1, unit) def test_unitized(self): From a6762a2e6b8ac595b82f0909a97dbf5a6c7cf190 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 15:50:47 +0800 Subject: [PATCH 05/13] clean up warning in mcsqs --- src/pymatgen/command_line/mcsqs_caller.py | 7 ++----- tests/command_line/test_mcsqs_caller.py | 6 +++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pymatgen/command_line/mcsqs_caller.py b/src/pymatgen/command_line/mcsqs_caller.py index 78647df8591..b5ac9af0a18 100644 --- a/src/pymatgen/command_line/mcsqs_caller.py +++ b/src/pymatgen/command_line/mcsqs_caller.py @@ -6,7 +6,6 @@ import os import tempfile -import warnings from pathlib import Path from shutil import which from subprocess import Popen, TimeoutExpired @@ -32,7 +31,7 @@ class Sqs(NamedTuple): @requires( which("mcsqs") and which("str2cif"), - "run_mcsqs requires first installing AT-AT, see https://www.brown.edu/Departments/Engineering/Labs/avdw/atat/", + "run_mcsqs requires ATAT, see https://www.brown.edu/Departments/Engineering/Labs/avdw/atat/", ) def run_mcsqs( structure: Structure, @@ -191,9 +190,7 @@ def _parse_sqs_path(path) -> Sqs: process = Popen(["str2cif"], stdin=input_file, stdout=output_file, cwd=path) process.communicate() - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - best_sqs = Structure.from_file(path / "bestsqs.out") + best_sqs = Structure.from_file(path / "bestsqs.out") # Get best SQS objective function with open(path / "bestcorr.out") as file: diff --git a/tests/command_line/test_mcsqs_caller.py b/tests/command_line/test_mcsqs_caller.py index ca9f8a018d6..3ef1bd0010d 100644 --- a/tests/command_line/test_mcsqs_caller.py +++ b/tests/command_line/test_mcsqs_caller.py @@ -17,7 +17,7 @@ TEST_DIR = f"{TEST_FILES_DIR}/io/atat/mcsqs" -@pytest.mark.skipif(not (which("mcsqs") and which("str2cif")), reason="mcsqs executable not present") +@pytest.mark.skipif(not (which("mcsqs") and which("str2cif")), reason="mcsqs or str2cif executable not present") class TestMcsqsCaller(PymatgenTest): def setUp(self): self.pzt_structs = loadfn(f"{TEST_DIR}/pzt-structs.json") @@ -98,10 +98,14 @@ def test_mcsqs_perfect_match_error_parallel(self): assert sqs.objective_function == "Perfect_match" + @pytest.mark.skip("wrong err msg, need someone to check this") def test_mcsqs_caller_runtime_error(self): struct = self.struct.copy() struct.replace_species({"Ti": {"Ti": 0.5, "Zr": 0.5}, "Zr": {"Ti": 0.5, "Zr": 0.5}}) struct.replace_species({"Pb": {"Ti": 0.2, "Pb": 0.8}}) struct.replace_species({"O": {"F": 0.8, "O": 0.2}}) + + # TODO: the following gives a different error with message: "mcsqs did not generate + # output files, is search_time sufficient or are number of instances too high?" with pytest.raises(RuntimeError, match="mcsqs exited before timeout reached"): run_mcsqs(struct, {2: 6, 3: 4}, 10, 0.000001) From fc6cdc49517b4f1ba0af1d370bf4b0c941278be6 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 15:57:56 +0800 Subject: [PATCH 06/13] change err msg --- src/pymatgen/command_line/mcsqs_caller.py | 6 +++--- tests/command_line/test_mcsqs_caller.py | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/pymatgen/command_line/mcsqs_caller.py b/src/pymatgen/command_line/mcsqs_caller.py index b5ac9af0a18..aa10083ec63 100644 --- a/src/pymatgen/command_line/mcsqs_caller.py +++ b/src/pymatgen/command_line/mcsqs_caller.py @@ -145,7 +145,7 @@ def run_mcsqs( raise RuntimeError("mcsqs exited before timeout reached") - except TimeoutExpired: + except TimeoutExpired as exc: for process in mcsqs_find_sqs_processes: process.kill() process.communicate() @@ -156,7 +156,7 @@ def run_mcsqs( raise RuntimeError( "mcsqs did not generate output files, " "is search_time sufficient or are number of instances too high?" - ) + ) from exc process = Popen(["mcsqs", "-best"]) process.communicate() @@ -165,7 +165,7 @@ def run_mcsqs( return _parse_sqs_path(".") os.chdir(original_directory) - raise TimeoutError("Cluster expansion took too long.") + raise TimeoutError("Cluster expansion took too long.") from exc def _parse_sqs_path(path) -> Sqs: diff --git a/tests/command_line/test_mcsqs_caller.py b/tests/command_line/test_mcsqs_caller.py index 3ef1bd0010d..4ff60fdd0a4 100644 --- a/tests/command_line/test_mcsqs_caller.py +++ b/tests/command_line/test_mcsqs_caller.py @@ -98,14 +98,11 @@ def test_mcsqs_perfect_match_error_parallel(self): assert sqs.objective_function == "Perfect_match" - @pytest.mark.skip("wrong err msg, need someone to check this") def test_mcsqs_caller_runtime_error(self): struct = self.struct.copy() struct.replace_species({"Ti": {"Ti": 0.5, "Zr": 0.5}, "Zr": {"Ti": 0.5, "Zr": 0.5}}) struct.replace_species({"Pb": {"Ti": 0.2, "Pb": 0.8}}) struct.replace_species({"O": {"F": 0.8, "O": 0.2}}) - # TODO: the following gives a different error with message: "mcsqs did not generate - # output files, is search_time sufficient or are number of instances too high?" - with pytest.raises(RuntimeError, match="mcsqs exited before timeout reached"): + with pytest.raises(RuntimeError, match="mcsqs did not generate output files"): run_mcsqs(struct, {2: 6, 3: 4}, 10, 0.000001) From a2711c855c0a4e6160fe97699c22f4621bc129d8 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 18:58:34 +0800 Subject: [PATCH 07/13] migrate unittest skip mark to pytest --- tests/analysis/test_phase_diagram.py | 1 - tests/command_line/test_enumlib_caller.py | 3 +-- tests/command_line/test_gulp_caller.py | 12 +----------- tests/core/test_surface.py | 4 ++-- tests/io/test_zeopp.py | 3 +-- tests/io/vasp/test_sets.py | 5 ++--- 6 files changed, 7 insertions(+), 21 deletions(-) diff --git a/tests/analysis/test_phase_diagram.py b/tests/analysis/test_phase_diagram.py index 934ba59fa9d..dbabecaa3b5 100644 --- a/tests/analysis/test_phase_diagram.py +++ b/tests/analysis/test_phase_diagram.py @@ -2,7 +2,6 @@ import collections import unittest -import unittest.mock from itertools import combinations from numbers import Number from unittest import TestCase diff --git a/tests/command_line/test_enumlib_caller.py b/tests/command_line/test_enumlib_caller.py index 010bb0bd1b8..b0f134cbdd5 100644 --- a/tests/command_line/test_enumlib_caller.py +++ b/tests/command_line/test_enumlib_caller.py @@ -1,6 +1,5 @@ from __future__ import annotations -import unittest from shutil import which import numpy as np @@ -119,7 +118,7 @@ def test_partial_disorder(self): for struct in structures: assert struct.formula == "Ca12 Al8 Si4 Ge8 O48" - @unittest.skip("Fails seemingly at random.") + @pytest.mark.skip("Fails seemingly at random.") def test_timeout(self): struct = Structure.from_file(filename=f"{TEST_FILES_DIR}/cif/garnet.cif") SpacegroupAnalyzer(struct, 0.1) diff --git a/tests/command_line/test_gulp_caller.py b/tests/command_line/test_gulp_caller.py index 192c06ef453..c0e95072292 100644 --- a/tests/command_line/test_gulp_caller.py +++ b/tests/command_line/test_gulp_caller.py @@ -8,7 +8,6 @@ import os import sys -import unittest from shutil import which from unittest import TestCase @@ -132,15 +131,6 @@ def test_structure_lines_no_frac_coords(self): assert "cell" not in inp_str assert "cart" in inp_str - @unittest.skip("Not Implemented yet") - def test_specie_potential(self): - pass - - @unittest.expectedFailure - def test_library_line_explicit_path(self): - gin = self.gio.library_line("/Users/mbkumar/Research/Defects/GulpExe/Libraries/catlow.lib") - assert "lib" in gin - def test_library_line_wrong_file(self): with pytest.raises(GulpError, match="GULP library not found"): self.gio.library_line("temp_to_fail.lib") @@ -280,7 +270,7 @@ def test_get_relaxed_structure(self): assert struct.lattice.a == 4.212 assert struct.lattice.alpha == 90 - @unittest.skip("Test later") + @pytest.mark.skip("Test later") def test_tersoff_input(self): self.gio.tersoff_input(self.structure) diff --git a/tests/core/test_surface.py b/tests/core/test_surface.py index 80f1819da2f..0527fc671b0 100644 --- a/tests/core/test_surface.py +++ b/tests/core/test_surface.py @@ -2,9 +2,9 @@ import json import os -import unittest import numpy as np +import pytest from numpy.testing import assert_allclose from pytest import approx @@ -697,7 +697,7 @@ def test_get_d(self): s2 = recon2.get_unreconstructed_slabs()[0] assert get_d(s1) == approx(get_d(s2)) - @unittest.skip("This test relies on neighbor orders and is hard coded. Disable temporarily") + @pytest.mark.skip("This test relies on neighbor orders and is hard coded. Disable temporarily") def test_previous_reconstructions(self): # Test to see if we generated all reconstruction types correctly and nothing changes diff --git a/tests/io/test_zeopp.py b/tests/io/test_zeopp.py index 819f13a2ee8..28bc18aad8c 100644 --- a/tests/io/test_zeopp.py +++ b/tests/io/test_zeopp.py @@ -1,6 +1,5 @@ from __future__ import annotations -import unittest from unittest import TestCase import pytest @@ -167,7 +166,7 @@ def test_get_voronoi_nodes(self): assert isinstance(vor_face_center_struct, Structure) -@unittest.skip("TODO: file free_sph.cif not present") +@pytest.mark.skip("TODO: file free_sph.cif not present") class TestGetFreeSphereParams(TestCase): def setUp(self): filepath = f"{TEST_FILES_DIR}/cif/free_sph.cif" diff --git a/tests/io/vasp/test_sets.py b/tests/io/vasp/test_sets.py index 4509e1b95f5..6b4f9ceebdb 100644 --- a/tests/io/vasp/test_sets.py +++ b/tests/io/vasp/test_sets.py @@ -2,7 +2,6 @@ import hashlib import os -import unittest from glob import glob from zipfile import ZipFile @@ -1607,7 +1606,7 @@ def test_user_incar_settings(self): assert not vis.incar["LASPH"], "LASPH user setting not applied" assert vis.incar["VDW_SR"] == 1.5, "VDW_SR user setting not applied" - @unittest.skipIf(not os.path.exists(TEST_DIR), "Test files are not present.") + @pytest.mark.skipif(not os.path.exists(TEST_DIR), reason="Test files are not present.") def test_from_prev_calc(self): prev_run = os.path.join(TEST_DIR, "fixtures", "relaxation") @@ -1624,7 +1623,7 @@ def test_from_prev_calc(self): assert "VDW_A2" in vis_bj.incar assert "VDW_S8" in vis_bj.incar - @unittest.skipIf(not os.path.exists(TEST_DIR), "Test files are not present.") + @pytest.mark.skipif(not os.path.exists(TEST_DIR), reason="Test files are not present.") def test_override_from_prev_calc(self): prev_run = os.path.join(TEST_DIR, "fixtures", "relaxation") From 3f5d2cc3be0d0d98b09c0f33f0b771ef0a19a7ca Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 30 Nov 2024 20:10:44 +0800 Subject: [PATCH 08/13] simplify skip mechanism --- tests/command_line/test_enumlib_caller.py | 7 +++---- tests/command_line/test_gulp_caller.py | 16 +++++++--------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/command_line/test_enumlib_caller.py b/tests/command_line/test_enumlib_caller.py index b0f134cbdd5..aa3cebff4a3 100644 --- a/tests/command_line/test_enumlib_caller.py +++ b/tests/command_line/test_enumlib_caller.py @@ -13,12 +13,11 @@ from pymatgen.transformations.standard_transformations import SubstitutionTransformation from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest -enum_cmd = which("enum.x") or which("multienum.x") -makestr_cmd = which("makestr.x") or which("makeStr.x") or which("makeStr.py") -enumlib_present = enum_cmd and makestr_cmd +ENUM_CMD = which("enum.x") or which("multienum.x") +MAKESTR_CMD = which("makestr.x") or which("makeStr.x") or which("makeStr.py") -@pytest.mark.skipif(not enumlib_present, reason="enum_lib not present.") +@pytest.mark.skipif(not (ENUM_CMD and MAKESTR_CMD), reason="enumlib not present.") class TestEnumlibAdaptor(PymatgenTest): def test_init(self): struct = self.get_structure("LiFePO4") diff --git a/tests/command_line/test_gulp_caller.py b/tests/command_line/test_gulp_caller.py index c0e95072292..18c3f05c268 100644 --- a/tests/command_line/test_gulp_caller.py +++ b/tests/command_line/test_gulp_caller.py @@ -29,13 +29,15 @@ TEST_DIR = f"{TEST_FILES_DIR}/command_line/gulp" -gulp_present = which("gulp") and os.getenv("GULP_LIB") and ("win" not in sys.platform) -# disable gulp tests for now. Right now, it is compiled against libgfortran3, which is no longer supported in the new -# Ubuntu 20.04. -gulp_present = False +GULP_PRESENT = which("gulp") and os.getenv("GULP_LIB") and ("win" not in sys.platform) +# Disable GULP tests for now: it is compiled against `libgfortran3``, +# which is no longer supported in Ubuntu 20.04 and onwards. +GULP_PRESENT = False + +if not GULP_PRESENT: + pytest.skip(reason="GULP not available", allow_module_level=True) -@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestGulpCaller: def test_run(self): mgo_lattice = np.eye(3) * 4.212 @@ -104,7 +106,6 @@ def test_decimal(self): caller.run(buckingham_input) -@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestGulpIO(TestCase): def setUp(self): self.structure = Structure.from_file(f"{VASP_IN_DIR}/POSCAR_Al12O18") @@ -275,7 +276,6 @@ def test_tersoff_input(self): self.gio.tersoff_input(self.structure) -@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestGlobalFunctions(TestCase): def setUp(self): mgo_latt = np.eye(3) * 4.212 @@ -327,7 +327,6 @@ def test_get_energy_relax_structure_buckingham(self): assert site_len == len(self.mgo_uc) -@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestBuckinghamPotentialLewis(TestCase): def setUp(self): self.bpl = BuckinghamPotential("lewis") @@ -355,7 +354,6 @@ def test_spring(self): assert self.bpl.spring_dict["O"] != "" -@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestBuckinghamPotentialBush(TestCase): def setUp(self): self.bpb = BuckinghamPotential("bush") From 06282cfe5160618bf9b0eef87620de1e3350343b Mon Sep 17 00:00:00 2001 From: Haoyu Yang Date: Sun, 1 Dec 2024 17:59:52 +0800 Subject: [PATCH 09/13] try to override unittest methods --- src/pymatgen/util/testing/__init__.py | 16 +--- src/pymatgen/util/testing/_temp_testcase.py | 83 +++++++++++++++++++++ tests/util/test_testing_migrate.py | 25 +++---- 3 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 src/pymatgen/util/testing/_temp_testcase.py diff --git a/src/pymatgen/util/testing/__init__.py b/src/pymatgen/util/testing/__init__.py index 9fc69b03004..5abd470b2ed 100644 --- a/src/pymatgen/util/testing/__init__.py +++ b/src/pymatgen/util/testing/__init__.py @@ -10,16 +10,15 @@ import json import pickle # use pickle, not cPickle so that we get the traceback in case of errors import string -import warnings from pathlib import Path from typing import TYPE_CHECKING -from unittest import TestCase import pytest from monty.json import MontyDecoder, MontyEncoder, MSONable from monty.serialization import loadfn from pymatgen.core import ROOT, SETTINGS, Structure +from pymatgen.util.testing._temp_testcase import _TempTestCase4Migrate if TYPE_CHECKING: from collections.abc import Sequence @@ -36,23 +35,12 @@ FAKE_POTCAR_DIR = f"{VASP_IN_DIR}/fake_potcars" -class PymatgenTest(TestCase): +class PymatgenTest(_TempTestCase4Migrate): """Extends unittest.TestCase with several assert methods for array and str comparison.""" # dict of lazily-loaded test structures (initialized to None) TEST_STRUCTURES: ClassVar[dict[str | Path, Structure | None]] = dict.fromkeys(STRUCTURES_DIR.glob("*")) - @classmethod - def setUpClass(cls): - """Issue a FutureWarning, see PR 4209.""" - warnings.warn( - "PymatgenTest is scheduled for migration to pytest after 2026-01-01. " - "Please adapt your tests accordingly.", - FutureWarning, - stacklevel=2, - ) - super().setUpClass() - @pytest.fixture(autouse=True) # make all tests run a in a temporary directory accessible via self.tmp_path def _tmp_dir(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: # https://pytest.org/en/latest/how-to/unittest.html#using-autouse-fixtures-and-accessing-other-fixtures diff --git a/src/pymatgen/util/testing/_temp_testcase.py b/src/pymatgen/util/testing/_temp_testcase.py new file mode 100644 index 00000000000..c462ff9608d --- /dev/null +++ b/src/pymatgen/util/testing/_temp_testcase.py @@ -0,0 +1,83 @@ +# ruff: noqa: PT009, PT027 + + +from __future__ import annotations + +import warnings +from unittest import TestCase + + +class _TempTestCase4Migrate(TestCase): + """Temporary TestCase for migration to `pytest` framework, + inserted FutureWarning for unittest.TestCase-specific methods. + """ + + @staticmethod + def _issue_warning(method_name): + warnings.warn( + f"unittest {method_name=} will not be supported by pytest after migration by 2026-01-01, see PR4209.", + FutureWarning, + stacklevel=2, + ) + + def setUp(self, *args, **kwargs): + self._issue_warning("setUp") + super().setUp(*args, **kwargs) + + def tearDown(self, *args, **kwargs): + self._issue_warning("tearDown") + super().tearDown(*args, **kwargs) + + @classmethod + def setUpClass(cls, *args, **kwargs): + cls._issue_warning("setUpClass") + super().setUpClass(*args, **kwargs) + + @classmethod + def tearDownClass(cls, *args, **kwargs): + cls._issue_warning("tearDownClass") + super().tearDownClass(*args, **kwargs) + + def assertEqual(self, *args, **kwargs): + self._issue_warning("assertEqual") + return super().assertEqual(*args, **kwargs) + + def assertNotEqual(self, *args, **kwargs): + self._issue_warning("assertNotEqual") + return super().assertNotEqual(*args, **kwargs) + + def assertTrue(self, *args, **kwargs): + self._issue_warning("assertTrue") + return super().assertTrue(*args, **kwargs) + + def assertFalse(self, *args, **kwargs): + self._issue_warning("assertFalse") + return super().assertFalse(*args, **kwargs) + + def assertIsNone(self, *args, **kwargs): + self._issue_warning("assertIsNone") + return super().assertIsNone(*args, **kwargs) + + def assertIsNotNone(self, *args, **kwargs): + self._issue_warning("assertIsNotNone") + return super().assertIsNotNone(*args, **kwargs) + + def assertIn(self, *args, **kwargs): # codespell:ignore + self._issue_warning("assertIn") # codespell:ignore + return super().assertIn(*args, **kwargs) # codespell:ignore + + def assertNotIn(self, *args, **kwargs): + self._issue_warning("assertNotIn") + return super().assertNotIn(*args, **kwargs) + + def assertIsInstance(self, *args, **kwargs): + self._issue_warning("assertIsInstance") + return super().assertIsInstance(*args, **kwargs) + + def assertNotIsInstance(self, *args, **kwargs): + self._issue_warning("assertNotIsInstance") + return super().assertNotIsInstance(*args, **kwargs) + + def assertRaises(self, *args, **kwargs): + self._issue_warning("assertRaises") + return super().assertRaises(*args, **kwargs) diff --git a/tests/util/test_testing_migrate.py b/tests/util/test_testing_migrate.py index d7ce59774f3..8ff18eff91b 100644 --- a/tests/util/test_testing_migrate.py +++ b/tests/util/test_testing_migrate.py @@ -16,6 +16,8 @@ Replace with direct Python `assert` statements. """ +# ruff: noqa: PT009, PT027, FBT003 + from __future__ import annotations import pytest @@ -23,17 +25,14 @@ from pymatgen.util.testing import PymatgenTest -@pytest.mark.filterwarnings("ignore:PymatgenTest is scheduled for migration to pytest") class TestPymatgenTestTestCase(PymatgenTest): """Baseline inspector for migration side effects.""" - def test_pmg_test_migration_warning(self): - """Test PymatgenTest migration warning.""" - with pytest.warns(FutureWarning, match="PymatgenTest is scheduled for migration to pytest after 2026-01-01"): - self.setUpClass() # invoke the setup phase - def test_unittest_testcase_specific_funcs(self): - """Make sure TestCase-specific methods are available until migration.""" + """Make sure TestCase-specific methods are available until migration. + TODO: check warnings + """ + # Testing setUp and tearDown methods self.setUp() self.tearDown() @@ -43,9 +42,9 @@ def test_unittest_testcase_specific_funcs(self): self.tearDownClass() # Test the assertion methods - self.assertTrue(True) # noqa: PT009, FBT003 - self.assertFalse(False) # noqa: PT009, FBT003 - self.assertEqual(1, 1) # noqa: PT009 + self.assertTrue(True) + self.assertFalse(False) + self.assertEqual(1, 1) class TestPymatgenTestPytest: @@ -65,8 +64,8 @@ def test_unittest_testcase_specific_funcs(self): # Test the assertion methods with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): - self.assertTrue(True) # noqa: PT009, FBT003 + self.assertTrue(True) with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): - self.assertFalse(False) # noqa: PT009, FBT003 + self.assertFalse(False) with pytest.raises(AttributeError, match="'TestPymatgenTestPytest' object has no attribute"): - self.assertEqual(1, 1) # noqa: PT009 + self.assertEqual(1, 1) From 6f95ba8890000b88ca790bbada4ffbfcd06b042c Mon Sep 17 00:00:00 2001 From: Haoyu Yang Date: Sun, 1 Dec 2024 20:05:44 +0800 Subject: [PATCH 10/13] revert for a separate migration PR --- tests/command_line/test_enumlib_caller.py | 10 ++++++---- tests/command_line/test_gulp_caller.py | 19 +++++++++++-------- tests/core/test_surface.py | 4 ++-- tests/io/test_zeopp.py | 3 ++- tests/io/vasp/test_sets.py | 5 +++-- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/tests/command_line/test_enumlib_caller.py b/tests/command_line/test_enumlib_caller.py index aa3cebff4a3..010bb0bd1b8 100644 --- a/tests/command_line/test_enumlib_caller.py +++ b/tests/command_line/test_enumlib_caller.py @@ -1,5 +1,6 @@ from __future__ import annotations +import unittest from shutil import which import numpy as np @@ -13,11 +14,12 @@ from pymatgen.transformations.standard_transformations import SubstitutionTransformation from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest -ENUM_CMD = which("enum.x") or which("multienum.x") -MAKESTR_CMD = which("makestr.x") or which("makeStr.x") or which("makeStr.py") +enum_cmd = which("enum.x") or which("multienum.x") +makestr_cmd = which("makestr.x") or which("makeStr.x") or which("makeStr.py") +enumlib_present = enum_cmd and makestr_cmd -@pytest.mark.skipif(not (ENUM_CMD and MAKESTR_CMD), reason="enumlib not present.") +@pytest.mark.skipif(not enumlib_present, reason="enum_lib not present.") class TestEnumlibAdaptor(PymatgenTest): def test_init(self): struct = self.get_structure("LiFePO4") @@ -117,7 +119,7 @@ def test_partial_disorder(self): for struct in structures: assert struct.formula == "Ca12 Al8 Si4 Ge8 O48" - @pytest.mark.skip("Fails seemingly at random.") + @unittest.skip("Fails seemingly at random.") def test_timeout(self): struct = Structure.from_file(filename=f"{TEST_FILES_DIR}/cif/garnet.cif") SpacegroupAnalyzer(struct, 0.1) diff --git a/tests/command_line/test_gulp_caller.py b/tests/command_line/test_gulp_caller.py index 18c3f05c268..9ff8c4bd10f 100644 --- a/tests/command_line/test_gulp_caller.py +++ b/tests/command_line/test_gulp_caller.py @@ -8,6 +8,7 @@ import os import sys +import unittest from shutil import which from unittest import TestCase @@ -29,15 +30,13 @@ TEST_DIR = f"{TEST_FILES_DIR}/command_line/gulp" -GULP_PRESENT = which("gulp") and os.getenv("GULP_LIB") and ("win" not in sys.platform) -# Disable GULP tests for now: it is compiled against `libgfortran3``, -# which is no longer supported in Ubuntu 20.04 and onwards. -GULP_PRESENT = False - -if not GULP_PRESENT: - pytest.skip(reason="GULP not available", allow_module_level=True) +gulp_present = which("gulp") and os.getenv("GULP_LIB") and ("win" not in sys.platform) +# disable gulp tests for now. Right now, it is compiled against libgfortran3, which is no longer supported in the new +# Ubuntu 20.04. +gulp_present = False +@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestGulpCaller: def test_run(self): mgo_lattice = np.eye(3) * 4.212 @@ -106,6 +105,7 @@ def test_decimal(self): caller.run(buckingham_input) +@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestGulpIO(TestCase): def setUp(self): self.structure = Structure.from_file(f"{VASP_IN_DIR}/POSCAR_Al12O18") @@ -271,11 +271,12 @@ def test_get_relaxed_structure(self): assert struct.lattice.a == 4.212 assert struct.lattice.alpha == 90 - @pytest.mark.skip("Test later") + @unittest.skip("Test later") def test_tersoff_input(self): self.gio.tersoff_input(self.structure) +@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestGlobalFunctions(TestCase): def setUp(self): mgo_latt = np.eye(3) * 4.212 @@ -327,6 +328,7 @@ def test_get_energy_relax_structure_buckingham(self): assert site_len == len(self.mgo_uc) +@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestBuckinghamPotentialLewis(TestCase): def setUp(self): self.bpl = BuckinghamPotential("lewis") @@ -354,6 +356,7 @@ def test_spring(self): assert self.bpl.spring_dict["O"] != "" +@pytest.mark.skipif(not gulp_present, reason="gulp not present.") class TestBuckinghamPotentialBush(TestCase): def setUp(self): self.bpb = BuckinghamPotential("bush") diff --git a/tests/core/test_surface.py b/tests/core/test_surface.py index 0527fc671b0..80f1819da2f 100644 --- a/tests/core/test_surface.py +++ b/tests/core/test_surface.py @@ -2,9 +2,9 @@ import json import os +import unittest import numpy as np -import pytest from numpy.testing import assert_allclose from pytest import approx @@ -697,7 +697,7 @@ def test_get_d(self): s2 = recon2.get_unreconstructed_slabs()[0] assert get_d(s1) == approx(get_d(s2)) - @pytest.mark.skip("This test relies on neighbor orders and is hard coded. Disable temporarily") + @unittest.skip("This test relies on neighbor orders and is hard coded. Disable temporarily") def test_previous_reconstructions(self): # Test to see if we generated all reconstruction types correctly and nothing changes diff --git a/tests/io/test_zeopp.py b/tests/io/test_zeopp.py index 28bc18aad8c..819f13a2ee8 100644 --- a/tests/io/test_zeopp.py +++ b/tests/io/test_zeopp.py @@ -1,5 +1,6 @@ from __future__ import annotations +import unittest from unittest import TestCase import pytest @@ -166,7 +167,7 @@ def test_get_voronoi_nodes(self): assert isinstance(vor_face_center_struct, Structure) -@pytest.mark.skip("TODO: file free_sph.cif not present") +@unittest.skip("TODO: file free_sph.cif not present") class TestGetFreeSphereParams(TestCase): def setUp(self): filepath = f"{TEST_FILES_DIR}/cif/free_sph.cif" diff --git a/tests/io/vasp/test_sets.py b/tests/io/vasp/test_sets.py index 6b4f9ceebdb..4509e1b95f5 100644 --- a/tests/io/vasp/test_sets.py +++ b/tests/io/vasp/test_sets.py @@ -2,6 +2,7 @@ import hashlib import os +import unittest from glob import glob from zipfile import ZipFile @@ -1606,7 +1607,7 @@ def test_user_incar_settings(self): assert not vis.incar["LASPH"], "LASPH user setting not applied" assert vis.incar["VDW_SR"] == 1.5, "VDW_SR user setting not applied" - @pytest.mark.skipif(not os.path.exists(TEST_DIR), reason="Test files are not present.") + @unittest.skipIf(not os.path.exists(TEST_DIR), "Test files are not present.") def test_from_prev_calc(self): prev_run = os.path.join(TEST_DIR, "fixtures", "relaxation") @@ -1623,7 +1624,7 @@ def test_from_prev_calc(self): assert "VDW_A2" in vis_bj.incar assert "VDW_S8" in vis_bj.incar - @pytest.mark.skipif(not os.path.exists(TEST_DIR), reason="Test files are not present.") + @unittest.skipIf(not os.path.exists(TEST_DIR), "Test files are not present.") def test_override_from_prev_calc(self): prev_run = os.path.join(TEST_DIR, "fixtures", "relaxation") From c94f2f535abc4a1cdd97775ac6822ef4b6a03b34 Mon Sep 17 00:00:00 2001 From: Haoyu Yang Date: Sun, 1 Dec 2024 20:19:37 +0800 Subject: [PATCH 11/13] check future warnings --- tests/util/test_testing_migrate.py | 41 +++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/tests/util/test_testing_migrate.py b/tests/util/test_testing_migrate.py index 8ff18eff91b..809b3370759 100644 --- a/tests/util/test_testing_migrate.py +++ b/tests/util/test_testing_migrate.py @@ -25,26 +25,49 @@ from pymatgen.util.testing import PymatgenTest +@pytest.mark.filterwarnings("ignore", message="will not be supported by pytest after migration", category=FutureWarning) class TestPymatgenTestTestCase(PymatgenTest): """Baseline inspector for migration side effects.""" def test_unittest_testcase_specific_funcs(self): - """Make sure TestCase-specific methods are available until migration. - TODO: check warnings + """Make sure TestCase-specific methods are available until migration, + and FutureWarning is emitted. """ + msg = "will not be supported by pytest after migration" # Testing setUp and tearDown methods - self.setUp() - self.tearDown() + with pytest.warns(FutureWarning, match=msg): + self.setUp() + with pytest.warns(FutureWarning, match=msg): + self.tearDown() # Testing class-level setUp and tearDown methods - self.setUpClass() - self.tearDownClass() + with pytest.warns(FutureWarning, match=msg): + self.setUpClass() + with pytest.warns(FutureWarning, match=msg): + self.tearDownClass() # Test the assertion methods - self.assertTrue(True) - self.assertFalse(False) - self.assertEqual(1, 1) + with pytest.warns(FutureWarning, match=msg): + self.assertEqual(1, 1) + + with pytest.warns(FutureWarning, match=msg): + self.assertNotEqual(1, 2) + + with pytest.warns(FutureWarning, match=msg): + self.assertTrue(True) + + with pytest.warns(FutureWarning, match=msg): + self.assertFalse(False) + + with pytest.warns(FutureWarning, match=msg): + self.assertIsNone(None) + + with pytest.warns(FutureWarning, match=msg): + self.assertIsNotNone("hello") + + with pytest.warns(FutureWarning, match=msg), self.assertRaises(ValueError): + raise ValueError("hi") class TestPymatgenTestPytest: From 9f39b11649e66230567fcd85da68bc9e5145718f Mon Sep 17 00:00:00 2001 From: Haoyu Yang Date: Sun, 1 Dec 2024 20:23:36 +0800 Subject: [PATCH 12/13] relocate migration guide to PR description --- src/pymatgen/util/testing/_temp_testcase.py | 11 ++++++----- tests/util/test_testing_migrate.py | 12 ------------ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/pymatgen/util/testing/_temp_testcase.py b/src/pymatgen/util/testing/_temp_testcase.py index c462ff9608d..d87ade89a46 100644 --- a/src/pymatgen/util/testing/_temp_testcase.py +++ b/src/pymatgen/util/testing/_temp_testcase.py @@ -1,5 +1,10 @@ -# ruff: noqa: PT009, PT027 +"""Temporary TestCase for migration to `pytest` framework, +inserted FutureWarning for unittest.TestCase-specific methods. + +TODO: remove entire module after migration +""" +# ruff: noqa: PT009, PT027 from __future__ import annotations @@ -8,10 +13,6 @@ class _TempTestCase4Migrate(TestCase): - """Temporary TestCase for migration to `pytest` framework, - inserted FutureWarning for unittest.TestCase-specific methods. - """ - @staticmethod def _issue_warning(method_name): warnings.warn( diff --git a/tests/util/test_testing_migrate.py b/tests/util/test_testing_migrate.py index 809b3370759..d99f4fe06f1 100644 --- a/tests/util/test_testing_migrate.py +++ b/tests/util/test_testing_migrate.py @@ -2,18 +2,6 @@ `unittest.TestCase`. It ensures we're aware the side effects from the migration. TODO: remove this test module after migration (2026-01-01), see PR 4209. - -`unittest.TestCase`-specific features and brief migration guide: -- Setup/teardown methods (`setUp`, `setUpClass`, `tearDown`, `tearDownClass`): - 1. Recommended approach in pytest: Use fixtures. - Documentation: https://docs.pytest.org/en/stable/reference/fixtures.html#fixture - OR - 2. Use pytest's xUnit-style setup/teardown functions: - `[setup/teardown]_[class/method/function]`. - Documentation: https://docs.pytest.org/en/stable/how-to/xunit_setup.html - -- Assertion methods (`assertTrue`, `assertFalse`, `assertEqual`, etc.): - Replace with direct Python `assert` statements. """ # ruff: noqa: PT009, PT027, FBT003 From 539d695e02e243fd940dd4666e9dbea2ce25c7f0 Mon Sep 17 00:00:00 2001 From: Haoyu Yang Date: Sun, 1 Dec 2024 20:36:10 +0800 Subject: [PATCH 13/13] migrate delete to another 4212 --- tests/command_line/test_gulp_caller.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/command_line/test_gulp_caller.py b/tests/command_line/test_gulp_caller.py index 9ff8c4bd10f..192c06ef453 100644 --- a/tests/command_line/test_gulp_caller.py +++ b/tests/command_line/test_gulp_caller.py @@ -132,6 +132,15 @@ def test_structure_lines_no_frac_coords(self): assert "cell" not in inp_str assert "cart" in inp_str + @unittest.skip("Not Implemented yet") + def test_specie_potential(self): + pass + + @unittest.expectedFailure + def test_library_line_explicit_path(self): + gin = self.gio.library_line("/Users/mbkumar/Research/Defects/GulpExe/Libraries/catlow.lib") + assert "lib" in gin + def test_library_line_wrong_file(self): with pytest.raises(GulpError, match="GULP library not found"): self.gio.library_line("temp_to_fail.lib")