diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp
index e11e60ae0c8b72ef111e1bc64c69ffe931d98621..1965a9966cafdf181b366feb1e41909077edd8ac 100644
--- a/src/algebra/LinearSolver.cpp
+++ b/src/algebra/LinearSolver.cpp
@@ -24,9 +24,11 @@ struct LinearSolver::Internals
       return false;
 #endif
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("Linear system library (" + ::name(library) + ") was not set!");
     }
+      // LCOV_EXCL_STOP
     }
   }
 
@@ -34,7 +36,9 @@ struct LinearSolver::Internals
   checkHasLibrary(const LSLibrary library)
   {
     if (not hasLibrary(library)) {
+      // LCOV_EXCL_START
       throw NormalError(::name(library) + " is not linked to pugs. Cannot use it!");
+      // LCOV_EXCL_STOP
     }
   }
 
@@ -64,9 +68,11 @@ struct LinearSolver::Internals
     case LSMethod::choleski: {
       break;
     }
+      // LCOV_EXCL_START
     default: {
       throw NormalError(name(method) + " is not a builtin linear solver!");
     }
+      // LCOV_EXCL_STOP
     }
   }
 
@@ -94,9 +100,11 @@ struct LinearSolver::Internals
     case LSPrecond::incomplete_LU: {
       break;
     }
+      // LCOV_EXCL_START
     default: {
       throw NormalError(name(precond) + " is not a PETSc preconditioner!");
     }
+      // LCOV_EXCL_STOP
     }
   }
 
@@ -114,9 +122,11 @@ struct LinearSolver::Internals
       checkPETScPrecond(options.precond());
       break;
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("undefined options compatibility for this library (" + ::name(options.library()) + ")!");
     }
+      // LCOV_EXCL_STOP
     }
   }
 
@@ -127,7 +137,9 @@ struct LinearSolver::Internals
                           const LinearSolverOptions& options)
   {
     if (options.precond() != LSPrecond::none) {
+      // LCOV_EXCL_START
       throw UnexpectedError("builtin linear solver do not allow any preconditioner!");
+      // LCOV_EXCL_STOP
     }
     switch (options.method()) {
     case LSMethod::cg: {
@@ -138,14 +150,15 @@ struct LinearSolver::Internals
       BiCGStab{A, x, b, options.epsilon(), options.maximumIteration(), options.verbose()};
       break;
     }
+      // LCOV_EXCL_START
     default: {
       throw NotImplementedError("undefined builtin method: " + name(options.method()));
     }
+      // LCOV_EXCL_STOP
     }
   }
 
 #ifdef PUGS_HAS_PETSC
-
   static int
   petscMonitor(KSP, int i, double residu, void*)
   {
@@ -238,9 +251,11 @@ struct LinearSolver::Internals
       direct_solver = true;
       break;
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("unexpected method: " + name(options.method()));
     }
+      // LCOV_EXCL_STOP
     }
 
     if (not direct_solver) {
@@ -265,9 +280,11 @@ struct LinearSolver::Internals
         PCSetType(pc, PCNONE);
         break;
       }
+        // LCOV_EXCL_START
       default: {
         throw UnexpectedError("unexpected preconditioner: " + name(options.precond()));
       }
+        // LCOV_EXCL_STOP
       }
     }
     if (options.verbose()) {
@@ -278,7 +295,9 @@ struct LinearSolver::Internals
     MatDestroy(&petscMat);
   }
 
-#else
+#else   // PUGS_HAS_PETSC
+
+  // LCOV_EXCL_START
   static void
   petscSolveLocalSystem(const CRSMatrix<double, size_t>&,
                         Vector<double>&,
@@ -288,6 +307,7 @@ struct LinearSolver::Internals
     checkHasLibrary(LSLibrary::petsc);
     throw UnexpectedError("unexpected situation should not reach this point!");
   }
+  // LCOV_EXCL_STOP
 
 #endif   // PUGS_HAS_PETSC
 };
@@ -298,6 +318,12 @@ LinearSolver::hasLibrary(LSLibrary library) const
   return Internals::hasLibrary(library);
 }
 
