diff --git a/CHANGELOG.md b/CHANGELOG.md
index 091964fb76adc46492045af248628073dc0ede8a..2e1660cd79aaf446d572ec72db60babf8ec9e611 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   not by require.
 * The loop over `EPICSVERSION` in `driver.makefile` has been removed; various other cleanup has been performed.
 * Improved output during IOC startup
+* e3-sequencer tests added, as well as removing pre-processing of .stt files.
 
 ## [4.0.0]
 
diff --git a/require-ess/tools/driver.makefile b/require-ess/tools/driver.makefile
index 66d3d0877224ed1d402f98b22d25d081928e51d8..c4ddc74ccbeb78e1d49ffb4358a3da3a0d5d2e84 100644
--- a/require-ess/tools/driver.makefile
+++ b/require-ess/tools/driver.makefile
@@ -514,7 +514,7 @@ DBDFILES += $(patsubst %.stt,%_snl.dbd,$(notdir $(filter %.stt,${SRCS})))
 DBDFILES += $(patsubst %.gt,%.dbd,$(notdir $(filter %.gt,${SRCS})))
 
 # snc location
-SNCALL=$(shell ls  -dv $(E3_SITEMODS_PATH)/sequencer/$(sequencer_VERSION)/bin/$(EPICS_HOST_ARCH) 2> /dev/null))
+SNCALL=$(shell ls  -dv $(E3_SITEMODS_PATH)/sequencer/$(sequencer_VERSION)/bin/$(EPICS_HOST_ARCH) 2> /dev/null)
 SNC=$(lastword $(SNCALL))/snc
 
 ifneq (,$(strip $(VLIBS)))
@@ -698,59 +698,35 @@ ${INSTALL_BINS}: $(addprefix ../,$(filter-out /%,${BINS})) $(filter /%,${BINS})
 
 # Create SNL code from st/stt file.
 # Important to have %.o: %.st and %.o: %.stt rule before %.o: %.c rule!
-# Preprocess in any case because docu and implemented EPICS rules mismatch here.
 
 CPPSNCFLAGS1  = $(filter -D%, ${OP_SYS_CFLAGS})
 CPPSNCFLAGS1 += $(filter-out ${OP_SYS_INCLUDE_CPPFLAGS} ,${CPPFLAGS}) ${CPPSNCFLAGS}
 CPPSNCFLAGS1 += -I $(dir $(SNC))../../include
 SNCFLAGS += -r
 
+%.i: %.st
+	@echo ">> Preprocessing $(<F)"
+	$(CPP) ${CPPSNCFLAGS1} $< > $(*F).i
 
-# 1) ESS uses 7.0.3.1 as the minimal EPICS BASE, so we don't need to check 3.13,
-# 2) We also need -c option in $(COMPILE.c) in order to compile generated source file properly
-# 3) SNC (2.1.21) should use -o, because without them, snc returns $(*F).i.c instead of $(*F).c
-#    With the EPICS standard building rule, -o and mv are used.
-#
-# Tuesday, November 28 15:59:37 CET 2017, Jeong Han Lee
-
-
-%$(OBJ) %_snl.dbd: %.st
+%.c: %.i
 	@echo ""
 	@echo ">> SNC building process .... "
 	@echo ">> SNC                  : $(SNC)"
 	@echo ">> SNC_VERSION          : $(sequencer_VERSION)"
-	@echo ">> Preprocessing $(<F)"
-	$(RM) $(*F).i
-	$(CPP) ${CPPSNCFLAGS1} $< > $(*F).i
-	@echo ">> Converting $(*F).i to $(*F).c"
-	$(RM) $@
 	@echo ">> SNC is defined as $(SNC)"
