diff --git a/HACKING b/HACKING
index 35fdb70fe01aae676911ad58a6d3d19e446292a6..0d66b0a631849c1f6d8a09fea3de5b53f2dcb039 100644
--- a/HACKING
+++ b/HACKING
@@ -11,6 +11,21 @@ As most of the API currently doesn't have documentation comments, this is mainly
 useful for getting hyperlinked `.mli`-files. But please feel free to add more
 comments.
 
+== Tests
+
+There are two sets of tests: Integration tests in `src/testsuite` and unit tests
+in `src/unit-tests`. The former can be used to test the whole reasoner on
+example formulas with known satisfiability, the latter is for individual
+functions.
+
+To run all tests, run `make test`. You will need to install the additional
+dependency `ounit`. The individual test suites can be run with
+`./cool-testsuite.native` and `./cool_unit_tests.native` respectively.
+
+If unit tests are not being build, use
+
+  ocaml setup.ml -configure --enable-tests
+
 Adding another Functor
 ----------------------
 
diff --git a/Makefile b/Makefile
index 8adf527eae31067ba9dfd11cde9904161eadaa59..3bfc7f64fdf813be52ef3b18b7c0becfcae46973 100644
--- a/Makefile
+++ b/Makefile
@@ -1,16 +1,44 @@
+# OASIS_START
+# DO NOT EDIT (digest: a3c674b4239234cbbe53afe090018954)
 
-.PHONY: all clean
+SETUP = ocaml setup.ml
+
+build: setup.data
+	$(SETUP) -build $(BUILDFLAGS)
+
+doc: setup.data build
+	$(SETUP) -doc $(DOCFLAGS)
+
+test: setup.data build
+	$(SETUP) -test $(TESTFLAGS)
 
 all: setup.ml
-	ocaml setup.ml -build
+	$(SETUP) -all $(ALLFLAGS)
+
+install: setup.data
+	$(SETUP) -install $(INSTALLFLAGS)
+
+uninstall: setup.data
+	$(SETUP) -uninstall $(UNINSTALLFLAGS)
+
+reinstall: setup.data
+	$(SETUP) -reinstall $(REINSTALLFLAGS)
+
+clean: setup.ml
+	$(SETUP) -clean $(CLEANFLAGS)
+
+distclean: setup.ml
+	$(SETUP) -distclean $(DISTCLEANFLAGS)
+
+setup.data: setup.ml
+	$(SETUP) -configure $(CONFIGUREFLAGS)
+
+configure: setup.ml
+	$(SETUP) -configure $(CONFIGUREFLAGS)
 
 setup.ml: _oasis
 	oasis setup
-	ocaml setup.ml -configure
-
-clean:
-	! [ -f setup.ml ] || ocaml setup.ml -clean
-	oasis setup-clean
-	$(RM) -f _tags
-	$(RM) -f setup.data setup.ml
-	$(RM) -f myocamlbuild.ml
+
+.PHONY: build doc test all install uninstall reinstall clean distclean configure
+
+# OASIS_STOP
diff --git a/_oasis b/_oasis
index ff624cdb8c1733a092799361c402e95805327c6c..ef192680963de7ef70d2a361982f3ccfb3ef79f0 100644
--- a/_oasis
+++ b/_oasis
@@ -1,4 +1,5 @@
 OASISFormat:    0.4
+BuildTools:     ocamlbuild
 Name:           cool
 Version:        0.1
 Synopsis:       Coalgebraic Ontology Logic solver
@@ -100,6 +101,25 @@ Executable testsuite
   if flag(static)
     CCLib:            -static
 
+Test testsuite
+  TestTools:        testsuite
+  Command:          $testsuite
+
+Executable unit_tests
+  Build$: flag(tests)
+  CompiledObject:   native
+  Path:             src/unit-tests/
+  BuildTools:       ocamlbuild
+  MainIs:           cool_unit_tests.ml
+  BuildDepends:     libcool, oUnit
+  if flag(static)
+    CCLib:            -static
+
+Test unit_tests
+  Run$: flag(tests)
+  TestTools:        unit_tests
+  Command:          $unit_tests
+
 Executable replexample
   CompiledObject:   native
   Path:             src/repl-example
diff --git a/src/coalg/coalg.ml b/src/coalg/coalg.ml
index 2ccd7014eaf53a9468cbdce993b9f2742f670603..f06bde32d14471a0a77261d94a882e905fa22a48 100644
--- a/src/coalg/coalg.ml
+++ b/src/coalg/coalg.ml
@@ -12,25 +12,6 @@ module FE = FunctorParsing
 
 open CoolUtils
 
-(* The type of formulae that are accepted. *)
-(*
-let sorts = Array.make 1 (CoAlgMisc.MultiModalK, [0])
-*)
-(*
-let sorts = Array.make 3 (CoAlgMisc.MultiModalK, [0])
-let _ = sorts.(0) <- (CoAlgMisc.Choice, [1; 2])
-*)
-(*
-let sorts = [| (CoAlgMisc.MultiModalKD, [0])
-            (*;  (CoAlgMisc.MultiModalKD, [1])
-            ;  (CoAlgMisc.Fusion, [1; 1]) *)
-            |]
-let sorts = [| (CM.MultiModalK, [0])
-            (*;  (CoAlgMisc.MultiModalKD, [1])
-            ;  (CoAlgMisc.Fusion, [1; 1]) *)
-            |]
-*)
-
 (* A partial function mapping nominals to their sorts. *)
 let nomTable name =
   assert (CoAlgFormula.isNominal name);
@@ -39,11 +20,47 @@ let nomTable name =
 
 let _ = Gc.set { (Gc.get()) with Gc.minor_heap_size = 4194304; major_heap_increment = 67108864; space_overhead = 120 }
 
