diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8a252c89436444923455c055d45d09c69f64495a..26459445738d382bbc85a6b36185c6f3613691bc 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -84,4 +84,6 @@ trigger lecture:
     branch: feature/test-dumux-trigger
     strategy: depend
   variables:
-    DUMUX_MERGE_REQUEST_BRANCH: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
+    DUMUX_PIPELINE_SOURCE: $CI_PIPELINE_SOURCE
+    DUMUX_MERGE_REQUEST_SOURCE_BRANCH: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
+    DUMUX_MERGE_REQUEST_TARGET_BRANCH: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
diff --git a/.gitlab-ci/default.yml b/.gitlab-ci/default.yml
index 54338ba6c7a1a11705811bc66096bf471c9b6c1a..706c7a310d42f1081a96e7ccfd71882cb9a3911e 100644
--- a/.gitlab-ci/default.yml
+++ b/.gitlab-ci/default.yml
@@ -10,35 +10,38 @@ workflow:
   rules:
     - if: $CI_PIPELINE_SOURCE=="parent_pipeline"
 
+# variables that may be overwritten by the trigger
 variables:
-  TRIGGER_SOURCE: "unknown"
-  MR_TARGET_BRANCH_NAME: "master"
+  TRIGGER_SOURCE: "undefined"
+  MR_TARGET_BRANCH_NAME: "undefined"
 
 select tests:
   stage: configure
   script:
     - |
+      dunecontrol --opts=$DUNE_OPTS_FILE --current all
       if [[ "$TRIGGER_SOURCE" == "merge_request_event" ]]; then
-          dunecontrol --opts=$DUNE_OPTS_FILE --current all
-          pushd build-cmake
-            python3 ../bin/testing/findtests.py -f ../affectedtests.json -t origin/$MR_TARGET_BRANCH_NAME
-          popd
+          echo "Detecting changes w.r.t to target branch '$MR_TARGET_BRANCH_NAME'"
+          python3 bin/testing/getchangedfiles.py -o changedfiles.txt -t origin/$MR_TARGET_BRANCH_NAME
+          python3 bin/testing/findtests.py -o affectedtests.json --file-list changedfiles.txt --build-dir build-cmake
       else
+          echo "Received '$TRIGGER_SOURCE' as pipeline trigger event"
           echo "Skipping test selection, build/test stages will consider all tests!"
-          echo "{}" >> ../affectedtests.json
+          touch affectedtests.json
       fi
   artifacts:
     paths:
+      - build-cmake
       - affectedtests.json
     expire_in: 3 hours
 
 build dumux:
   stage: build
   script:
-    - dunecontrol --opts=$DUNE_OPTS_FILE --current all
     - |
       pushd build-cmake
-        if [[ "$TRIGGER_SOURCE" == "merge_request_event" ]]; then
+        make clean && make all
+        if [ -s ../affectedtests.json ]; then
           python3 ../bin/testing/runselectedtests.py -c ../affectedtests.json -b
         else
           python3 ../bin/testing/runselectedtests.py --all -b
@@ -61,7 +64,7 @@ test dumux:
   script:
     - |
       pushd build-cmake
-        if [[ "$TRIGGER_SOURCE" == "merge_request_event" ]]; then
+        if [ -s ../affectedtests.json ]; then
           python3 ../bin/testing/runselectedtests.py -c ../affectedtests.json -t
         else
           python3 ../bin/testing/runselectedtests.py --all -t
diff --git a/bin/testing/findtests.py b/bin/testing/findtests.py
index f9a810522d6e49a9cd441d2c1435abd891ddaaaa..81e0fa256e7649f4ed036bf2013db57461989ae1 100755
--- a/bin/testing/findtests.py
+++ b/bin/testing/findtests.py
@@ -25,40 +25,36 @@ def hasCommonMember(myset, mylist):
 
 
 # make dry run and return the compilation command