+void
+LinearSolver::checkOptions(const LinearSolverOptions& options) const
+{
+  Internals::checkOptions(options);
+}
+
 void
 LinearSolver::solveLocalSystem(const CRSMatrix<double, size_t>& A, Vector<double>& x, const Vector<double>& b)
 {
@@ -306,13 +332,18 @@ LinearSolver::solveLocalSystem(const CRSMatrix<double, size_t>& A, Vector<double
     Internals::builtinSolveLocalSystem(A, x, b, m_options);
     break;
   }
+    // LCOV_EXCL_START
   case LSLibrary::petsc: {
+    // not covered since if PETSc is not linked this point is
+    // unreachable: LinearSolver throws an exception at construction
+    // in this case.
     Internals::petscSolveLocalSystem(A, x, b, m_options);
     break;
   }
   default: {
     throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems for sparse matrices");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
diff --git a/src/algebra/LinearSolver.hpp b/src/algebra/LinearSolver.hpp
index b72b09b0f6a3255f914c96ee3df7717923931294..3ed5660bd9ce51c1e5ba5de3fe00c595aa96f1e7 100644
--- a/src/algebra/LinearSolver.hpp
+++ b/src/algebra/LinearSolver.hpp
@@ -19,6 +19,7 @@ class LinearSolver
 
  public:
   bool hasLibrary(LSLibrary library) const;
+  void checkOptions(const LinearSolverOptions& options) const;
 
   void solveLocalSystem(const CRSMatrix<double, size_t>& A, Vector<double>& x, const Vector<double>& b);
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 61a2b661986a3b0ab2d8484bdd38779e197b28c0..aaf4bfbad904a6466b4442b0d99f35a41840ddb3 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -73,6 +73,7 @@ add_executable (unit_tests
   test_IncDecExpressionProcessor.cpp
   test_INodeProcessor.cpp
   test_ItemType.cpp
+  test_LinearSolver.cpp
   test_LinearSolverOptions.cpp
   test_ListAffectationProcessor.cpp
   test_MathModule.cpp
diff --git a/tests/test_LinearSolver.cpp b/tests/test_LinearSolver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f56bb59ed1d6b7df2e81d8e4076f0ee23bb27ec6
--- /dev/null
+++ b/tests/test_LinearSolver.cpp
@@ -0,0 +1,527 @@
+#include <catch2/catch.hpp>
+
+#include <utils/pugs_config.hpp>
+
+#include <algebra/LinearSolver.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("LinearSolver", "[algebra]")
+{
+  SECTION("check has library")
+  {
+    LinearSolver linear_solver;
+
+    REQUIRE(linear_solver.hasLibrary(LSLibrary::builtin) == true);
+
+#ifdef PUGS_HAS_PETSC
+    REQUIRE(linear_solver.hasLibrary(LSLibrary::petsc) == true);
+#else    // PUGS_HAS_PETSC
+    REQUIRE(linear_solver.hasLibrary(LSLibrary::petsc) == false);
+#endif   // PUGS_HAS_PETSC
+  }
+
+  SECTION("check linear solver building")
+  {
+    LinearSolverOptions options;
+
+    SECTION("builtin")
+    {
+      SECTION("builtin methods")
+      {
+        options.library() = LSLibrary::builtin;
+        options.precond() = LSPrecond::none;
+
+        options.method() = LSMethod::cg;
+        REQUIRE_NOTHROW(LinearSolver{options});
+
+        options.method() = LSMethod::bicgstab;
+        REQUIRE_NOTHROW(LinearSolver{options});
+
+        options.method() = LSMethod::bicgstab2;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: BICGStab2 is not a builtin linear solver!");
+
+        options.method() = LSMethod::lu;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: LU is not a builtin linear solver!");
+
+        options.method() = LSMethod::choleski;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Choleski is not a builtin linear solver!");
+
+        options.method() = LSMethod::gmres;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: GMRES is not a builtin linear solver!");
+      }
+
+      SECTION("builtin precond")
+      {
+        options.library() = LSLibrary::builtin;
+        options.method()  = LSMethod::cg;
+
+        options.precond() = LSPrecond::none;
+        REQUIRE_NOTHROW(LinearSolver{options});
+
+        options.precond() = LSPrecond::diagonal;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: diagonal is not a builtin preconditioner!");
+
+        options.precond() = LSPrecond::incomplete_LU;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: ILU is not a builtin preconditioner!");
+
+        options.precond() = LSPrecond::incomplete_choleski;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: ICholeski is not a builtin preconditioner!");
+
+        options.precond() = LSPrecond::amg;
+        REQUIRE_THROWS_WITH(LinearSolver{options}, "error: AMG is not a builtin preconditioner!");
+      }
+    }
+
+    SECTION("PETSc")
+    {
+      LinearSolverOptions always_valid;
+      always_valid.library() = LSLibrary::builtin;
+      always_valid.method()  = LSMethod::cg;
+      always_valid.precond() = LSPrecond::none;
+
+      LinearSolver linear_solver{always_valid};
+
+      SECTION("PETSc methods")
+      {
+        options.library() = LSLibrary::petsc;
+        options.precond() = LSPrecond::none;
+
+        options.method() = LSMethod::cg;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::bicgstab;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::bicgstab2;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::lu;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::choleski;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::gmres;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+      }
+
+      SECTION("builtin precond")
+      {
+        options.library() = LSLibrary::petsc;
+        options.method()  = LSMethod::cg;
+
+        options.precond() = LSPrecond::none;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::diagonal;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::incomplete_LU;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::incomplete_choleski;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::amg;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+      }
+    }
+
+#ifndef PUGS_HAS_PETSC
+    SECTION("not linked PETSc")
+    {
+      options.library() = LSLibrary::petsc;
+      options.method()  = LSMethod::cg;
+      options.precond() = LSPrecond::none;
+
+      REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!");
+    }
+#endif   // PUGS_HAS_PETSC
+  }
+
+  SECTION("check linear solvers")
+  {
+    SECTION("symmetric system")
+    {
+      SparseMatrixDescriptor S{5};
+      S(0, 0) = 2;
+      S(0, 1) = -1;
+
+      S(1, 0) = -1;
+      S(1, 1) = 2;
+      S(1, 2) = -1;
+
+      S(2, 1) = -1;
+      S(2, 2) = 2;
+      S(2, 3) = -1;
+
+      S(3, 2) = -1;
+      S(3, 3) = 2;
+      S(3, 4) = -1;
+
+      S(4, 3) = -1;
+      S(4, 4) = 2;
+
+      CRSMatrix A{S};
+
+      Vector<const double> x_exact = [] {
+        Vector<double> y{5};
+        y[0] = 1;
+        y[1] = 3;
+        y[2] = 2;
+        y[3] = 4;
+        y[4] = 5;
+        return y;
+      }();
+
+      Vector<double> b = A * x_exact;
+
+      SECTION("builtin")
+      {
+        SECTION("CG no preconditioner")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::builtin;
+          options.method()  = LSMethod::cg;
+          options.precond() = LSPrecond::none;
+
+          Vector<double> x{5};
+          x = 0;
+
+          LinearSolver solver{options};
+
+          solver.solveLocalSystem(A, x, b);
+          Vector error = x - x_exact;
+          REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+        }
+      }
+
+      SECTION("PETSc")
+      {
+#ifdef PUGS_HAS_PETSC
+
+        SECTION("CG")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::cg;
+          options.precond() = LSPrecond::none;
+          options.verbose() = true;
+
+          SECTION("CG no preconditioner")
+          {
+            options.precond() = LSPrecond::none;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("CG Diagonal")
+          {
+            options.precond() = LSPrecond::diagonal;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("CG ICholeski")
+          {
+            options.precond() = LSPrecond::incomplete_choleski;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("CG AMG")
+          {
+            options.precond() = LSPrecond::amg;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+        }
+
+        SECTION("Choleski")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::choleski;
+          options.precond() = LSPrecond::none;
+
+          Vector<double> x{5};
+          x = 0;
+
+          LinearSolver solver{options};
+
+          solver.solveLocalSystem(A, x, b);
+          Vector error = x - x_exact;
+          REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+        }
+
+#else    // PUGS_HAS_PETSC
+        SECTION("PETSc not linked")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::cg;
+          options.precond() = LSPrecond::none;
+
+          REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!");
+        }
+#endif   // PUGS_HAS_PETSC
+      }
+    }
+
+    SECTION("none symmetric system")
+    {
+      SparseMatrixDescriptor S{5};
+      S(0, 0) = 2;
+      S(0, 1) = -1;
+
+      S(1, 0) = -0.2;
+      S(1, 1) = 2;
+      S(1, 2) = -1;
+
+      S(2, 1) = -1;
+      S(2, 2) = 4;
+      S(2, 3) = -2;
+
+      S(3, 2) = -1;
+      S(3, 3) = 2;
+      S(3, 4) = -0.1;
+
+      S(4, 3) = 1;
+      S(4, 4) = 3;
+
+      CRSMatrix A{S};
+
+      Vector<const double> x_exact = [] {
+        Vector<double> y{5};
+        y[0] = 1;
+        y[1] = 3;
+        y[2] = 2;
+        y[3] = 4;
+        y[4] = 5;
+        return y;
+      }();
+
+      Vector<double> b = A * x_exact;
+
+      SECTION("builtin")
+      {
+        SECTION("BICGStab no preconditioner")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::builtin;
+          options.method()  = LSMethod::bicgstab;
+          options.precond() = LSPrecond::none;
+
+          Vector<double> x{5};
+          x = 0;
+
+          LinearSolver solver{options};
+
+          solver.solveLocalSystem(A, x, b);
+          Vector error = x - x_exact;
+          REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+        }
+      }
+
+      SECTION("PETSc")
+      {
+#ifdef PUGS_HAS_PETSC
+
+        SECTION("BICGStab")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::bicgstab;
+          options.precond() = LSPrecond::none;
+          options.verbose() = true;
+
+          SECTION("BICGStab no preconditioner")
+          {
+            options.precond() = LSPrecond::none;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("BICGStab Diagonal")
+          {
+            options.precond() = LSPrecond::diagonal;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("BICGStab ILU")
+          {
+            options.precond() = LSPrecond::incomplete_LU;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+        }
+
+        SECTION("BICGStab2")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::bicgstab2;
+          options.precond() = LSPrecond::none;
+
+          SECTION("BICGStab2 no preconditioner")
+          {
+            options.precond() = LSPrecond::none;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("BICGStab2 Diagonal")
+          {
+            options.precond() = LSPrecond::diagonal;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+        }
+
+        SECTION("GMRES")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::gmres;
+          options.precond() = LSPrecond::none;
+
+          SECTION("GMRES no preconditioner")
+          {
+            options.precond() = LSPrecond::none;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("GMRES Diagonal")
+          {
+            options.precond() = LSPrecond::diagonal;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+
+          SECTION("GMRES ILU")
+          {
+            options.precond() = LSPrecond::incomplete_LU;
+
+            Vector<double> x{5};
+            x = 0;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+          }
+        }
+
+        SECTION("LU")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::lu;
+          options.precond() = LSPrecond::none;
+
+          Vector<double> x{5};
+          x = 0;
+
+          LinearSolver solver{options};
+
+          solver.solveLocalSystem(A, x, b);
+          Vector error = x - x_exact;
+          REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x_exact, x_exact)));
+        }
+
+#else    // PUGS_HAS_PETSC
+        SECTION("PETSc not linked")
+        {
+          LinearSolverOptions options;
+          options.library() = LSLibrary::petsc;
+          options.method()  = LSMethod::cg;
+          options.precond() = LSPrecond::none;
+
+          REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!");
+        }
+#endif   // PUGS_HAS_PETSC
+      }
+    }
+  }
+}