-(*
-let _ =
-    let str = "A . B C" in
-    print_endline (FE.stringFromFunctorExp (FE.functorExpFromString str))
-*)
+type options =
+  { verbose : bool
+  ; fragment : CM.fragment_spec
+  }
+
+let defaultOpts = { verbose = false; fragment = CM.Auto }
+
+let options =
+  [ { Args.long = "verbose"
+    ; Args.short = Some 'v'
+    ; Args.description = "print verbose output with statistics"
+    ; Args.argument = Args.No_arg (fun a -> { a with verbose = true })
+    }
+  ; { Args.long = "agents"
+    ; Args.short = None
+    ; Args.description = "expects the next argument AGLIST to be a list of integers\n"
+                    ^ "which is treaten as the set of agents for CL formulas\n"
+                    ^ "it defaults to 1,2,3,4,5,6,7,8,9,10"
+    ; Args.argument =
+        let parse_agents strAglist =
+          let aglist = List.map int_of_string (Str.split (Str.regexp ",") strAglist) in
+          CoolUtils.cl_set_agents (Array.of_list aglist)
+        in
+        Args.Required_arg ("AGLIST", fun s a -> parse_agents s; a)
+    }
+  ; { Args.long = "fragment"
+    ; Args.short = None
+    ; Args.description = "Use reasoner algorithm specialized to FRAGMENT.\n"
+                         ^ "Possible values are: auto, altfree, altfreeaconj\n"
+                         ^ "where \"auto\" detects the fragment from the given formulas"
+    ; Args.argument =
+        let parse_fragment s = match s with
+          | "auto" -> CM.Auto
+          | "altfree" -> CM.Fragment CM.AlternationFree
+          | "altfreeaconj" -> CM.Fragment CM.AconjunctiveAltFree
+          | _ -> raise (Args.ParseError ("Unknown fragment " ^ s))
+        in
+        Args.Required_arg
+          ("FRAGMENT", fun s a -> { a with fragment = parse_fragment s })
+    }
+  ]
 
 let printUsage name =
   print_endline ("Usage: \"" ^ name ^ " <task> <functor> [<flags>]\" where");
@@ -61,12 +78,7 @@ let printUsage name =
   print_endline "                 or <functor> . <functor> (binds strongest)";
   print_endline "                 (so K+KD.CL*Id stand for (K + ((KD.CL) * Id)))";
   print_endline "       <flags> = a list of the following items";
-  print_endline "           --agents AGLIST";
-  print_endline "             expects the next argument AGLIST to be a list of integers";
-  print_endline "             which is treaten as the set of agents for CL formulas";
-  print_endline "             it defaults to 1,2,3,4,5,6,7,8,9,10";
-  print_endline "           --verbose";
-  print_endline "             print verbose output for sat task";
+  Args.help stdout 11 options;
   print_endline "";
   print_endline "Problems are read from standard input and have the form";
   print_endline "       [ ass_1; .. ; ass_n |- ] concept";
@@ -90,10 +102,9 @@ let printUsage name =
   exit 1
 
 let counter = ref 0
-let verbose = ref false
 
-let choiceSat () =
-    let verb = !verbose in
+let choiceSat opts =
+    let verb = opts.verbose in
     let sorts = (FE.sortTableFromString Sys.argv.(2)) in
 
     (* test if GML or PML occurs in sort table *)
@@ -114,13 +125,13 @@ let choiceSat () =
           incr counter;
           print_string ("\nFormula " ^ (string_of_int !counter) ^ ": ");
           flush stdout;
-          printRes (CoAlgReasoner.isSat ~verbose:verb sorts nomTable tbox f)
+          printRes (CoAlgReasoner.isSat ~verbose:verb opts.fragment sorts nomTable tbox f)
         else ()
       done
     with End_of_file -> ()
 
-let choiceProvable () =
-    let verb = !verbose in
+let choiceProvable opts =
+    let verb = opts.verbose in
     let sorted_not ((s,f):CF.sortedFormula) : CF.sortedFormula =
         (s,CF.NOT f)
     in
@@ -139,12 +150,12 @@ let choiceProvable () =
           incr counter;
           print_string ("\nFormula " ^ (string_of_int !counter) ^ ": ");
           flush stdout;
-          printRes (CoAlgReasoner.isSat ~verbose:verb sorts nomTable tbox fneg)
+          printRes (CoAlgReasoner.isSat ~verbose:verb opts.fragment sorts nomTable tbox fneg)
         else ()
       done
     with End_of_file -> ()
 
-let choicePrint () =
+let choicePrint opts =
     try
       while true do
         let input = read_line () in
@@ -158,7 +169,7 @@ let choicePrint () =
       done
     with End_of_file -> ()
 
-let choiceNom2fix () =
+let choiceNom2fix opts =
   try
     while true do
       let input = read_line () in
@@ -173,7 +184,7 @@ let choiceNom2fix () =
     done
   with End_of_file -> ()
 
-let choiceNNF () =
+let choiceNNF opts =
     try
       while true do
         let input = read_line () in
@@ -187,11 +198,15 @@ let choiceNNF () =
       done
     with End_of_file -> ()
 
-let choiceGraph () =
-  choiceSat ();
+let choiceGraph opts =
+  let sorts = (FE.sortTableFromString Sys.argv.(2)) in
+
+  let input = read_line () in
+  let (tbox, f) = CoAlgFormula.importQuery input in
+  ignore(CoAlgReasoner.isSat opts.fragment sorts nomTable tbox f);
   print_endline (CM.graphToDot ())
 
-let choiceVerify () =
+let choiceVerify opts =
     try
       while true do
         print_string "> ";
@@ -208,40 +223,21 @@ let choiceVerify () =
       done
     with End_of_file -> ()
 
-let rec parseFlags arr offs : unit =
-    let offs = ref (offs) in
-    let getParam () =
-        if ((!offs + 1) >= Array.length arr) then
-            raise (CoAlgFormula.CoAlgException ("Parameter missing for flag »" ^ (arr.(!offs)) ^ "«"))
-        else
-            offs := !offs + 1;
-            arr.(!offs)
-    in
-    if (!offs >= Array.length arr) then ()
-    else (ignore (match arr.(!offs) with
-         | "--verbose" -> verbose := true
-         | "--agents" ->
-            let strAglist = getParam () in
-            let aglist = List.map int_of_string (Str.split (Str.regexp ",") strAglist) in
-            CoolUtils.cl_set_agents (Array.of_list aglist)
-         | _ ->
-            raise (CoAlgFormula.CoAlgException ("Unknown flag »" ^ (arr.(!offs)) ^ "«"))
-         ) ; parseFlags arr (!offs + 1)
-     )
-
 let _ =
   if Array.length Sys.argv < 3 then printUsage (Sys.argv.(0))
   else
     let choice = Sys.argv.(1) in
