From 288f38dca3e97de86ad9e894d3de37f736d2756f Mon Sep 17 00:00:00 2001
From: Simon Rose <simon.rose@ess.eu>
Date: Tue, 6 Sep 2022 13:48:48 +0200
Subject: [PATCH] E3-731: Improve wrapper fixture

Previously, the wrapper fixture would set some environment variables
when running `run_make`; this meant that you could not debug a test
case by simply navigating to the test directory and trying to build
the generated module. This change fixes that.
---
 tests/conftest.py     | 66 +++++++++++++++++++++++--------------------
 tests/test_build.py   | 34 +++++++++++-----------
 tests/test_e3.py      | 26 ++++++-----------
 tests/test_fixture.py |  8 ++++++
 4 files changed, 69 insertions(+), 65 deletions(-)
 create mode 100644 tests/test_fixture.py

diff --git a/tests/conftest.py b/tests/conftest.py
index 7fdf70eb..12ada8cb 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -10,6 +10,20 @@ from git import Repo
 
 class Wrapper:
     def __init__(self, root_path: Path, name=None, include_dbd=True, **kwargs):
+        test_env = os.environ.copy()
+
+        assert "EPICS_BASE" in test_env
+        assert "E3_REQUIRE_VERSION" in test_env
+
+        e3_require_config = (
+            Path(os.environ.get("EPICS_BASE"))
+            / "require"
+            / os.environ.get("E3_REQUIRE_VERSION")
+            / "configure"
+        )
+
+        assert e3_require_config.is_dir()
+
         if name is None:
             name = "test_mod_" + "".join(choice(ascii_lowercase) for _ in range(16))
         self.path = root_path / f"e3-{name}"
@@ -24,11 +38,10 @@ class Wrapper:
         self.config_dir = self.path / "configure"
         self.config_dir.mkdir()
 
-        self.config_module = self.path / "CONFIG_MODULE"
+        self.config_module = self.config_dir / "CONFIG_MODULE"
         self.config_module.touch()
 
-        self.makefile = self.path / f"{name}.Makefile"
-
+        self.makefile = self.path / "Makefile"
         makefile_contents = f"""
 TOP:=$(CURDIR)
 
@@ -37,20 +50,24 @@ E3_MODULE_VERSION?=0.0.0+0
 E3_MODULE_SRC_PATH:={module_path}
 E3_MODULE_MAKEFILE:={name}.Makefile
 
-include $(TOP)/CONFIG_MODULE
+include $(TOP)/configure/CONFIG_MODULE
+-include $(TOP)/configure/CONFIG_MODULE.local
+
+REQUIRE_CONFIG:={e3_require_config}
 
 include $(REQUIRE_CONFIG)/CONFIG
 include $(REQUIRE_CONFIG)/RULES_SITEMODS
 """
-        with open(self.path / "Makefile", "w") as f:
-            f.write(makefile_contents)
+        (self.makefile).write_text(makefile_contents)
 
+        self.module_makefile = self.path / f"{name}.Makefile"
         module_makefile_contents = """
 where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
 include $(E3_REQUIRE_TOOLS)/driver.makefile
+
+EXCLUDE_ARCHS+=debug
 """
-        with open(self.path / f"{name}.Makefile", "w") as f:
-            f.write(module_makefile_contents)
+        (self.module_makefile).write_text(module_makefile_contents)
 
         if include_dbd:
             self.add_file("test.dbd")
@@ -71,8 +88,8 @@ include $(E3_REQUIRE_TOOLS)/driver.makefile
         with open(self.config_module, "a") as f:
             f.write(f"export {makefile_var} {modifier}= {value}\n")
 
-    def add_var_to_makefile(self, makefile_var: str, value: str, modifier="+"):
-        with open(self.makefile, "a") as f:
+    def add_var_to_module_makefile(self, makefile_var: str, value: str, modifier="+"):
+        with open(self.module_makefile, "a") as f:
             f.write(f"{makefile_var} {modifier}= {value}\n")
 
     def get_env_var(self, var: str):
@@ -88,34 +105,21 @@ include $(E3_REQUIRE_TOOLS)/driver.makefile
         self,
         target: str,
         *args,
-        module_name: str = "",
         module_version: str = "",
         cell_path: str = "",
     ):
         """Attempt to run `make <target> <args>` for the current wrapper."""
 
-        test_env = os.environ.copy()
-
-        assert "EPICS_BASE" in test_env
-        assert Path(test_env["EPICS_BASE"]).is_dir
-        assert "E3_REQUIRE_VERSION" in test_env
-        assert test_env["E3_REQUIRE_VERSION"]
-
-        e3_require_location = (
-            Path(os.environ.get("EPICS_BASE"))
-            / "require"
-            / os.environ.get("E3_REQUIRE_VERSION")
-        )
-        assert e3_require_location.is_dir()
-        test_env["E3_REQUIRE_LOCATION"] = e3_require_location
+        # We should not install in the global environment during tests
+        if target == "install":
+            target = "cellinstall"
 
