Merge "Remove unused linenoise"
diff --git a/.zuul.yaml b/.zuul.yaml
index b6e739e..6f09b90 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -9,3 +9,5 @@
             requires: CzechLight-deps-f29-gcc-asan-ubsan
         - f29-clang-asan:
             requires: CzechLight-deps-f29-clang-asan
+        - f29-clang-tsan:
+            requires: CzechLight-deps-f29-clang-tsan
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b33d1e6..c9f9919 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -127,25 +127,17 @@
 include(CTest)
 if(BUILD_TESTING)
     enable_testing()
-    find_path(TROMPELOEIL_PATH trompeloeil.hpp PATH_SUFFIXES trompeloeil/)
-    if("${TROMPELOEIL_PATH}" STREQUAL "TROMPELOEIL_PATH-NOTFOUND")
-        message(FATAL_ERROR "Cannot find the \"trompeloeil.hpp\" file provided by <https://github.com/rollbear/trompeloeil>. "
-            "Please set TROMPELOEIL_PATH to where it is available.")
-    endif()
-
-    find_path(CATCH_PATH catch.hpp PATH_SUFFIXES catch/)
-    if("${CATCH_PATH}" STREQUAL "CATCH_PATH-NOTFOUND")
-        message(FATAL_ERROR "Cannot find the \"catch.hpp\" file provided by <http://catch-lib.net/>. "
-            "Please set CATCH_PATH to where it is available.")
-    endif()
+    find_package(trompeloeil 33 REQUIRED)
+    find_package(Catch2 2.7.0 REQUIRED)
 
     add_library(TestCatchIntegration STATIC
         tests/catch_integration.cpp
         tests/trompeloeil_catch.h
+        tests/wait-a-bit-longer.cpp
         )
-    target_include_directories(TestCatchIntegration SYSTEM PUBLIC ${TROMPELOEIL_PATH} ${CATCH_PATH})
     target_include_directories(TestCatchIntegration PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests/ ${CMAKE_CURRENT_SOURCE_DIR}/src/)
-    target_link_libraries(TestCatchIntegration spdlog::spdlog)
+    target_link_libraries(TestCatchIntegration Catch2::Catch2 trompeloeil spdlog::spdlog)
+    target_compile_definitions(TestCatchIntegration PUBLIC CATCH_CONFIG_FAST_COMPILE)
 
     if (NOT SYSREPOCTL_EXECUTABLE)
         find_program(SYSREPOCTL_EXECUTABLE sysrepoctl)
diff --git a/ci/build.sh b/ci/build.sh
index aea3316..5438b0a 100755
--- a/ci/build.sh
+++ b/ci/build.sh
@@ -33,6 +33,12 @@
     export LDFLAGS="-fsanitize=address ${LDFLAGS}"
 fi
 
+if [[ $ZUUL_JOB_NAME =~ .*-tsan ]]; then
+    export CFLAGS="-fsanitize=thread ${CFLAGS}"
+    export CXXFLAGS="-fsanitize=thread ${CXXFLAGS}"
+    export LDFLAGS="-fsanitize=thread ${LDFLAGS}"
+fi
+
 PREFIX=~/target
 mkdir ${PREFIX}
 BUILD_DIR=~/build
diff --git a/submodules/dependencies b/submodules/dependencies
index 037b667..2caa878 160000
--- a/submodules/dependencies
+++ b/submodules/dependencies
@@ -1 +1 @@
-Subproject commit 037b6672918865849be625c3ae44fce044b9e070
+Subproject commit 2caa878e9df4a48159d9628a9f0832bb45ffcbfc
diff --git a/tests/catch_integration.cpp b/tests/catch_integration.cpp
index b3143fb..e1b18db 100644
--- a/tests/catch_integration.cpp
+++ b/tests/catch_integration.cpp
@@ -1,2 +1,29 @@
 #define CATCH_CONFIG_MAIN
-#include <catch.hpp>
+#include <catch2/catch.hpp>
+#include <sstream>
+#include <trompeloeil.hpp>
+
+namespace trompeloeil
+  {
+    template <>
+    void reporter<specialized>::send(
+      severity s,
+      const char* file,
+      unsigned long line,
+      const char* msg)
+    {
+      std::ostringstream os;
+      if (line) os << file << ':' << line << '\n';
+      os << msg;
+      auto failure = os.str();
+      if (s == severity::fatal)
+      {
+        FAIL(failure);
+      }
+      else
+      {
+        CAPTURE(failure);
+        CHECK(failure.empty());
+      }
+    }
+  }
diff --git a/tests/cd.cpp b/tests/cd.cpp
index 71eb6bb..44d87dd 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -291,6 +291,6 @@
                 input = "cd example:twoKeyList[number=4][name=abcd]";
             }
         }
-        REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException&);
+        REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException);
     }
 }
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index 274d171..f18af1c 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -183,6 +183,6 @@
                 input = "set leafBinary db=ahj";
         }
 
-        REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException&);
+        REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException);
     }
 }
