diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 178df06bd2a00c2f474c331422016f4738404448..9f23487e54cd5d355f1d67251551f0faa50bae87 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,7 @@ stages:
     - format
     - check
     - build
+    - test
 
 variables:
     GIT_STRATEGY: clone
@@ -10,3 +11,4 @@ include:
   - local: /.gitlab-ci/format.yml
   - local: /.gitlab-ci/check.yml
   - local: /.gitlab-ci/build.yml
+  - local: /.gitlab-ci/test.yml
diff --git a/.gitlab-ci/test.yml b/.gitlab-ci/test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d0e278575f5a1edc1872fbce9d62b68f10d7affa
--- /dev/null
+++ b/.gitlab-ci/test.yml
@@ -0,0 +1,45 @@
+test_amd64_ubuntu_24.04:
+    stage: test
+    script:
+        - .ci/test/test.sh --build-dir "build_*"
+    tags:
+        - compiler
+        - ubuntu-24.04-amd64
+    needs:
+        - job: compile_amd64_ubuntu_24.04
+          artifacts: true
+
+test_sanitizer_amd64_ubuntu_24.04:
+    stage: test
+    script:
+        - .ci/test/test.sh --build-dir "build_*"
+    tags:
+        - compiler
+        - ubuntu-24.04-amd64
+    needs:
+        - job: compile_sanitizer_amd64_ubuntu_24.04
+          artifacts: true
+
+test_coverage_amd64_ubuntu_24.04:
+    stage: test
+    script:
+        - .ci/test/test.sh --build-dir "build" --gcov-bin gcov-14
+    tags:
+        - compiler
+        - ubuntu-24.04-amd64
+    variables:
+      GIT_SUBMODULE_STRATEGY: recursive
+    needs:
+        - job: compile_coverage_amd64_ubuntu_24.04
+          artifacts: true
+    coverage: /^\s*lines:\s*\d+.\d+\%/
+    artifacts:
+        when: always
+        expire_in: 1 week
+        paths:
+            - "xml"
+            - "html"
+        reports:
+            coverage_report:
+                coverage_format: cobertura
+                path: "xml/coverage.xml"