-        require_config = f"{e3_require_location}/configure"
-        test_env["REQUIRE_CONFIG"] = require_config
+        if target == "uninstall":
+            target = "celluninstall"
 
-        if module_name:
-            test_env["E3_MODULE_NAME"] = module_name
+        env = os.environ.copy()
         if module_version:
-            test_env["E3_MODULE_VERSION"] = module_version
+            env["E3_MODULE_VERSION"] = module_version
         if cell_path:
             self.write_dot_local_data("CONFIG_CELL", {"E3_CELL_PATH": cell_path})
         make_cmd = ["make", "-C", self.path, target] + list(args)
@@ -123,7 +127,7 @@ include $(E3_REQUIRE_TOOLS)/driver.makefile
             make_cmd,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE,
-            env=test_env,
+            env=env,
             encoding="utf-8",
         )
         outs, errs = p.communicate()
diff --git a/tests/test_build.py b/tests/test_build.py
index fa12115a..8f0c46b2 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -69,7 +69,7 @@ def test_local_module(wrapper):
 
 
 def test_missing_dbd_file(wrapper):
-    wrapper.add_var_to_makefile("DBDS", "nonexistent.dbd")
+    wrapper.add_var_to_module_makefile("DBDS", "nonexistent.dbd")
     rc, _, errs = wrapper.run_make("build")
 
     assert rc == 2
@@ -80,7 +80,7 @@ def test_missing_dbd_file(wrapper):
 
 
 def test_missing_source_file(wrapper):
-    wrapper.add_var_to_makefile("SOURCES", "nonexistent.c")
+    wrapper.add_var_to_module_makefile("SOURCES", "nonexistent.c")
     rc, _, errs = wrapper.run_make("build")
 
     assert rc == 2
@@ -125,8 +125,8 @@ def test_header_install_location(wrapper):
 
     extensions = ["h", "hpp", "hxx", "hh"]
     for ext in extensions:
-        wrapper.add_var_to_makefile("HEADERS", f"db/subdir/header.{ext}")
-    wrapper.add_var_to_makefile("KEEP_HEADER_SUBDIRS", "db")
+        wrapper.add_var_to_module_makefile("HEADERS", f"db/subdir/header.{ext}")
+    wrapper.add_var_to_module_makefile("KEEP_HEADER_SUBDIRS", "db")
 
     for ext in extensions:
         (subdir / f"header.{ext}").touch()
@@ -444,10 +444,10 @@ def test_recursive_header_include(wrappers):
     wrapper_b.add_var_to_config_module(f"{wrapper_c.name}_DEP_VERSION", module_version)
     wrapper_a.add_var_to_config_module(f"{wrapper_b.name}_DEP_VERSION", module_version)
 
-    wrapper_c.add_var_to_makefile("HEADERS", f"{wrapper_c.name}.h")
+    wrapper_c.add_var_to_module_makefile("HEADERS", f"{wrapper_c.name}.h")
     (wrapper_c.module_dir / f"{wrapper_c.name}.h").touch()
 
-    wrapper_a.add_var_to_makefile("SOURCES", f"{wrapper_a.name}.c")
+    wrapper_a.add_var_to_module_makefile("SOURCES", f"{wrapper_a.name}.c")
     with open(wrapper_a.module_dir / f"{wrapper_a.name}.c", "w") as f:
         f.write(f'#include "{wrapper_c.name}.h"')
 
@@ -472,7 +472,7 @@ def test_recursive_header_include(wrappers):
 
 
 def test_updated_template_files(wrapper):
-    wrapper.add_var_to_makefile("SUBS", "x.substitutions")
+    wrapper.add_var_to_module_makefile("SUBS", "x.substitutions")
 
     substitution_file = wrapper.module_dir / "x.substitutions"
     substitution_file.write_text("file x.template {pattern {x} {y}}")
@@ -497,9 +497,9 @@ def test_updated_template_files(wrapper):
 def test_expand_db_files(wrapper):
     """Test that the automated template/substitution file expansion works."""
 
-    wrapper.add_var_to_makefile("TMPS", "templates/a.template")
-    wrapper.add_var_to_makefile("SUBS", "b.substitutions")
-    wrapper.add_var_to_makefile("USR_DBFLAGS", "-I templates")
+    wrapper.add_var_to_module_makefile("TMPS", "templates/a.template")
+    wrapper.add_var_to_module_makefile("SUBS", "b.substitutions")
+    wrapper.add_var_to_module_makefile("USR_DBFLAGS", "-I templates")
 
     template_dir = wrapper.module_dir / "templates"
     template_dir.mkdir()