diff --git a/tests/ls.cpp b/tests/ls.cpp
index a19add2..3e8fd52 100644
--- a/tests/ls.cpp
+++ b/tests/ls.cpp
@@ -202,6 +202,6 @@
                 input = "lssecond:a";
         }
 
-        REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException&);
+        REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException);
     }
 }
diff --git a/tests/presence_containers.cpp b/tests/presence_containers.cpp
index 6872d1b..91b3e95 100644
--- a/tests/presence_containers.cpp
+++ b/tests/presence_containers.cpp
@@ -106,7 +106,7 @@
             input = "list[quote='lol']";
         }
 
-        REQUIRE_THROWS_AS(parser.parseCommand("create " + input, errorStream), InvalidCommandException&);
-        REQUIRE_THROWS_AS(parser.parseCommand("delete " + input, errorStream), InvalidCommandException&);
+        REQUIRE_THROWS_AS(parser.parseCommand("create " + input, errorStream), InvalidCommandException);
+        REQUIRE_THROWS_AS(parser.parseCommand("delete " + input, errorStream), InvalidCommandException);
     }
 }
diff --git a/tests/trompeloeil_catch.h b/tests/trompeloeil_catch.h
index c6d1131..3de799b 100644
--- a/tests/trompeloeil_catch.h
+++ b/tests/trompeloeil_catch.h
@@ -1,67 +1,8 @@
 #pragma once
 
-#ifdef REQUIRE
-# error This file needs to be included prior to including anything from Catch.
-#endif
-
-// clang-format off
-
-#include <ostream>
-#include <thread>
-#include <catch.hpp>
+#include <catch2/catch.hpp>
 #include <trompeloeil.hpp>
 
-// this is copy-paste from https://github.com/rollbear/trompeloeil/blob/master/docs/CookBook.md/#unit_test_frameworks
-namespace trompeloeil {
+extern template struct trompeloeil::reporter<trompeloeil::specialized>;
 
-/** @short Pass reports from the Trompeloeil mocker to Catch for processing as test failures */
-template <>
-struct reporter<trompeloeil::specialized>
-{
-  static void send(trompeloeil::severity s,
-                   const char* file,
-                   unsigned long line,
-                   const char* msg)
-  {
-    std::ostringstream os;
-    if (line) os << file << ':' << line << '\n';
-    os << msg;
-    auto failure = os.str();
-    if (s == severity::fatal)
-    {
-      FAIL(failure);
-    }
-    else
-    {
-      CAPTURE(failure);
-      CHECK(failure.empty());
-    }
-  }
-};
-}
-
-/** @short Wait until a given sequence of expectation is matched, and then a bit more to ensure that there's silence afterwards */
-void waitForCompletionAndBitMore(const trompeloeil::sequence& seq)
-{
-    using namespace std::literals;
-    using clock = std::chrono::steady_clock;
-
-    // We're busy-waiting a bit
-    const auto waitingStep = 30ms;
-    // Timeout after this much
-    const auto completionTimeout = 5000ms;
-    // When checking for silence afterwards, wait at least this long.
-    // We'll also wait as long as it originally took to process everything.
-    const auto minExtraWait = 100ms;
-
-    auto start = clock::now();
-    while (!seq.is_completed()) {
-        std::this_thread::sleep_for(waitingStep);
-        if (clock::now() - start > completionTimeout) {
-            break;
-        }
-    }
-    REQUIRE(seq.is_completed());
-    auto duration = std::chrono::duration<double>(clock::now() - start);
-    std::this_thread::sleep_for(std::max(duration, decltype(duration)(minExtraWait)));
-}
+void waitForCompletionAndBitMore(const trompeloeil::sequence& seq);
diff --git a/tests/wait-a-bit-longer.cpp b/tests/wait-a-bit-longer.cpp
new file mode 100644
index 0000000..029e36f
--- /dev/null
+++ b/tests/wait-a-bit-longer.cpp
@@ -0,0 +1,30 @@
+#include <catch2/catch.hpp>
+#include <chrono>
+#include <thread>
+#include <trompeloeil.hpp>
+
+/** @short Wait until a given sequence of expectation is matched, and then a bit more to ensure that there's silence afterwards */
+void waitForCompletionAndBitMore(const trompeloeil::sequence& seq)
+{
+    using namespace std::literals;
+    using clock = std::chrono::steady_clock;
+
+    // We're busy-waiting a bit
+    const auto waitingStep = 30ms;
+    // Timeout after this much
+    const auto completionTimeout = 5000ms;
+    // When checking for silence afterwards, wait at least this long.
+    // We'll also wait as long as it originally took to process everything.
+    const auto minExtraWait = 100ms;
+
+    auto start = clock::now();
+    while (!seq.is_completed()) {
+        std::this_thread::sleep_for(waitingStep);
+        if (clock::now() - start > completionTimeout) {
+            break;
+        }
+    }
+    REQUIRE(seq.is_completed());
+    auto duration = std::chrono::duration<double>(clock::now() - start);
+    std::this_thread::sleep_for(std::max(duration, decltype(duration)(minExtraWait)));
+}