-def getCompileCommand(testConfig):
-    lines = subprocess.check_output(["make", "--dry-run",
-                                     testConfig["target"]],
-                                    encoding='ascii').splitlines()
+def getCompileCommand(testConfig, buildTreeRoot='.'):
+    target = testConfig['target']
+    lines = subprocess.check_output(["make", "-B", "--dry-run", target],
+                                    encoding='ascii',
+                                    cwd=buildTreeRoot).splitlines()
 
     def hasCppCommand(line):
         return any(cpp in line for cpp in ['g++', 'clang++'])
 
+    # there may be library build commands first, last one is the actual target
     commands = list(filter(lambda line: hasCppCommand(line), lines))
-    assert len(commands) <= 1
-    return commands[0] if commands else None
+    return commands[-1] if commands else None
 
 
 # get the command and folder to compile the given test
-def buildCommandAndDir(testConfig, cache):
-    compCommand = getCompileCommand(testConfig)
+def buildCommandAndDir(testConfig, buildTreeRoot='.'):
+    compCommand = getCompileCommand(testConfig, buildTreeRoot)
     if compCommand is None:
-        with open(cache) as c:
-            data = json.load(c)
-            return data["command"], data["dir"]
+        raise Exception("Could not determine compile command for {}".format(testConfig))
     else:
         (_, dir), command = [comm.split() for comm in compCommand.split("&&")]
-        with open(cache, "w") as c:
-            json.dump({"command": command, "dir": dir}, c)
         return command, dir
 
 
 # check if a test is affected by changes in the given files
-def isAffectedTest(testConfigFile, changedFiles):
+def isAffectedTest(testConfigFile, changedFiles, buildTreeRoot='.'):
     with open(testConfigFile) as configFile:
         testConfig = json.load(configFile)
 
-    cacheFile = "TestTargets/" + testConfig["target"] + ".json"
-    command, dir = buildCommandAndDir(testConfig, cacheFile)
+    command, dir = buildCommandAndDir(testConfig, buildTreeRoot)
     mainFile = command[-1]
 
     # detect headers included in this test
@@ -68,19 +64,10 @@ def isAffectedTest(testConfigFile, changedFiles):
     headers = subprocess.run(command + ["-MM", "-H"],
                              stderr=PIPE, stdout=PIPE, cwd=dir,
                              encoding='ascii').stderr.splitlines()
+    headers = [h.lstrip('. ') for h in headers]
+    headers.append(mainFile)
 
-    # filter only headers from this project and turn them into relative paths
-    projectDir = os.path.abspath(os.getcwd().rstrip("build-cmake"))
-
-    def isProjectHeader(headerPath):
-        return projectDir in headerPath
-
-    testFiles = [os.path.relpath(mainFile.lstrip(". "), projectDir)]
-    testFiles.extend([os.path.relpath(header.lstrip(". "), projectDir)
-                      for header in filter(isProjectHeader, headers)])
-    testFiles = set(testFiles)
-
-    if hasCommonMember(changedFiles, testFiles):
+    if hasCommonMember(changedFiles, headers):
         return True, testConfig["name"], testConfig["target"]
 
     return False, testConfig["name"], testConfig["target"]
@@ -90,41 +77,37 @@ if __name__ == '__main__':
 
     # parse input arguments
     parser = ArgumentParser(description='Find tests affected by changes')
-    parser.add_argument('-s', '--source',
-                        required=False, default='HEAD',
-                        help='The source tree (default: `HEAD`)')
-    parser.add_argument('-t', '--target',
-                        required=False, default='master',
-                        help='The tree to compare against (default: `master`)')
+    parser.add_argument('-l', '--file-list', required=True,
+                        help='A file containing a list of files that changed')
     parser.add_argument('-np', '--num-processes',
                         required=False, type=int, default=4,
                         help='Number of processes (default: 4)')