-	$(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c.tmp
-	@mv $(*F).c.tmp $(*F).c
-	@echo ">> Compiling $(*F).c"
-	$(RM) $@
-	$(COMPILE.c) -c ${SNC_CFLAGS} $(*F).c
+	$(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c
+
+%_snl.dbd: %.c
 	@echo ">> Building $(*F)_snl.dbd"
 	awk -F [\(\)]  '/epicsExportRegistrar/ { print "registrar (" $$2 ")"}' $(*F).c > $(*F)_snl.dbd
 
-%$(OBJ) %_snl.dbd: %.stt
+%.c: %.stt
 	@echo ""
 	@echo ">> SNC building process .... "
 	@echo ">> SNC                  : $(SNC)"
 	@echo ">> SNC_VERSION          : $(sequencer_VERSION)"
-	@echo ">> Preprocessing $(<F)"
-	$(RM) $(*F).i
-	$(CPP) ${CPPSNCFLAGS1} $< > $(*F).i
-	@echo ">> Converting $(*F).i to $(*F).c"
-	$(RM) $@
 	@echo ">> SNC is defined as $(SNC)"
-	$(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c.tmp
-	@mv $(*F).c.tmp $(*F).c
-	@echo ">> Compiling $(*F).c"
-	$(RM) $@
-	$(COMPILE.c) -c ${SNC_CFLAGS} $(*F).c
-	@echo "Building $(*F)_snl.dbd"
-	awk -F [\(\)]  '/epicsExportRegistrar/ { print "registrar(" $$2 ")"}' $(*F).c > $(*F)_snl.dbd
+	$(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $< -o $(*F).c
 
 
 # Create GPIB code from *.gt file.
diff --git a/tests/test_sequencer.py b/tests/test_sequencer.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa743cb7209a07957d1baa3804c0a1be883c53ce
--- /dev/null
+++ b/tests/test_sequencer.py
@@ -0,0 +1,130 @@
+import pathlib
+
+import pytest
+
+from .utils import Wrapper
+
+GITLAB_URL = "https://gitlab.esss.lu.se"
+
+TEST_SEQ_SRC = """
+program test
+
+ss ss1 {{
+    state init {{
+        when(delay(1)) {{
+            printf({});
+        }}
+        state init
+    }}
+}}
+"""
+
+
+class Sequencer(Wrapper):
+    sequencer_url = f"{GITLAB_URL}/e3/wrappers/core/e3-sequencer.git"
+
+    def __init__(self, path: pathlib.Path):
+        super().__init__(path, name="sequencer", url=self.sequencer_url)
+
+        self.version = "sequencer_test"
+
+        self.write_dot_local_data(
+            "RELEASE",
+            {"EPICS_BASE": self.epics_base, "E3_REQUIRE_VERSION": self.require_version},
+        )
+        self.write_dot_local_data("CONFIG_MODULE", {"E3_MODULE_VERSION": self.version})
+
+        self.cell_path = self.path / "cellMods"
+
+        rc, *_ = self.run_make("init")
+        assert rc == 0
+        rc, *_ = self.run_make("patch")
+        assert rc == 0
+        rc, *_ = self.run_make("build")
+        assert rc == 0
+        rc, *_ = self.run_make("cellinstall", cell_path=self.cell_path)
+        assert rc == 0
+
+        self.snc_path = (
+            self.cell_path
+            / f"base-{self.base_version}"
+            / f"require-{self.require_version}"
+            / "sequencer"
+            / self.version
+            / "bin"
+            / self.host_arch
+            / "snc"
+        )
+        assert self.snc_path.is_file()
+
+
+@pytest.fixture(scope="module")
+def sequencer(tmp_path_factory):
+    path = tmp_path_factory.mktemp("sequencer_build")
+    yield Sequencer(path)
+
+
+class SNLWrapper(Wrapper):
+    def __init__(self, sequencer: Sequencer, path: pathlib.Path):
+        super().__init__(path)
+        self.sequencer = sequencer
+
+        self.add_var_to_config_module("SEQUENCER_DEP_VERSION", self.sequencer.version)
+
+    def run_make(self, *args):
+        return super().run_make(
+            *args,
+            f"E3_CELL_PATH={self.sequencer.cell_path}",
+            f"SNC={self.sequencer.snc_path}",
+        )
+
+
+@pytest.fixture
+def snl_wrapper(sequencer, tmp_path):
+    yield SNLWrapper(sequencer, tmp_path)
+
+
+@pytest.mark.parametrize("extension", ["st", "stt"])
+def test_compile_snl_file(snl_wrapper: SNLWrapper, extension):
+    snl_filename = "test_filename"
+    seq_source = snl_wrapper.module_dir / f"{snl_filename}.{extension}"
+    seq_source.write_text(TEST_SEQ_SRC.format('""'))
+
+    snl_wrapper.add_var_to_module_makefile("SOURCES", f"{snl_filename}.{extension}")
+
+    rc, *_ = snl_wrapper.run_make("cellbuild")
+    assert rc == 0
+
+    assert (snl_wrapper.build_dir / f"{snl_filename}.o").is_file()
+    assert (snl_wrapper.build_dir / f"{snl_filename}_snl.dbd").is_file()
+
+
+def test_preprocess_st_file(snl_wrapper: SNLWrapper):
+    snl_filename = "test_file.st"
+    seq_src = snl_wrapper.module_dir / snl_filename
+    seq_src.write_text(
+        '#define MESSAGE "waiting\\n"\n' + TEST_SEQ_SRC.format("MESSAGE")
+    )
+
+    snl_wrapper.add_var_to_module_makefile("SOURCES", snl_filename)
+
+    rc, *_ = snl_wrapper.run_make("cellbuild")
+    assert rc == 0
+
+
+def test_do_not_preprocess_stt_file(snl_wrapper: SNLWrapper):
+    snl_filename = "test_file"
+    seq_src = snl_wrapper.module_dir / f"{snl_filename}.stt"
+    seq_src.write_text(
+        '#define MESSAGE "waiting\\n"\n' + TEST_SEQ_SRC.format("MESSAGE")
+    )
+
+    snl_wrapper.add_var_to_module_makefile("SOURCES", f"{snl_filename}.stt")
+
+    rc, _, errs = snl_wrapper.run_make("cellbuild")
+    assert rc == 2
+    assert (
+        f"No rule to make target `{snl_filename}.c', needed by `{snl_filename}_snl.dbd'"
+        in errs
+    )
+    assert not (snl_wrapper.build_dir / f"{snl_filename}.i").is_file()
diff --git a/tests/utils.py b/tests/utils.py
index 3a9958dacc22760b0ad059c7eb105ed5e0bb887a..312f9c32a4b9589af446b52b94b28b45441aa70d 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -10,42 +10,54 @@ from run_iocsh import IOC
 
 
 class Wrapper:
-    def __init__(self, root_path: Path, name=None, include_dbd=True, **kwargs):
+    def __init__(
+        self, root_path: Path, *, name=None, include_dbd=True, url=None, **kwargs
+    ):
         test_env = os.environ.copy()
 
         assert "EPICS_BASE" in test_env
         assert "E3_REQUIRE_VERSION" in test_env
+        assert "EPICS_HOST_ARCH" in test_env
+
+        self.epics_base = Path(os.getenv("EPICS_BASE"))
+        self.base_version = self.epics_base.name.split("base-")[-1]
+        self.host_arch = os.getenv("EPICS_HOST_ARCH")
+        self.require_version = os.getenv("E3_REQUIRE_VERSION")
 
         e3_require_config = (
-            Path(os.environ.get("EPICS_BASE"))
-            / "require"
-            / os.environ.get("E3_REQUIRE_VERSION")
-            / "configure"
+            self.epics_base / "require" / self.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}"
         self.name = name
-
         self.version = "0.0.0+0"
 
+        self.path = root_path / f"e3-{self.name}"
+        self.config_dir = self.path / "configure"
+        self.config_module = self.config_dir / "CONFIG_MODULE"
         module_path = (
-            name if "E3_MODULE_SRC_PATH" not in kwargs else kwargs["E3_MODULE_SRC_PATH"]
+            self.name
+            if "E3_MODULE_SRC_PATH" not in kwargs
+            else kwargs["E3_MODULE_SRC_PATH"]
         )
         self.module_dir = self.path / module_path
-        self.module_dir.mkdir(parents=True)
+        self.build_dir = self.module_dir / f"O.{self.base_version}_{self.host_arch}"
 
-        self.config_dir = self.path / "configure"
-        self.config_dir.mkdir()
+        self.makefile = self.path / "Makefile"
+        self.module_makefile = self.path / f"{self.name}.Makefile"
 
-        self.config_module = self.config_dir / "CONFIG_MODULE"
-        self.config_module.touch()
+        self.url = url
+        if self.url:
+            Repo.clone_from(self.url, self.path)
+        else:
+            self.module_dir.mkdir(parents=True)
+            self.config_dir.mkdir()
+            self.config_module.touch()
 
-        self.makefile = self.path / "Makefile"
-        makefile_contents = f"""
+            makefile_contents = f"""
 TOP:=$(CURDIR)
 
 E3_MODULE_NAME:={name}
@@ -61,21 +73,20 @@ REQUIRE_CONFIG:={e3_require_config}
 include $(REQUIRE_CONFIG)/CONFIG
 include $(REQUIRE_CONFIG)/RULES_SITEMODS
 """
-        (self.makefile).write_text(makefile_contents)
+            self.makefile.write_text(makefile_contents)
 
-        self.module_makefile = self.path / f"{name}.Makefile"
-        module_makefile_contents = """
+            module_makefile_contents = """
 where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
 include $(E3_REQUIRE_TOOLS)/driver.makefile
 
 EXCLUDE_ARCHS+=debug
 """
-        (self.module_makefile).write_text(module_makefile_contents)
+            self.module_makefile.write_text(module_makefile_contents)
 
-        if include_dbd:
-            self.add_file("test.dbd")
+            if include_dbd:
+                self.add_file("test.dbd")
 
-        Repo.init(self.path)
+            Repo.init(self.path)
 
     def add_file(self, name):
         (self.module_dir / name).touch()