-    parseFlags Sys.argv 3;
-    match choice with
-    | "sat" -> choiceSat ()
-    | "prov" -> choiceProvable ()
-    | "print" -> choicePrint ()
-    | "nnf" -> choiceNNF ()
-    | "verify" -> choiceVerify ()
-    | "graph" -> choiceGraph ()
-    | "nom2fix" -> choiceNom2fix()
-    | _ -> printUsage (Sys.argv.(0))
+    match Args.parse Sys.argv 3 options defaultOpts with
+    | Args.Error e -> Printf.printf "%s\n\n" e; printUsage (Sys.argv.(0))
+    | Args.Ok opts ->
+       match choice with
+       | "sat" -> choiceSat opts
+       | "prov" -> choiceProvable opts
+       | "print" -> choicePrint opts
+       | "nnf" -> choiceNNF opts
+       | "verify" -> choiceVerify opts
+       | "graph" -> choiceGraph opts
+       | "nom2fix" -> choiceNom2fix opts
+       | _ -> printUsage (Sys.argv.(0))
 
 (* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/coalgcompare/coalgcompare.ml b/src/coalgcompare/coalgcompare.ml
index 8ef8daa3451454ad8076a0e07f69e30f716d3229..548b6bd34deded1400f872e46d48175169064358 100644
--- a/src/coalgcompare/coalgcompare.ml
+++ b/src/coalgcompare/coalgcompare.ml
@@ -124,7 +124,7 @@ let exportALCQuery tbox (_, f) =
 
 
 let solvCoalg sortTable args =
-  G.evaluateFkt (fun (tbox, sf) -> CoAlgReasoner.isSat sortTable nomTable tbox sf) args
+  G.evaluateFkt (fun (tbox, sf) -> CoAlgReasoner.isSat CoAlgMisc.Auto sortTable nomTable tbox sf) args
 
 let solvALC (tbox, sf) =
   let args = exportALCQuery tbox sf in
diff --git a/src/debugger/debugger.ml b/src/debugger/debugger.ml
index 7f75b176ecf945dc43933b068ad087c6582b80b3..7efe4f585456a60d7e247914612b9b0e535dc973 100644
--- a/src/debugger/debugger.ml
+++ b/src/debugger/debugger.ml
@@ -100,7 +100,7 @@ let _ =
     let formula = Sys.argv.(2) in
     let sorts = (FE.sortTableFromString Sys.argv.(1)) in
     let (tbox, f) = CoAlgFormula.importQuery formula in
-    Reasoner.initReasoner sorts nomTable tbox f;
+    Reasoner.initReasoner CoAlgMisc.Auto sorts nomTable tbox f;
     let session = {
       Repl.prompt = "> ";
       Repl.bindings = Repl.addHelpBinding [
diff --git a/src/lib/CoAlgFormula.ml b/src/lib/CoAlgFormula.ml
index 6472add496dd83380077f8893889a256752f1e82..f72e7f67534930f6c562728b068268f5ff52397d 100644
--- a/src/lib/CoAlgFormula.ml
+++ b/src/lib/CoAlgFormula.ml
@@ -723,6 +723,56 @@ let rec verifyMuAltFree formula =
      raise (CoAlgException ("verifyMuAltFree: CTL should have been removed at this point"))
 
 
+exception NotAconjunctive of unit
+
+let isMuAconjunctive (formula:formula) : bool =
+  let module SS = Set.Make(String) in
+
+  (* Accepts formula f and a set of currently bound mu variables. Returns a subset
+   * of those, which actually occured free in the formula.
+   * Raises an CoAlgException if the formula is determined not to be aconjunctive *)
+  let rec verifyMuAconjunctiveInternal (formula:formula) (bound_mu_vars: SS.t) : SS.t =
+    let proc f = verifyMuAconjunctiveInternal f bound_mu_vars in
+    match formula with
+    | VAR s when SS.mem s bound_mu_vars -> SS.singleton s
+    | VAR s                             -> SS.empty
+    (* s is now bound by a nu and shadows any privious binding s *)
+    | NU (s, a) -> verifyMuAconjunctiveInternal a (SS.remove s bound_mu_vars)
+    (* Bind s in the recursive call but don't return it as occuring free *)
+    | MU (s, a) ->
+       SS.remove s (verifyMuAconjunctiveInternal a (SS.add s bound_mu_vars))
+
+    (* This is the interesting case *)
+    | AND (a, b) ->
+       let a_vars = proc a in
+       let b_vars = proc b in
+
+       (* If both subformulas contain free mu variables, the formula is not aconjunctive *)
+       if not (SS.is_empty a_vars) && not (SS.is_empty b_vars) then
+         raise (NotAconjunctive ())
+       else
+         SS.union a_vars b_vars (* one of them is empty anyways *)
+
+    (* The other cases just pass argument and return value through *)
+    | TRUE | FALSE | AP _
+    | CONST _ | CONSTN _ -> SS.empty
+    | AT (_,a) | NOT a
+    | EX (_,a) | AX (_,a) -> proc a
+    | OR (a,b) | EQU (a,b) | IMP (a,b) -> SS.union (proc a) (proc b)
+    | ENFORCES (_,a) | ALLOWS   (_,a)
+    | MIN (_,_,a)    | MAX (_,_,a)
+    | ATLEASTPROB (_, a) | LESSPROBFAIL (_, a)
+    | MORETHAN (_,_,a) | MAXEXCEPT (_,_,a) -> proc a
+    | ID(a) -> proc a
+    | NORM(a, b) | NORMN(a, b)
+    | CHC (a,b)  -> SS.union (proc a) (proc b)
+    | FUS (_,a) -> proc a
+    | AF _  | EF _ | AG _ | EG _ | AU _ | EU _ | AR _ | ER _ | AB _ | EB _ ->
+       raise (CoAlgException ("verifyMuAconjunctiveInternal: CTL should have been removed at this point"))
+  in
+  try ignore (verifyMuAconjunctiveInternal formula SS.empty); true
+  with NotAconjunctive () -> false
+
 (** Process from outside in. For every variable bound keep the tuple
     (name, negations). For every negation crossed map a +1 over snd on
     that list. For every variable met check that the matching
@@ -791,7 +841,7 @@ let rec verifyMuGuarded unguarded formula =
      raise (CoAlgException ("verifyMuGuarded: CTL should have been removed at this point"))
 
 let verifyFormula formula =
-  ignore (verifyMuAltFree formula);
+  verifyMuAltFree formula;
   verifyMuMonotone [] formula;
   verifyMuGuarded [] formula
 
diff --git a/src/lib/CoAlgFormula.mli b/src/lib/CoAlgFormula.mli
index 3fb17edfc6d14e294f745249ede320cd6ef0aba1..cee483d6e346dc2286d16be9d6a87ab75afd4d8a 100644
--- a/src/lib/CoAlgFormula.mli
+++ b/src/lib/CoAlgFormula.mli
@@ -79,6 +79,12 @@ val importFormula : string -> formula
 val importSortedFormula : string -> sortedFormula
 val importQuery : string -> sortedFormula list * sortedFormula
 
+(** Decides whether a fomula is aconjunctive.
+
+   A formula is aconjunctive if for every conjunction (a ʌ b), at most one of
+   the conjuncts a, b contains a free μ-variable. *)
+val isMuAconjunctive : formula -> bool
+
 val verifyFormula : formula -> unit
 
 val nnfNeg : formula -> formula
diff --git a/src/lib/CoAlgMisc.ml b/src/lib/CoAlgMisc.ml
index 6d20a7ac4da9096ac1e9d3c49ccc5930e2c457eb..447c4427138564f63ecbd09bf8d0c87391b1ef2f 100644
--- a/src/lib/CoAlgMisc.ml
+++ b/src/lib/CoAlgMisc.ml
@@ -663,6 +663,10 @@ let bsetMakeRealEmpty () =
     let res = bsetMake () in
     bsetRem res !S.bstrue; (* remove bstrue which is initially in an empty bset *)
     res
+let bsetSingleton lf =
+  let res = bsetMakeRealEmpty () in
+  bsetAdd res lf;
+  res
 let bsetCopy bs = S.copyBS bs
 let bsetFold fkt bs init = S.foldBS fkt bs init
 let bsetIter fkt bset = S.iterBS fkt bset
@@ -890,6 +894,13 @@ let atFormulaGetNominal f =
 
 let lfToAt _ lf = lf
 
+
+type fragment = AlternationFree | AconjunctiveAltFree
+type fragment_spec = Auto | Fragment of fragment
+let fragment = ref AlternationFree
+let getFragment () = !fragment
+
+
 (* Calculate all possible formulae. This includes all subformulae and
    in the case of μ-Calculus the Fischer-Ladner closure.
 
@@ -1207,7 +1218,7 @@ let initTablesAt hcF htF name sort =
   in
   C.HcFHt.iter addAt htF.(sort)
 
-let ppFormulae nomTbl tbox (s, f) =
+let ppFormulae fragSpec nomTbl tbox (s, f) =
   let card = Array.length !sortTable in
   if card <= 0 then
     raise (C.CoAlgException "Number of sorts must be positive.")
@@ -1268,6 +1279,28 @@ let ppFormulae nomTbl tbox (s, f) =
     if size2 > !size then size := size2 else ()
   done;
 
+  begin match fragSpec with
+  | Auto -> if C.isMuAconjunctive f then
+              fragment := AconjunctiveAltFree
+            else
+              fragment := AlternationFree
+  | Fragment AconjunctiveAltFree ->
+     if not (C.isMuAconjunctive f) then
+       raise (CoAlgFormula.CoAlgException "Formula not in required fragment")
+     else fragment := AconjunctiveAltFree
+  | Fragment AlternationFree ->
+     if C.isMuAconjunctive f then
+       raise (CoAlgFormula.CoAlgException "Formula not in required fragment")
+     else fragment := AlternationFree
+  end;
+
+  (* if (fragSpec = Auto || fragSpec = Fragment AconjunctiveAltFree) && C.isMuAconjunctive f then *)
+  (*   fragment := AconjunctiveAltFree; *)
+  (* else if (fragSpec = Auto || fragSpec = Fragment AlternationFree) && (not (C.isMuAconjunctive f)) *)
+  (*   fragment := AlternationFree; *)
+  (* else *)
+  (*   raise (CoAlgFormula.CoAlgException "Formula not in required fragment"); *)
+
   arrayFormula := Array.init card (fun _ -> Array.make !size C.TRUE);
   arrayType := Array.init card (fun _ -> Array.make !size Other);
   arrayDest1 := Array.init card (fun _ -> Array.make !size (-1));
diff --git a/src/lib/CoAlgMisc.mli b/src/lib/CoAlgMisc.mli
index b38722f508fda56701adccfe04381727a07918f0..d03a537d35dc9b5117cbb6015295ba997c1c364e 100644
--- a/src/lib/CoAlgMisc.mli
+++ b/src/lib/CoAlgMisc.mli
@@ -321,6 +321,7 @@ val nhtFold : (nominal -> bset -> 'a -> 'a) -> nht -> 'a -> 'a
 
 val bsetMake : unit -> bset (* a new bset which only contains True *)
 val bsetMakeRealEmpty : unit -> bset (* a new bset without containing True *)
+val bsetSingleton : localFormula -> bset
 val bsetAdd : bset -> localFormula -> unit
 val bsetMem : bset -> localFormula -> bool
 val bsetRem : bset -> int -> unit
@@ -368,8 +369,26 @@ val atFormulaGetNominal : atFormula -> nominal
 
 val lfToAt : sort -> localFormula -> atFormula
 
-val ppFormulae : (string -> sort option) -> CoAlgFormula.sortedFormula list -> CoAlgFormula.sortedFormula ->
-    CoAlgFormula.sortedFormula list * CoAlgFormula.sortedFormula * bset
+(** Sum of different fragments of the full mu-calculus.
+ 
+   Used by the reasoner to select between different specific algorithms for
+   individual fragments.
+ *)
+type fragment = AlternationFree | AconjunctiveAltFree
+
+type fragment_spec = Auto | Fragment of fragment
+
+(** Return the fragment of the mu-calculus that the original formula uses *)
+val getFragment : unit -> fragment
+
+(** [ppFormulae fragment nomTbl tbox sf] initializes the array representation and various
+    tables for the formula.
+  
+   @return (simplified NNF of f, simplified nnf of tbox, bitset containing f)
+ *)
+val ppFormulae : fragment_spec -> (string -> sort option) -> CoAlgFormula.sortedFormula list
+                 -> CoAlgFormula.sortedFormula
+                 -> CoAlgFormula.sortedFormula list * CoAlgFormula.sortedFormula * bset
 
 
 (* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/lib/CoAlgReasoner.ml b/src/lib/CoAlgReasoner.ml
index fca69aaf78c08132e7c8dc9bff98cc8815e9c440..583153fdd09eab0d0b3989142858d9c85ca2c775 100644
--- a/src/lib/CoAlgReasoner.ml
+++ b/src/lib/CoAlgReasoner.ml
@@ -939,36 +939,56 @@ let insertCore sort bs defer =
      c
   | Some c -> c
 
+(** Insert cores for the children generated by a modal rule application.
+  *
+  * @return true if all new cores are already unsatisfiable, false otherwise
+  *)
 let insertRule state dep chldrn =
   let chldrn = listFromLazylist chldrn in
-  let insert (isUns, acc) (sort, bs, defer) =
+  let mkCores (sort, bs, defer) =
     let bs1 = bsetAddTBox sort bs in
-    let core = insertCore sort bs1 defer in
-    let isUns1 = if coreGetStatus core = Unsat then isUns else false in
-    (isUns1, core::acc)
+    (* For the aconjunctive fragment, instead of adding a core annotated with
+     * all deferrals, we add a core for each formula in the deferral set. *)
+    let deferSingletons =
+      if getFragment () = AconjunctiveAltFree
+         && bsetCompare defer (bsetMakeRealEmpty ()) <> 0 then
+        bsetFold (fun f list -> bsetSingleton f::list) defer []
+      else
+        [defer] in
+    List.map (insertCore sort (bsetCopy bs1)) deferSingletons
+  in
+  (* A list of cores for each child: [[child1a;child1b]; [child2a;child2b]] *)
+  let coresPerChild = List.map mkCores chldrn in
+  (* All combinations that take a core from each child:
+   * [[ch1a;ch2a]; [ch1a;ch2b]; [ch1b;ch2a]; [ch1b;ch2b]]
+   * Neccesary because stateAddRule expects each child to have only one core. *)
+  let coresPerRule = CU.TList.combinations coresPerChild in
+  let addRule cores =
+    let idx = stateAddRule state dep cores in
+    List.iter (fun c -> coreAddParent c state idx) cores;
+    if List.for_all (fun core -> coreGetStatus core = Unsat) cores then begin
+      propagateUnsat [UState (state, Some idx)];
+      true
+    end else 
+      false
   in
-  let (isUnsat, children) = List.fold_left insert (true, []) chldrn in
-  let idx = stateAddRule state dep (List.rev children) in
-  List.iter (fun c -> coreAddParent c state idx) children;
-  if isUnsat then begin
-    propagateUnsat [UState (state, Some idx)];
-    false
-  end else true
-
-let rec insertAllRules state = function
-  | [] -> true
-  | (dep, chldrn)::tl ->
-      let notUnsat = insertRule state dep chldrn in
-      if notUnsat then insertAllRules state tl else false
+  List.mem true (List.map (fun r -> addRule r) coresPerRule)
+  
+
+(** Call insertRule on all rules
+  * @return true if any rule application generated only unsat cores
+  *)
+let rec insertAllRules state rules =
+  List.mem true (List.map (fun (dep, children) -> insertRule state dep children) rules)
 
 let expandState state =
   match stateNextRule state with
   | MultipleElements rules ->
-      let notUnsat = insertAllRules state rules in
-      if notUnsat then stateSetStatus state Open
+      let unsat = insertAllRules state rules in
+      if not unsat then stateSetStatus state Open
   | SingleElement (dep, chldrn) ->
-      let notUnsat = insertRule state dep chldrn in
-      if notUnsat then queueInsertState state else ()
+      let unsat = insertRule state dep chldrn in
+      if not unsat then queueInsertState state else ()
   | NoMoreElements -> stateSetStatus state Open
 
 
@@ -1050,9 +1070,9 @@ let rec buildGraphLoop () =
   (* if propagating nominals added some more queue members, do all again.. *)
   if queueIsEmpty () then () else buildGraphLoop ()
 
-let initReasoner sorts nomTable tbox sf =
+let initReasoner fragSpec sorts nomTable tbox sf =
   sortTable := Array.copy sorts;
-  let (tbox1, sf1, bs) = ppFormulae nomTable tbox sf in
+  let (tbox1, sf1, bs) = ppFormulae fragSpec nomTable tbox sf in
   let sort = fst sf in
   let bs1 = bsetAddTBox sort bs in
   let deferrals = bsetMakeRealEmpty() in
@@ -1094,9 +1114,9 @@ let reasonerFinished () =
     @return True if sf is satisfiable wrt tbox, false otherwise.
  *)
 
-let isSat ?(verbose = false) sorts nomTable tbox sf =
+let isSat ?(verbose = false) fragSpec sorts nomTable tbox sf =
   let start = if verbose then Unix.gettimeofday () else 0. in
-  initReasoner sorts nomTable tbox sf;
+  initReasoner fragSpec sorts nomTable tbox sf;
   let sat =
     try
       buildGraphLoop ();
diff --git a/src/lib/CoAlgReasoner.mli b/src/lib/CoAlgReasoner.mli
index 52bb178a7fca3ea8b51d142e4282b4b5087d87ad..506af70642ac0269d51226a36d28585eb52aea5a 100644
--- a/src/lib/CoAlgReasoner.mli
+++ b/src/lib/CoAlgReasoner.mli
@@ -13,12 +13,15 @@ type input = sortTable * nomTable * assumptions * CoAlgFormula.sortedFormula
 
 exception ReasonerError of string
 
-val isSat : ?verbose:bool -> sortTable -> (string -> CoAlgFormula.sort option) ->
-  CoAlgFormula.sortedFormula list -> CoAlgFormula.sortedFormula -> bool
+val isSat : ?verbose:bool -> CoAlgMisc.fragment_spec -> sortTable
+            -> (string -> CoAlgFormula.sort option) ->
+            CoAlgFormula.sortedFormula list -> CoAlgFormula.sortedFormula -> bool
 
 
-val initReasoner : sortTable -> (string -> CoAlgFormula.sort option) ->
-  CoAlgFormula.sortedFormula list -> CoAlgFormula.sortedFormula -> unit
+val initReasoner : CoAlgMisc.fragment_spec -> sortTable
+                   -> (string -> CoAlgFormula.sort option) ->
+                   CoAlgFormula.sortedFormula list
+                   -> CoAlgFormula.sortedFormula -> unit
 
 (** Run a single step of the reasoner. That is: Expand one node and perform sat
     propagation.
diff --git a/src/lib/CoolUtils.ml b/src/lib/CoolUtils.ml
index 20141603128437e8d97c304194d29229cd4d86ea..ef61ee63a037780cd295ac16f678aa4d38b3f23c 100644
--- a/src/lib/CoolUtils.ml
+++ b/src/lib/CoolUtils.ml
@@ -35,6 +35,87 @@ module TList = struct
     let empty l = match l with
                   | (_::_) -> false
                   | [] -> true
+    let rec combinations list = match list with
+      | [] -> []
+      | [l] -> List.map (fun x -> [x]) l
+      | (l::ls) ->
+         let tail = combinations ls in
+         List.fold_left (fun acc x -> acc @ List.map (fun lst -> x::lst) tail) [] l
+end
+
+module Args = struct
+  type 'a param_parser = 'a -> 'a
+  type 'a argument = Required_arg of string * (string -> 'a param_parser)
+                   | No_arg of 'a param_parser
+
+  type 'a description =
+    { long : string
+    ; short : char option
+    ; description : string
+    ; argument : 'a argument
+    }
+
+  exception ParseError of string
+
+  let find str mapping =
+    try (List.find (fun arg -> arg.long = str) mapping)
+    with
+      Not_found -> raise (ParseError (Printf.sprintf "Unknown option %s" str))
+
+  let findShort char mapping =
+    try (List.find (fun arg -> arg.short = Some char) mapping)
+    with
+      Not_found -> raise (ParseError (Printf.sprintf "Unknown option %c" char))
+
+  type 'a result = Ok of 'a | Error of string
+
+  let parse ~argv ~offset ~options ~default =
+    let rec parseImpl offs args =
+      let offs = ref (offs) in
+      let getParam () =
+          if ((!offs + 1) >= Array.length argv) then
+            raise (ParseError (Printf.sprintf "Parameter missing for flag »%s«" argv.(!offs)))
+          else
+            offs := !offs + 1;
+            argv.(!offs)
+      in
+      if (!offs >= Array.length argv) then args
+      else
+        let arg = argv.(!offs) in
+        let arg_desc = if String.length arg > 2 && String.sub arg 0 2 = "--" then
+                         find (String.sub arg 2 (String.length arg - 2)) options
+                       else if String.length arg > 1 && String.get arg 0 = '-' then
+                         findShort (String.get arg 1) options
+                       else 
+                         raise (ParseError (Printf.sprintf "Unknown argument %s" arg))
+        in
+        let args = match arg_desc.argument with
+          | Required_arg (_,p) -> p (getParam ()) args
+          | No_arg p -> p args
+        in
+        parseImpl (!offs+1) args
+
+    in
+    try Ok (parseImpl offset default)
+    with ParseError e -> Error e
+
+  let help channel offset mapping =
+    let padding offset = String.make offset ' ' in
+    let help_option desc =
+      let long = "--" ^ desc.long in
+      let short = match desc.short with
+        | Some x -> ",-" ^ Char.escaped x
+        | None -> ""
+      in
+      let var = match desc.argument with
+        | No_arg _ -> ""
+        | Required_arg (a,_) -> " " ^ a
+      in
+      Printf.fprintf channel "%s%s%s%s\n" (padding offset) long short var;
+      (* TODO split_on_char is not available in ocaml < 4.04 *)
+      List.iter (Printf.fprintf channel "%s%s\n" (padding (offset+2))) (String.split_on_char '\n' desc.description)
+    in
+    List.iter help_option mapping
 end
 
 let agents = ref ([|1;2;3;4;5|])
diff --git a/src/lib/CoolUtils.mli b/src/lib/CoolUtils.mli
index 62aa41db4e03135a4d2fafc56b36f833ff216931..df6a0b2519191697f8b648a5efbb71d2872f6253 100644
--- a/src/lib/CoolUtils.mli
+++ b/src/lib/CoolUtils.mli
@@ -1,20 +1,69 @@
 
-(* Some basic utilities as they are known from haskell prelude and so on... *)
+(** Some basic utilities as they are known from haskell prelude and so on... *)
 
+(** Additional utility functions for the Array module  *)
 module TArray : sig
     val all : ('a -> bool) -> 'a array -> bool
     val any : ('a -> bool) -> 'a array -> bool
     val elem : 'a -> 'a array -> bool
     val included : 'a array -> 'a array -> bool
     val to_string : ('a -> string) -> 'a array -> string
-    val uniq : 'a array -> 'a array (* delete consecutive duplicates *)
+    val uniq : 'a array -> 'a array (** delete consecutive duplicates *)
 end
 
+(** Additional utility functions for the List module  *)
 module TList : sig
     val zip : ('a list) -> ('b list) -> ('a * 'b) list
     val powerset : ('a list) -> ('a list list)
     val prod : ('a list) -> ('b list) -> (('a * 'b) list)
     val empty : ('a list) -> bool
+
+    (** Compute possible combinations of the sublists.
+
+        [combinations [[1;2];[3;4]] = [[1;3];[1;4];[2;3];[2;4]]]
+     *)
+    val combinations : 'a list list -> 'a list list
+end
+
+(** Command line argument parser *)
+module Args : sig
+  type 'a param_parser = 'a -> 'a
+
+  (** Specifies whether the option takes an addition paramter. *)
+  type 'a argument = Required_arg of string * (string -> 'a param_parser)
+                     (** name of the argument and parser. 
+                         Parser can throw ParseErrors
+                      *)
+                   | No_arg of 'a param_parser
+
+  type 'a description =
+    { long : string
+    ; short : char option
+    ; description : string
+    ; argument : 'a argument
+    }
+
+  exception ParseError of string
+
+  type 'a result = Ok of 'a | Error of string
+
+  (** parses command line options.
+
+      The user can choose the type 'a, provided they provide parsers and a
+      default value.
+
+      @param argv The Sys.argv array
+      @param offset The index in [argv] where option parsing should start
+      @param options Descriptions of all available options
+      @param default Default value for the option type
+      @return The option type or a parse error
+   *)
+  val parse : argv:string array -> offset:int -> options:'a description list
+              -> default:'a -> 'a result
+
+  (** [help out indent options] prints help for [options] to [out] indented
+      by [indent] characters *)
+  val help : out_channel -> int -> 'a description list -> unit
 end
 
 val cl_get_agents : unit -> int array
diff --git a/src/owl/cool-owl.ml b/src/owl/cool-owl.ml
index 55a25b6cfe4f559b58c33c247529776982bcef49..87a2ad2f435de4dc59f0076afffb78cd70e7384c 100644
--- a/src/owl/cool-owl.ml
+++ b/src/owl/cool-owl.ml
@@ -45,7 +45,7 @@ let consistency () =
         | (_, []) -> raise (Exception "File needs to contain at least one ontology")
     in
     let (sorts,nomTable,axioms) = OWL.to_coalg ontology in
-    let cons = CR.isSat sorts nomTable axioms (0,CF.TRUE) in
+    let cons = CR.isSat CoAlgMisc.Auto sorts nomTable axioms (0,CF.TRUE) in
     let cons = if cons then "yes" else "no" in
     let (ontoname, _) = ontology in
     Printf.printf "%s consistent: %s\n" ontoname cons
diff --git a/src/testsuite/Testsuite.ml b/src/testsuite/Testsuite.ml
index 1d99e4ae7e668c50fb731e53cd02ea7e6ba8c11c..1cddc01131fdf9055dee5f884cca272ae99c06f5 100644
--- a/src/testsuite/Testsuite.ml
+++ b/src/testsuite/Testsuite.ml
@@ -41,7 +41,7 @@ let runSatCheck (_,logic,input) =
       (* isSat leaks tons of memory. Call it in new process to allow the OS to
          reclaim that memory between calls. *)
       match Unix.fork () with
-      | 0 -> let res = CoAlgReasoner.isSat logic nomTable tbox f in
+      | 0 -> let res = CoAlgReasoner.isSat CoAlgMisc.Auto logic nomTable tbox f in
              if res then exit 0 else exit 1
       | -1 -> raise (CoAlgFormula.CoAlgException "fork() failed")
       | _ -> match Unix.wait () with
@@ -68,14 +68,23 @@ let runSatCheckVerbose (sc:satCheck) =
     PF.printf "%s? → " (sotr expectation);
     flush stdout;
     let res = runSatCheck sc in
-    (if (res = expectation)
-    then PF.printf "%s\n" (cs "1;32" "Yes")
-    else PF.printf "%s, cool claims it is %s\n"
-                    (cs "1;31" "No")
-                    (sotr res));
+    if (res = expectation) then
+      PF.printf "%s\n" (cs "1;32" "Yes")
+     else
+      PF.printf "%s, cool claims it is %s\n" (cs "1;31" "No") (sotr res);
     flush stdout;
     res
 
+let reportFailed (failed : (satCheck*testResult) list) =
+  if failed <> [] then begin
+      Printf.printf "=> Failed tests:\n";
+      let printTest ((_,_,title),res) =
+        let res_string = terminal_string_of_testResult res in
+        Printf.printf "  not %s: %s\n" res_string title
+      in
+      List.iter printTest failed;
+      Printf.printf "\n"
+    end;
 
 
 (* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/testsuite/Testsuite.mli b/src/testsuite/Testsuite.mli
index f2e31ca0228e731f0c75b4da093253dbbdc32abf..a85adb403098d923f63fcde962ec42e9d1c269c1 100644
--- a/src/testsuite/Testsuite.mli
+++ b/src/testsuite/Testsuite.mli
@@ -15,5 +15,8 @@ type satCheck = testResult * (CoAlgMisc.sortTable) * string
 val runSatCheck : satCheck -> testResult
 val runSatCheckVerbose : satCheck -> testResult
 
+(** Print a summary for failed testcases.
+ * @param failedTests a list of failed testcases *)
+val reportFailed : (satCheck * testResult) list -> unit
 
 (* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/testsuite/cool-testsuite.ml b/src/testsuite/cool-testsuite.ml
index 129471f7297cf5fba4042d46041b9f4d2729c0c1..fc089600f3ab07311094930aa88ccd1f35bfc54e 100644
--- a/src/testsuite/cool-testsuite.ml
+++ b/src/testsuite/cool-testsuite.ml
@@ -49,6 +49,18 @@ let k_testcases: satCheck list =
     ; c Sat   "<r> d & <r> e & ((<r> d & <r> e) => [r] e) & (d & e => <r> ~(d&e)) |- True"
     ]
 
+let aconjunctive_testcases: satCheck list =
+  let c a b = (a, k, b) in
+  [ (* These formulas are aconjunctive *)
+    c Sat "ν x. ν y. ([r] x & <r> y)"
+  ; c Unsat "ν x. ν y. ([r] x & <r> y & False)"
+  (* These formulas aren't *)
+  ; c Unsat "μ x. μ y. ([r] x & <r> y)"
+  ; c Unsat "(ν x. (<r> True & [r] x)) & (μ x. ((p0 | [r] x) & (~p0 | [r] x)))"
+  (* This is not aconjunctive and should be answered wrongly by the algorithm
+   * for aconjunctive formulas *)
+  ; c Unsat "µ x. ( [](~p | x) & <>(p | x) )"
+  ]
 
 let nominal_testcases =
     let c a b = (a,k,b) in
@@ -148,13 +160,14 @@ let testcases ~(slow:bool) =
         ; sat "SKIP (({>= 3/5} p0 & {>= 2/5} p1) + False)"
         ; sat "SKIP ({>= 3/5} (False + <R1> True & <R2> True) & {>= 2/5} ({>= 1/10} p0 & {>= 1/10} (~ p0) + False ) + False)"
         ]
+    ; c "K-mu (aconjunctive fragment)" aconjunctive_testcases
     ]
 
 let doTests ~(slow:bool) =
     let skipchecker = Str.regexp "SKIP " in (* skip formulas beginning with "SKIP " *)
     let success = ref 0 in
     let skipped = ref 0 in
-    let failed = ref 0 in
+    let failed = ref [] in
     foreach_l (testcases slow) (fun (name,init_globals,table) ->
         Printf.printf "==== Testing %s ====\n" name;
         (*
@@ -173,16 +186,18 @@ let doTests ~(slow:bool) =
                 (* compare the expected and the actual result *)
                 if actual = expected
                 then success := !success + 1
-                else failed := !failed + 1
+                else failed := (sc,actual) :: !failed
             end
         );
         print_endline ""
     );
     let s n = if n = 1 then "testcase" else "testcases" in
+    reportFailed !failed;
     Printf.printf "=> %d %s succeeded, %d %s failed, %d skipped.\n"
         !success (s !success)
-        !failed (s !failed)
-        !skipped
+        (List.length !failed) (s (List.length !failed))
+        !skipped;
+    if !failed = [] then exit 0 else exit 1
 
 
 let printUsage name =
diff --git a/src/unit-tests/CoAlgFormula_tests.ml b/src/unit-tests/CoAlgFormula_tests.ml
new file mode 100644
index 0000000000000000000000000000000000000000..b87e81625726c478ce580d9a078e0ad2b71c4a13
--- /dev/null
+++ b/src/unit-tests/CoAlgFormula_tests.ml
@@ -0,0 +1,14 @@
+open OUnit2
+open CoAlgFormula
+
+let isMuAconjunctive_test _ =
+  let with_formula str action = action (importFormula str) str in
+
+  with_formula "ν x. ν y. ([r] x & <r> y)" (fun f f_str ->
+   assert_bool f_str (isMuAconjunctive f));
+  with_formula "μ x. μ y. ([r] x & <r> y)" (fun f f_str ->
+   assert_bool f_str (not (isMuAconjunctive f)))
+
+let tests = "CoAlgFormula" >: ("isMuAconjunctive" >:: isMuAconjunctive_test)
+
+(* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/unit-tests/CoAlgFormula_tests.mli b/src/unit-tests/CoAlgFormula_tests.mli
new file mode 100644
index 0000000000000000000000000000000000000000..ac3e6e6da69f9f977785c220e30ef4bf3a11e645
--- /dev/null
+++ b/src/unit-tests/CoAlgFormula_tests.mli
@@ -0,0 +1,3 @@
+val tests : OUnit2.test
+
+(* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/unit-tests/CoolUtils_tests.ml b/src/unit-tests/CoolUtils_tests.ml
new file mode 100644
index 0000000000000000000000000000000000000000..3266675602423114a8f4edf18de599a3774946c7
--- /dev/null
+++ b/src/unit-tests/CoolUtils_tests.ml
@@ -0,0 +1,79 @@
+open OUnit2
+
+let string_of_list (printer: 'a -> string) (lst: 'a  list) : string =
+  "[" ^ (String.concat ";" (List.map printer lst)) ^ "]"
+
+module TList_tests : (sig val tests : test end) = struct
+  open CoolUtils.TList
+
+  let combinations_test _ =
+    let list_printer = string_of_list (string_of_list string_of_int) in
+    assert_equal ~msg:"empty list" ~printer:list_printer [] (combinations []);
+    assert_equal ~msg:"two elements" ~printer:list_printer
+                 [[1;2]] (combinations [[1]; [2]]);
+    assert_equal ~msg:"two lists" ~printer:list_printer
+                 [[1;3]; [1;4]; [2;3]; [2;4]] (combinations [[1;2]; [3;4]])
+
+  let tests = "TList" >: ("combinations" >:: combinations_test)
+end
+
+module Args_tests : (sig val tests : test end) = struct
+  open CoolUtils.Args
+
+  type opt = { verbose : bool; file : string option }
+
+  let def_opts = { verbose = false; file = None }
+
+  let options =
+    [ { long = "verbose"
+      ; short = Some 'v'
+      ; description = "Print verbose ouput"
+      ; argument = No_arg (fun o -> { o with verbose = true })
+      }
+    ; { long = "file"
+      ; short = None
+      ; description = "File to load"
+      ; argument = Required_arg ("FILE", fun s o -> { o with file = Some s })
+      }
+    ]
+    
+  let print_opts { verbose; file } = match file with
+    | None -> Printf.sprintf "{ verbose = \"%B\"; file = None}" verbose
+    | Some f -> Printf.sprintf "{ verbose = \"%B\"; file = Some \"%s\"}" verbose f
+
+  let parse_test _ =
+    let print_maybe = function
+      | Error e -> "Error \"" ^ e ^ "\""
+      | Ok x -> "Ok " ^ print_opts x
+    in
+    let cmp_maybe a b = match (a, b) with
+      | Error _, Error _ -> true
+      | Ok x, Ok y -> x = y
+      | _ -> false
+    in
+    let assert_parse argv res =
+      assert_equal ~msg:(string_of_list (fun x -> x) argv)
+                   ~printer:print_maybe
+                   ~cmp:cmp_maybe
+        res (parse (Array.of_list argv) 0 options def_opts)
+    in
+
+    assert_parse ["--verbose"] (Ok { verbose = true; file = None });
+    assert_parse ["-v"] (Ok { verbose = true; file = None });
+    assert_parse [] (Ok { verbose = false; file = None });
+    assert_parse ["--file"; "foo"] (Ok { verbose = false; file = Some "foo" });
+    assert_parse ["--verbose"; "--file"; "foo"]
+                 (Ok { verbose = true; file = Some "foo" });
+    assert_parse ["--file"; "foo"; "--verbose"]
+                 (Ok { verbose = true; file = Some "foo" });
+
+    assert_parse ["--verbuse"] (Error "");
+    assert_parse ["--file"] (Error "");
+    assert_parse ["file"] (Error "")
+
+  let tests = "Args" >: ("parse" >:: parse_test)
+end
+
+let tests = "CoolUtils" >::: [TList_tests.tests; Args_tests.tests]
+
+(* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/unit-tests/CoolUtils_tests.mli b/src/unit-tests/CoolUtils_tests.mli
new file mode 100644
index 0000000000000000000000000000000000000000..ac3e6e6da69f9f977785c220e30ef4bf3a11e645
--- /dev/null
+++ b/src/unit-tests/CoolUtils_tests.mli
@@ -0,0 +1,3 @@
+val tests : OUnit2.test
+
+(* vim: set et sw=2 sts=2 ts=8 : *)
diff --git a/src/unit-tests/cool_unit_tests.ml b/src/unit-tests/cool_unit_tests.ml
new file mode 100644
index 0000000000000000000000000000000000000000..65954fb02793073bee8fd49dda3dfa3b725462c8
--- /dev/null
+++ b/src/unit-tests/cool_unit_tests.ml
@@ -0,0 +1,10 @@
+open OUnit2
+
+let tests =
+  [ CoolUtils_tests.tests
+  ; CoAlgFormula_tests.tests
+  ]
+
+let () = run_test_tt_main (test_list tests)
+
+(* vim: set et sw=2 sts=2 ts=8 : *)