-    parser.add_argument('-f', '--outfile',
+    parser.add_argument('-o', '--outfile',
                         required=False, default='affectedtests.json',
                         help='The file in which to write the affected tests')
+    parser.add_argument('-b', '--build-dir',
+                        required=False, default='.',
+                        help='The path to the top-level build directory of the project to be checked')
     args = vars(parser.parse_args())
 
-    # find the changes files
-    changedFiles = subprocess.check_output(
-        ["git", "diff-tree", "-r", "--name-only", args['source'],  args['target']],
-        encoding='ascii'
-    ).splitlines()
-    changedFiles = set(changedFiles)
+    buildDir = os.path.abspath(args['build_dir'])
+    targetFile = os.path.abspath(args['outfile'])
+    with open(args['file_list']) as files:
+        changedFiles = set([line.strip('\n') for line in files.readlines()])
 
     # clean build directory
-    subprocess.run(["make", "clean"])
-    subprocess.run(["make"])
-
-    # create cache folder
-    os.makedirs("TestTargets", exist_ok=True)
+    subprocess.run(["make", "clean"], cwd=buildDir)
+    subprocess.run(["make", "all"], cwd=buildDir)
 
     # detect affected tests
     print("Detecting affected tests:")
     affectedTests = {}
-    tests = glob("TestMetaData/*json")
+    tests = glob(os.path.join(buildDir, "TestMetaData") + "/*json")
 
     numProcesses = max(1, args['num_processes'])
-    findAffectedTest = partial(isAffectedTest, changedFiles=changedFiles)
+    findAffectedTest = partial(isAffectedTest,
+                               changedFiles=changedFiles,
+                               buildTreeRoot=buildDir)
     with Pool(processes=numProcesses) as p:
         for affected, name, target in p.imap_unordered(findAffectedTest, tests, chunksize=4):
             if affected:
@@ -133,5 +116,5 @@ if __name__ == '__main__':
 
     print("Detected {} affected tests".format(len(affectedTests)))
 
-    with open(args['outfile'], 'w') as jsonFile:
+    with open(targetFile, 'w') as jsonFile:
         json.dump(affectedTests, jsonFile)
diff --git a/bin/testing/getchangedfiles.py b/bin/testing/getchangedfiles.py
new file mode 100644
index 0000000000000000000000000000000000000000..69651b7c2b94f2cb1dcffa6852cc01a4763de485
--- /dev/null
+++ b/bin/testing/getchangedfiles.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+
+"""
+Get the names of the files that differ between two git trees
+"""
+
+import os
+import subprocess
+from argparse import ArgumentParser
+
+
+def getCommandOutput(command, cwd=None):
+    return subprocess.check_output(command, encoding='ascii', cwd=cwd)
+
+
+# get the files that differ between two trees in a git repo
+def getChangedFiles(gitFolder, sourceTree, targetTree):
+
+    gitFolder = os.path.abspath(gitFolder)
+    root = getCommandOutput(
+        command=['git', 'rev-parse', '--show-toplevel'],
+        cwd=gitFolder
+    ).strip('\n')
+    changedFiles = getCommandOutput(
+        command=["git", "diff-tree", "-r", "--name-only", sourceTree, targetTree],
+        cwd=gitFolder
+    ).splitlines()
+
+    return [os.path.join(root, file) for file in changedFiles]
+
+
+if __name__ == '__main__':
+
+    # parse input arguments
+    parser = ArgumentParser(
+        description='Get the files that differ between two git-trees'
+    )
+    parser.add_argument('-f', '--folder',
+                        required=False, default='.',
+                        help='The path to a folder within the git repository')
+    parser.add_argument('-s', '--source-tree',
+                        required=False, default='HEAD',
+                        help='The source tree (default: `HEAD`)')
+    parser.add_argument('-t', '--target-tree',
+                        required=False, default='master',
+                        help='The tree to compare against (default: `master`)')
+    parser.add_argument('-o', '--outfile',
+                        required=False, default='changedfiles.txt',
+                        help='The file in which to write the changed files')
+    args = vars(parser.parse_args())
+
+    changedFiles = getChangedFiles(args['folder'],
+                                   args['source_tree'],
+                                   args['target_tree'])
+
+    with open(args['outfile'], 'w') as outFile:
+        for file in changedFiles:
+            outFile.write(f"{os.path.abspath(file)}\n")