@@ -543,7 +543,7 @@ def test_expand_db_files(wrapper):
 def test_arch_filter(wrapper, installed_archs, param, expected):
     arch_regex = re.compile(r"Pass 2: T_A =\s*([^\s]+)")
 
-    wrapper.add_var_to_makefile(
+    wrapper.add_var_to_module_makefile(
         "CROSS_COMPILER_TARGET_ARCHS", installed_archs, modifier=""
     )
 
@@ -562,8 +562,8 @@ def test_build_fails_if_nth_architecture_fails(wrapper, archs, failing_arch):
     # LIBOBJS is determined in part based on configuration data coming from
     # $(CONFIG)/os/CONFIG.Common.$(T_A); since our architectures do not actually
     # exist, we need to manually define these /before/ driver.makefile is included.
-    makefile_content = wrapper.makefile.read_text()
-    with open(wrapper.makefile, "w") as f:
+    makefile_content = wrapper.module_makefile.read_text()
+    with open(wrapper.module_makefile, "w") as f:
         f.write(
             f"""ifeq ($(T_A),{failing_arch})
                 LIBOBJS = nonexistent_{failing_arch}.o
@@ -576,10 +576,12 @@ def test_build_fails_if_nth_architecture_fails(wrapper, archs, failing_arch):
 
     # Skip the host architecture, we are not testing it.
     host_arch = os.getenv("EPICS_HOST_ARCH")
-    wrapper.add_var_to_makefile("EXCLUDE_ARCHS", host_arch)
+    wrapper.add_var_to_module_makefile("EXCLUDE_ARCHS", host_arch)
 
-    wrapper.add_var_to_makefile("CROSS_COMPILER_TARGET_ARCHS", archs, modifier="")
-    wrapper.add_var_to_makefile("SOURCES", "-none-")
+    wrapper.add_var_to_module_makefile(
+        "CROSS_COMPILER_TARGET_ARCHS", archs, modifier=""
+    )
+    wrapper.add_var_to_module_makefile("SOURCES", "-none-")
 
     rc, _, errs = wrapper.run_make("build")
     assert rc == 2
diff --git a/tests/test_e3.py b/tests/test_e3.py
index 4918681d..5b2c0d4f 100644
--- a/tests/test_e3.py
+++ b/tests/test_e3.py
@@ -6,27 +6,17 @@ def test_loc(wrappers):
 
 
 def test_sitelibs(wrappers):
-    # Overwrite the default makefile
     wrapper = wrappers.get()
-    with open(wrapper.path / "Makefile", "w") as f:
-        f.write(
-            f"""
-TOP:=$(CURDIR)
 
-E3_MODULE_NAME:={wrapper.name}
+    makefile_contents = wrapper.makefile.read_text()
 
-include $(REQUIRE_CONFIG)/RULES_E3
-include $(REQUIRE_CONFIG)/RULES_CELL
-include $(REQUIRE_CONFIG)/DEFINES_FT
-include $(REQUIRE_CONFIG)/RULES_PATCH
-include $(REQUIRE_CONFIG)/RULES_TEST
-
-include $(REQUIRE_CONFIG)/RULES_DKMS
-include $(REQUIRE_CONFIG)/RULES_VARS
-
-include $(REQUIRE_CONFIG)/RULES_DEV
-"""
+    wrapper.makefile.write_text(
+        makefile_contents.replace(
+            "include $(REQUIRE_CONFIG)/RULES_SITEMODS",
+            "include $(REQUIRE_CONFIG)/RULES_E3",
         )
+    )
+
     rc, _, errs = wrapper.run_make("build")
     assert rc == 2
     assert "RULES_E3 should only be loaded from RULES_SITEMODS" in errs
@@ -36,6 +26,6 @@ def test_incorrect_module_name(wrappers):
     module_name = "ADCore"
 
     wrapper = wrappers.get(name=module_name)
-    rc, _, errs = wrapper.run_make("build", module_name=module_name)
+    rc, _, errs = wrapper.run_make("build")
     assert rc == 2
     assert f"E3_MODULE_NAME '{module_name}' is not valid" in errs
diff --git a/tests/test_fixture.py b/tests/test_fixture.py
new file mode 100644
index 00000000..2e61921b
--- /dev/null
+++ b/tests/test_fixture.py
@@ -0,0 +1,8 @@
+import subprocess
+
+from .conftest import Wrapper
+
+
+def test_can_run_make_in_wrapper_directory(wrapper: Wrapper):
+    results = subprocess.run(["make", "-C", wrapper.path, "vars"])
+    assert results.returncode == 0
-- 
GitLab