diff --git a/src/TexVC/TexVC.php b/src/TexVC/TexVC.php
index debfa830..541295ab 100644
--- a/src/TexVC/TexVC.php
+++ b/src/TexVC/TexVC.php
@@ -59,7 +59,7 @@ class TexVC {
 				$tuRef = $this->tu->getBaseElements()[$pkg];
 				$result[$pkg] = $input->containsFunc( $tuRef );
 			}
-			if ( !$options['usemhchem'] ) {
+			if ( isset( $options['usemhchem'] ) && !$options['usemhchem'] ) {
 				if ( $result['mhchem_required'] ) {
 					return [
 						'status' => 'C',
diff --git a/tests/phpunit/unit/TexVC/AllTest.php b/tests/phpunit/unit/TexVC/AllTest.php
index bb3ec40f..54cc604c 100644
--- a/tests/phpunit/unit/TexVC/AllTest.php
+++ b/tests/phpunit/unit/TexVC/AllTest.php
@@ -15,74 +15,605 @@ class AllTest extends MediaWikiUnitTestCase {
 
 	protected function setUp(): void {
 		parent::setUp();
-		$this->testCases = [
-			"Box functions" => [
-				"input" =>
+		$this->texVC = new TexVC();
+	}
+
+	public static function provideTestCases() {
+		$DELIMITERS1 = [ '(', ')', '[', ']', '\\{', '\\}', '|' ];
+		$DELIMITERS2 = array_map(
+			function ( $f ) { return '\\' . $f; },
+			array_slice(
+				explode( '\\', '\\backslash\\downarrow\\Downarrow\\langle\\lbrace\\lceil\\lfloor' .
+						 '\\llcorner\\lrcorner\\rangle\\rbrace\\rceil\\rfloor\\rightleftharpoons' .
+						 '\\twoheadleftarrow\\twoheadrightarrow\\ulcorner\\uparrow\\Uparrow' .
+						 '\\updownarrow\\Updownarrow\\urcorner\\Vert\\vert\\lbrack\\rbrack' ), 1 ) );
+		$BIGS = array_slice( explode( '\\', '\\big\\Big\\bigg\\Bigg\\biggl\\Biggl\\biggr\\Biggr' .
+									  '\\bigl\\Bigl\\bigr\\Bigr'), 1 );
+		$ENV = ['matrix', 'pmatrix', 'bmatrix', 'Bmatrix', 'vmatrix', 'Vmatrix',
+				'array', 'align', 'alignat', 'smallmatrix', 'cases'];
+		function arg($env) {
+			switch ($env) {
+			case 'array':
+				return '{|c||c|}';
+			case 'alignedat':
+			case 'alignat':
+				return '{3}';
+			default:
+				return '';
+			}
+		}
+		return [
+			[
+				"Box functions",
+				[
+					"input" =>
 					"\\text {-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}} " .
 					"\\mbox {-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}} " .
 					"\\hbox {-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}} " .
 					"\\vbox {-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}} ",
-				"output" =>
+					"output" =>
 					"{\\text{-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}}}" .
 					"{\\mbox{-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}}}" .
 					"{\\hbox{-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}}}" .
 					"{\\vbox{-0-9a-zA-Z+*,=():/;?.!'` \u{0080}-\u{00FF}}}"
+				],
 			],
-			"Box functions (2)" => [
-				"input" => "{\\text{ABC}}{\\mbox{ABC}}{\\hbox{ABC}}{\\vbox{ABC}}",
-				"skipOcaml" => true /* extra braces in ocaml version */
+			[
+				"Box functions (2)",
+				[
+					"input" => "{\\text{ABC}}{\\mbox{ABC}}{\\hbox{ABC}}{\\vbox{ABC}}",
+					"skipOcaml" => true /* extra braces in ocaml version */
+				],
 			],
-			"LaTeX functions" => [
-				"input" =>
+			[
+				"LaTeX functions",
+				[
+					"input" =>
 					"\\arccos \\arcsin \\arctan \\arg \\cosh \\cos \\cot \\coth " .
 					"\\csc \\deg \\det \\dim \\exp \\gcd \\hom \\inf \\ker \\lg " .
 					"\\lim \\liminf \\limsup \\ln \\log \\max \\min \\Pr \\sec " .
 					"\\sin \\sinh \\sup \\tan \\tanh "
+				],
 			],
-			"Mediawiki functions" => [
-				"input" => "\\arccot\\arcsec\\arccsc\\sgn\\sen",
-				"output" =>
+			[
+				"Mediawiki functions",
+				[
+					"input" => "\\arccot\\arcsec\\arccsc\\sgn\\sen",
+					"output" =>
 					"\\operatorname {arccot} \\operatorname {arcsec} " .
 					"\\operatorname {arccsc} \\operatorname {sgn} " .
 					"\\operatorname {sen} "
+				],
+			],
+			[
+				'Literals (1)',
+				[
+					'input' =>
+					'\\aleph \\alpha \\amalg \\And \\angle \\approx ' .
+					'\\approxeq \\ast \\asymp \\backepsilon \\backprime ' .
+					'\\backsim \\backsimeq \\barwedge \\Bbbk \\because \\beta ' .
+					'\\beth \\between \\bigcap \\bigcirc \\bigcup \\bigodot ' .
+					'\\bigoplus \\bigotimes \\bigsqcup \\bigstar ' .
+					'\\bigtriangledown \\bigtriangleup \\biguplus \\bigvee ' .
+					'\\bigwedge \\blacklozenge \\blacksquare \\blacktriangle ' .
+					'\\blacktriangledown \\blacktriangleleft \\blacktriangleright ' .
+					'\\bot \\bowtie \\Box \\boxdot \\boxminus \\boxplus ' .
+					'\\boxtimes \\bullet \\bumpeq \\Bumpeq \\cap \\Cap \\cdot ' .
+					'\\cdots \\centerdot \\checkmark \\chi \\circ \\circeq ' .
+					'\\circlearrowleft \\circlearrowright \\circledast ' .
+					'\\circledcirc \\circleddash \\circledS \\clubsuit \\colon ' .
+					'\\complement \\cong \\coprod \\cup \\Cup ' .
+					'\\curlyeqprec \\curlyeqsucc \\curlyvee \\curlywedge ' .
+					'\\curvearrowleft \\curvearrowright \\dagger \\daleth ' .
+					'\\dashv \\ddagger \\ddots \\delta \\Delta ' .
+					'\\diagdown \\diagup \\diamond \\Diamond \\diamondsuit ' .
+					'\\digamma \\displaystyle \\div \\divideontimes \\doteq ' .
+					'\\doteqdot \\dotplus \\dots \\dotsb \\dotsc \\dotsi \\dotsm ' .
+					'\\dotso \\doublebarwedge \\downdownarrows \\downharpoonleft ' .
+					'\\downharpoonright \\ell \\emptyset \\epsilon \\eqcirc ' .
+					'\\eqsim \\eqslantgtr \\eqslantless \\equiv \\eta \\eth ' .
+					'\\exists \\fallingdotseq \\Finv \\flat \\forall \\frown ' .
+					'\\Game \\gamma \\Gamma \\geq \\geqq \\geqslant \\gets \\gg ' .
+					'\\ggg \\gimel \\gnapprox \\gneq \\gneqq \\gnsim \\gtrapprox ' .
+					'\\gtrdot \\gtreqless \\gtreqqless \\gtrless \\gtrsim ' .
+					'\\gvertneqq \\hbar \\heartsuit \\hookleftarrow ' .
+					'\\hookrightarrow \\hslash \\iff \\iiiint \\iiint \\iint ' .
+					'\\Im \\imath \\implies \\in \\infty \\injlim \\int ' .
+					'\\intercal \\iota \\jmath \\kappa \\lambda \\Lambda \\land ' .
+					'\\ldots \\leftarrow \\Leftarrow \\leftarrowtail ' .
+					'\\leftharpoondown \\leftharpoonup \\leftleftarrows ' .
+					'\\leftrightarrow \\Leftrightarrow \\leftrightarrows ' .
+					'\\leftrightharpoons \\leftrightsquigarrow \\leftthreetimes ' .
+					'\\leq \\leqq \\leqslant \\lessapprox \\lessdot ' .
+					'\\lesseqgtr \\lesseqqgtr \\lessgtr \\lesssim \\limits \\ll ' .
+					'\\Lleftarrow \\lll \\lnapprox \\lneq \\lneqq \\lnot \\lnsim ' .
+					'\\longleftarrow \\Longleftarrow \\longleftrightarrow ' .
+					'\\Longleftrightarrow \\longmapsto \\longrightarrow ' .
+					'\\Longrightarrow \\looparrowleft \\looparrowright \\lor ' .
+					'\\lozenge \\Lsh \\ltimes \\lVert \\lvertneqq \\mapsto ' .
+					'\\measuredangle \\mho \\mid \\mod \\models \\mp \\mu ' .
+					'\\multimap \\nabla \\natural \\ncong \\nearrow \\neg \\neq ' .
+					'\\nexists \\ngeq \\ngeqq \\ngeqslant \\ngtr \\ni ' .
+					'\\nleftarrow \\nLeftarrow \\nleftrightarrow ' .
+					'\\nLeftrightarrow \\nleq \\nleqq \\nleqslant \\nless \\nmid ' .
+					'\\nolimits \\not \\notin \\nparallel \\nprec \\npreceq ' .
+					'\\nrightarrow \\nRightarrow \\nshortmid \\nshortparallel ' .
+					'\\nsim \\nsubseteq \\nsubseteqq \\nsucc \\nsucceq ' .
+					'\\nsupseteq \\nsupseteqq \\ntriangleleft \\ntrianglelefteq ' .
+					'\\ntriangleright \\ntrianglerighteq \\nu \\nvdash \\nVdash ' .
+					'\\nvDash \\nVDash \\nwarrow \\odot \\oint \\omega \\Omega ' .
+					'\\ominus \\oplus \\oslash \\otimes ' .
+					'\\P \\parallel \\partial ' .
+					'\\perp \\phi \\Phi \\pi \\Pi \\pitchfork \\pm \\prec ' .
+					'\\precapprox \\preccurlyeq \\preceq \\precnapprox ' .
+					'\\precneqq \\precnsim \\precsim \\prime \\prod \\projlim ' .
+					'\\propto \\psi \\Psi \\qquad \\quad \\Re \\rho \\rightarrow ' .
+					'\\Rightarrow \\rightarrowtail \\rightharpoondown ' .
+					'\\rightharpoonup \\rightleftarrows \\rightrightarrows ' .
+					'\\rightsquigarrow \\rightthreetimes \\risingdotseq ' .
+					'\\Rrightarrow \\Rsh \\rtimes \\rVert \\S ' .
+					'\\scriptscriptstyle \\scriptstyle \\searrow \\setminus ' .
+					'\\sharp \\shortmid \\shortparallel \\sigma \\Sigma \\sim ' .
+					'\\simeq \\smallfrown \\smallsetminus \\smallsmile \\smile ' .
+					'\\spadesuit \\sphericalangle \\sqcap \\sqcup \\sqsubset ' .
+					'\\sqsubseteq \\sqsupset \\sqsupseteq \\square \\star ' .
+					'\\subset \\Subset \\subseteq \\subseteqq \\subsetneq ' .
+					'\\subsetneqq \\succ \\succapprox \\succcurlyeq \\succeq ' .
+					'\\succnapprox \\succneqq \\succnsim \\succsim \\sum ' .
+					'\\supset \\Supset \\supseteq \\supseteqq \\supsetneq ' .
+					'\\supsetneqq \\surd \\swarrow \\tau \\textstyle ' .
+					'\\therefore \\theta \\Theta ' .
+					'\\thickapprox \\thicksim \\times \\to \\top \\triangle ' .
+					'\\triangledown \\triangleleft \\trianglelefteq \\triangleq ' .
+					'\\triangleright \\trianglerighteq ' .
+					'\\upharpoonleft \\upharpoonright \\uplus \\upsilon ' .
+					'\\Upsilon \\upuparrows \\varDelta \\varepsilon \\varGamma ' .
+					'\\varinjlim \\varkappa \\varLambda \\varliminf \\varlimsup ' .
+					'\\varnothing \\varOmega \\varphi \\varPhi \\varpi \\varPhi ' .
+					'\\varprojlim \\varpropto \\varrho \\varsigma \\varSigma ' .
+					'\\varsubsetneq \\varsubsetneqq \\varsupsetneq ' .
+					'\\varsupsetneqq \\vartheta \\varTheta \\vartriangle ' .
+					'\\vartriangleleft \\vartriangleright \\varUpsilon \\varXi ' .
+					'\\vdash \\Vdash \\vDash \\vdots \\vee ' .
+					'\\veebar \\vline \\Vvdash \\wedge ' .
+					'\\wp \\wr \\xi \\Xi \\zeta '
+				],
+			],
+			[
+				'Literals (2)',
+				[
+					'input' =>
+					'\\AA\\Coppa\\coppa\\Digamma\\euro\\geneuro\\geneuronarrow' .
+					'\\geneurowide\\Koppa\\koppa\\officialeuro\\Sampi\\sampi' .
+					'\\Stigma\\stigma\\textvisiblespace\\varstigma',
+					'output' =>
+					'\\mbox{\\AA} \\mbox{\\Coppa} \\mbox{\\coppa} ' .
+					'\\mbox{\\Digamma} \\mbox{\\euro} \\mbox{\\geneuro} ' .
+					'\\mbox{\\geneuronarrow} \\mbox{\\geneurowide} ' .
+					'\\mbox{\\Koppa} \\mbox{\\koppa} \\mbox{\\officialeuro} ' .
+					'\\mbox{\\Sampi} \\mbox{\\sampi} \\mbox{\\Stigma} ' .
+					'\\mbox{\\stigma} \\mbox{\\textvisiblespace} ' .
+					'\\mbox{\\varstigma} '
+				],
+			],
+			[
+				'Literals (2\')',
+				[
+					/* We can parse what we emit (but the ocaml version can't) */
+					'input' =>
+					'\\mbox{\\AA} \\mbox{\\Coppa} \\mbox{\\coppa} ' .
+					'\\mbox{\\Digamma} \\mbox{\\euro} \\mbox{\\geneuro} ' .
+					'\\mbox{\\geneuronarrow} \\mbox{\\geneurowide} ' .
+					'\\mbox{\\Koppa} \\mbox{\\koppa} \\mbox{\\officialeuro} ' .
+					'\\mbox{\\Sampi} \\mbox{\\sampi} \\mbox{\\Stigma} ' .
+					'\\mbox{\\stigma} \\mbox{\\textvisiblespace} ' .
+					'\\mbox{\\varstigma} ',
+					'skipOcaml' => true
+				],
+			],
+			[
+				'Literals (2) MJ',
+				[
+					'usemathrm' => true,
+					'input' =>
+					'\\AA\\Coppa\\coppa\\Digamma\\euro\\geneuro\\geneuronarrow' .
+					'\\geneurowide\\Koppa\\koppa\\officialeuro\\Sampi\\sampi' .
+					'\\Stigma\\stigma\\textvisiblespace\\varstigma',
+					'output' =>
+					'\\mathrm {\\AA} \\mathrm {\\Coppa} \\mathrm {\\coppa} ' .
+					'\\mathrm {\\Digamma} \\mathrm {\\euro} \\mathrm {\\geneuro} ' .
+					'\\mathrm {\\geneuronarrow} \\mathrm {\\geneurowide} ' .
+					'\\mathrm {\\Koppa} \\mathrm {\\koppa} \\mathrm {\\officialeuro} ' .
+					'\\mathrm {\\Sampi} \\mathrm {\\sampi} \\mathrm {\\Stigma} ' .
+					'\\mathrm {\\stigma} \\mathrm {\\textvisiblespace} ' .
+					'\\mathrm {\\varstigma} '
+				],
+			],
+			[
+				'Literals (2\') MJ',
+				[
+					'usemathrm' => true,
+					/* We can parse what we emit (but the ocaml version can't) */
+					'input' =>
+					'\\mathrm {\\AA} \\mathrm {\\Coppa} \\mathrm {\\coppa} ' .
+					'\\mathrm {\\Digamma} \\mathrm {\\euro} \\mathrm {\\geneuro} ' .
+					'\\mathrm {\\geneuronarrow} \\mathrm {\\geneurowide} ' .
+					'\\mathrm {\\Koppa} \\mathrm {\\koppa} \\mathrm {\\officialeuro} ' .
+					'\\mathrm {\\Sampi} \\mathrm {\\sampi} \\mathrm {\\Stigma} ' .
+					'\\mathrm {\\stigma} \\mathrm {\\textvisiblespace} ' .
+					'\\mathrm {\\varstigma} ',
+					'skipOcaml' => true
+				],
+			],
+			[
+				'Literals (3)',
+				[
+					'oldtexvc' => true,
+					'input' =>
+					'\\C\\H\\N\\Q\\R\\Z\\alef\\alefsym\\Alpha\\and\\ang\\Beta' .
+					'\\bull\\Chi\\clubs\\cnums\\Complex\\Dagger\\diamonds\\Doteq' .
+					'\\doublecap\\doublecup\\empty\\Epsilon\\Eta\\exist\\ge' .
+					'\\gggtr\\hArr\\harr\\Harr\\hearts\\image\\infin\\Iota\\isin' .
+					'\\Kappa\\larr\\Larr\\lArr\\le\\lrarr\\Lrarr\\lrArr\\Mu' .
+					'\\natnums\\ne\\Nu\\O\\omicron\\Omicron\\or\\part\\plusmn' .
+					'\\rarr\\Rarr\\rArr\\real\\reals\\Reals\\restriction\\Rho' .
+					'\\sdot\\sect\\spades\\sub\\sube\\supe\\Tau\\thetasym' .
+					'\\varcoppa\\weierp\\Zeta',
+					'output' =>
+					'\\mathbb {C} \\mathbb {H} \\mathbb {N} \\mathbb {Q} ' .
+					'\\mathbb {R} \\mathbb {Z} \\aleph \\aleph \\mathrm {A} ' .
+					'\\land \\angle \\mathrm {B} \\bullet \\mathrm {X} ' .
+					'\\clubsuit \\mathbb {C} \\mathbb {C} \\ddagger ' .
+					'\\diamondsuit \\doteqdot \\Cap \\Cup \\emptyset ' .
+					'\\mathrm {E} \\mathrm {H} \\exists \\geq \\ggg ' .
+					'\\Leftrightarrow \\leftrightarrow \\Leftrightarrow ' .
+					'\\heartsuit \\Im \\infty \\mathrm {I} \\in \\mathrm {K} ' .
+					'\\leftarrow \\Leftarrow \\Leftarrow \\leq ' .
+					'\\leftrightarrow \\Leftrightarrow \\Leftrightarrow ' .
+					'\\mathrm {M} \\mathbb {N} \\neq \\mathrm {N} \\emptyset ' .
+					'oO\\lor \\partial \\pm ' .
+					'\\rightarrow \\Rightarrow \\Rightarrow \\Re \\mathbb {R} ' .
+					'\\mathbb {R} \\upharpoonright \\mathrm {P} \\cdot ' .
+					'\\S \\spadesuit \\subset \\subseteq \\supseteq ' .
+					'\\mathrm {T} \\vartheta \\mbox{\\coppa} \\wp \\mathrm {Z} '
+				],
 			],
-		];
 
-		$this->texVC = new TexVC();
+			// This does not work because (I think) the $DELIMITERS1/2 are not
+			// defined in the scope of the inner array_map()
+			// [
+			// 	'Big',
+			// 	[
+			// 		'input' => implode( '', array_map( function ($b) {
+			// 			return implode( '', array_map( function ($d) {
+			// 				return '\\' . $b . $d;
+			// 			}, $DELIMITERS1 . $DELIMITERS2 . ['\\darr', '\\uarr'] ) );
+			// 		}, $BIGS ) ),
+			// 		'output' => implode( '', array_map( function ($b) {
+			// 			return implode( '', array_map( function ($d) {
+			// 				if ($d === '\\darr') { $d = '\\downarrow'; }
+			// 				if ($d === '\\uarr') { $d = '\\uparrow'; }
+			// 				if (substr($d, 0, 1) === '\\' && strlen($d) > 2) { $d = $d . ' '; }
+			// 				return '{\\' . $b . ' ' . $d . '}';
+			// 			}, $DELIMITERS1 . $DELIMITERS2 . ['\\darr', '\\uarr'] ) );
+			// 		}, $BIGS ) )
+			// 	],
+			// ],
+			// [
+
+			// These take too long to run. I don't know why.
+			// 	'Delimiters (1)',
+			// 	[
+			// 		'input' => implode( '', $DELIMITERS1 ) . implode( ' ', $DELIMITERS2 ) . ' '
+			// 	],
+			// ],
+			// [
+			// 	'Delimiters (2)',
+			// 	[
+			// 		'input' =>
+			// 		'\\darr\\dArr\\Darr\\lang\\rang\\uarr\\uArr\\Uarr',
+			// 		'output' =>
+			// 		'\\downarrow \\Downarrow \\Downarrow \\langle \\rangle ' .
+			// 		'\\uparrow \\Uparrow \\Uparrow '
+			// 	],
+			// ],
+			// [
+			// 	'Delimiters (3)',
+			// 	[
+			// 		'input' =>
+			// 		'\\left' . implode( '\\left', $DELIMITERS1 ) .
+			// 		'\\right' . implode( '\\right', array_reverse( $DELIMITERS1 ) )
+			// 	],
+			// ],
+			// [
+			// 	'Delimiters (4)',
+			// 	[
+			// 		'input' =>
+			// 		'\\left' . implode( '\\left', $DELIMITERS2 ) .
+			// 		'\\right' . implode( '\\right', array_reverse( $DELIMITERS2 ) )
+			// 	],
+			// ],
+			// [
+			// 	'Delimiters (5)',
+			// 	[
+			// 		'input' =>
+			// 		'\\left\\darr \\left\\dArr \\left\\Darr \\left\\lang ' .
+			// 		'\\right\\rang \\right\\uarr \\right\\uArr \\right\\Uarr ',
+			// 		'output' =>
+			// 		'\\left\\downarrow \\left\\Downarrow \\left\\Downarrow ' .
+			// 		'\\left\\langle \\right\\rangle ' .
+			// 		'\\right\\uparrow \\right\\Uparrow \\right\\Uparrow '
+			// 	],
+			// ],
+
+			[
+				'FUN_AR1',
+				[
+					'input' =>
+					'\\acute{A}\\bar{A}\\bcancel{A}\\bmod{A}\\boldsymbol{A}' .
+					'\\breve{A}\\cancel{A}\\check{A}\\ddot{A}\\dot{A}\\emph{A}' .
+					'\\grave{A}\\hat{A}\\hphantom{A}\\mathbb{A}\\mathbf{A}' .
+					'\\mathcal{A}\\mathclose{A}\\mathfrak{A}\\mathit{A}' .
+					'\\mathop{A}\\mathopen{A}\\mathord{A}\\mathpunct{A}' .
+					'\\mathrm{A}\\mathsf{A}\\mathtt{A}' .
+					'\\operatorname{A}\\overleftarrow{A}\\overleftrightarrow{A}' .
+					'\\overline{A}\\overrightarrow{A}\\phantom{A}\\pmod{A}\\sqrt{A}' .
+					'\\textbf{A}\\textit{A}\\textrm{A}\\textsf{A}\\texttt{A}' .
+					'\\tilde{A}\\underline{A}\\vec{A}\\vphantom{A}\\widehat{A}' .
+					'\\widetilde{A}\\xcancel{A}',
+					'output' =>
+					'{\\acute {A}}{\\bar {A}}{\\bcancel {A}}{\\bmod {A}}' .
+					'{\\boldsymbol {A}}{\\breve {A}}{\\cancel {A}}{\\check {A}}' .
+					'{\\ddot {A}}{\\dot {A}}{\\emph {A}}{\\grave {A}}{\\hat {A}}' .
+					'{\\hphantom {A}}\\mathbb {A} \\mathbf {A} {\\mathcal {A}}' .
+					'{\\mathclose {A}}{\\mathfrak {A}}{\\mathit {A}}' .
+					'\\mathop {A} {\\mathopen {A}}{\\mathord {A}}' .
+					'{\\mathpunct {A}}\\mathrm {A} {\\mathsf {A}}' .
+					'{\\mathtt {A}}\\operatorname {A} {\\overleftarrow {A}}' .
+					'{\\overleftrightarrow {A}}{\\overline {A}}' .
+					'{\\overrightarrow {A}}{\\phantom {A}}{\\pmod {A}}{\\sqrt {A}}' .
+					'{\\textbf {A}}{\\textit {A}}{\\textrm {A}}{\\textsf {A}}' .
+					'{\\texttt {A}}{\\tilde {A}}{\\underline {A}}{\\vec {A}}' .
+					'{\\vphantom {A}}{\\widehat {A}}{\\widetilde {A}}{\\xcancel {A}}',
+					'skipOcaml' => 'double spacing and extra braces'
+				],
+			],
+			[
+				'FUN_AR1 (2)',
+				[
+					'oldtexvc' => true,
+					'input' => '\\Bbb{foo}\\bold{bar}',
+					'output' => '{\\mathbb {foo}}{\\mathbf {bar}}',
+					'skipOcaml' => 'double spacing',
+					'skipReparse' => 'spacing'
+				],
+			],
+			[
+				'FUN_AR1NB (1)',
+				[
+					'input' => '\\operatorname {sin} ',
+					'skipOcaml' => 'missing space'
+				],
+			],
+			[
+				'FUN_AR1NB (2)',
+				[
+					'input' => '\\mathbb {A} \\mathbf {B} \\mathrm {C} ',
+					'skipOcaml' => 'extra braces'
+				],
+			],
+			[
+				'FUN_AR1NB (3)',
+				[
+					'input' => '\\overbrace {A} _{b}^{c}\\underbrace {C} _{d}^{e}',
+					'skipOcaml' => 'ocaml bug'
+				],
+			],
+			[
+				'FUN_AR1NB (4)',
+				[
+					'input' => '\\xleftarrow{A}\\xrightarrow{A}',
+					'output' => '\\xleftarrow {A} \\xrightarrow {A} '
+				],
+			],
+			[
+				'FUN_AR1NB (5)',
+				[
+					'input' => '\\mathrel{A}\\mathbin{A}',
+					'output' => '\\mathrel {A} \\mathbin {A} '
+				],
+			],
+			[
+				'FUN_AR1OPT',
+				[
+					'input' =>
+					'\\sqrt{2}\\sqrt[3]{2}' .
+					'\\xleftarrow{above}\\xleftarrow[below]{above}' .
+					'\\xrightarrow{above}\\xrightarrow[below]{above}',
+					'output' =>
+					'{\\sqrt {2}}{\\sqrt[{3}]{2}}' .
+					'\\xleftarrow {above} {\\xleftarrow[{below}]{above}}' .
+					'\\xrightarrow {above} {\\xrightarrow[{below}]{above}}',
+					'skipOcaml' => 'spacing'
+				],
+			],
+			[
+				'FUN_AR2',
+				[
+					'input' =>
+					'\\binom{A}{B}\\cancelto{A}{B}\\cfrac{A}{B}\\dbinom{A}{B}' .
+					'\\dfrac{A}{B}\\frac{A}{B}\\overset{A}{B}\\stackrel{A}{B}' .
+					'\\tbinom{A}{B}\\tfrac{A}{B}\\underset{A}{B}',
+					'output' =>
+					'{\\binom {A}{B}}{\\cancelto {A}{B}}{\\cfrac {A}{B}}' .
+					'{\\dbinom {A}{B}}{\\dfrac {A}{B}}{\\frac {A}{B}}' .
+					'{\\overset {A}{B}}{\\stackrel {A}{B}}{\\tbinom {A}{B}}' .
+					'{\\tfrac {A}{B}}{\\underset {A}{B}}',
+					'skipOcaml' => 'double spacing'
+				],
+			],
+			[
+				'FUN_AR2nb',
+				[
+					'input' => '\\sideset{_\\dagger^*}{_\\dagger^*}\\prod',
+					'output' => '\\sideset {_{\\dagger }^{*}}{_{\\dagger }^{*}}\\prod '
+				],
+			],
+			[
+				'FUN_INFIX (1)',
+				[
+					'input' => '\\left({a\\atop 1}{b\\atop m}{c\\atop n}\\right)',
+					'output' => '\\left({a \\atop 1}{b \\atop m}{c \\atop n}\\right)'
+				],
+			],
+			[
+				'FUN_INFIX (2)',
+				[
+					'input' => '{1\\,0\\choose0\\,1}',
+					'output' => '{1\\,0 \\choose 0\\,1}'
+				],
+			],
+			[
+				'FUN_INFIX (3)',
+				[
+					'input' => '{a\\over b}',
+					'output' => '{a \\over b}'
+				],
+			],
+			[
+				'FUN_INFIX (4)',
+				[
+					'input' => 'a\\over b',
+					'output' => '{a \\over b}'
+				],
+			],
+			[
+				'DECLh',
+				[
+					'input' => '{abc \\rm def \\it ghi \\cal jkl \\bf mno}',
+					'output' => '{abc{\\rm {def{\\it {ghi{\\cal {jkl{\\bf {mno}}}}}}}}}'
+				],
+			],
+			[
+				'litsq_zq',
+				[
+					'input' => ']^2',
+					'output' => ']^{2}'
+				],
+			],
+			[
+				'Matrices (1)',
+				[
+					'input' => array_map(
+						function ($env) {
+							return '\\begin{' . $env . '}' . arg($env) . ' a & b \\\\\\hline c & d \\end{' . $env . '}';
+						},
+						$ENV ),
+					'output' => array_map(
+						function ($env) {
+							if ($env === 'align') { $env = 'aligned'; }
+							if ($env === 'alignat') { $env = 'alignedat'; }
+							return '{\\begin{' . $env . '}' . arg($env) . 'a&b\\\\\\hline c&d\\end{' . $env . '}}';
+						},
+						$ENV ),
+			    ],
+			],
+			[
+				'Matrices (2)',
+				[
+					'input' => '{\\begin{array}{|c|}\\hline {\\!n\\!}\\\\\\hline \\end{array}}'
+				],
+			],
+			[
+				'Matrices (3)',
+				[
+					'input' => '\\begin{alignedat} { 3 } a & b & c \\end{alignedat}',
+					'output' => '{\\begin{alignedat}{3}a&b&c\\end{alignedat}}'
+				],
+			],
+			[
+				'Color (1)',
+				[
+					'input' => '\\definecolor {mycolor}{rgb}{0.1,.2,0.}\\color {mycolor}'
+				],
+			],
+			[
+				'Color (2)',
+				[
+					'input' =>
+					'\\color {blue}\\color [named]{blue}\\color [gray]{0.5}' .
+					'\\color [rgb]{0,1,0}\\color [cmyk]{1,0,0,0}'
+				],
+			],
+			[
+				'Color (3)',
+				[
+					'input' =>
+					'\\pagecolor {blue}\\pagecolor [named]{blue}' .
+					'\\pagecolor [gray]{0.5}\\pagecolor [rgb]{0,1,0}' .
+					'\\pagecolor [cmyk]{1,0,0,0}'
+				],
+			],
+			[
+				'Color (4)',
+				[
+					'input' =>
+					'\\definecolor{mycolor}{rgb}{0.1,.2,0.}\\color[CMYK]{0,1,0,1}',
+					'output' =>
+					'\\definecolor {mycolor}{rgb}{0.1,.2,0.}\\color [cmyk]{0,1,0,1}'
+				],
+			],
+			[
+				'Color (5)',
+				[
+					'input' =>
+					'\\definecolor{mycolor}{RGB}{255,102,51}' .
+					'\\pagecolor [RGB]{51,102,255}',
+					'output' =>
+					'\\definecolor {mycolor}{rgb}{1,0.4,0.2}' .
+					'\\pagecolor [rgb]{0.2,0.4,1}'
+				],
+			],
+			[
+				'Unicode',
+				[
+					'input' => '{\\mbox{💩\uD83D\uDCA9}}'
+				],
+			],
+		];
 	}
 
-	public function testRunCases() {
-		// Prefilter cases with errors
-		$this->testCases = array_slice( $this->testCases, 0, 1 );
-		foreach ( $this->testCases as $title => $tc ) {
-			$tc["output"] = $tc["output"] ?? $tc["input"];
-			if ( !array_key_exists( "skipJs", $tc ) || !$tc["skipJs"] ) {
-				$message = "output should be correct";
-				$result = $this->texVC->check( $tc["input"], [
-					"debug" => true,
-					"usemathrm" => $tc["usemathrm"] ?? false,
-					"oldtexvc" => $tc["oldtexvc"] ?? false
-				] );
-				$this->assertEquals( "+", $result["status"], $message );
-				$this->assertEquals( $result["output"],  $tc["output"], $message );
-			}
-			if ( !array_key_exists( "skipReparse", $tc ) || !$tc["skipReparse"] ) {
-				// verify that the output doesn't change if we feed it
-				// through again.
-				$message = "should parse its own output";
-				$result1 = $this->texVC->check( $tc["output"],  [ "debug" => true ] );
-				$result2 = $this->texVC->check( $result1["output"], [ "debug" => true ] );
-				$this->assertEquals( '+', $result2["status"], $message );
-				$this->assertEquals( $result2["output"], $result1["output"], $message );
-			}
-			if ( !array_key_exists( "skipOcaml", $tc ) || !$tc["skipOcaml"] ) {
-				$message = "should match ocaml output";
-				// nyi tryocaml($tc["input"],$tc["output"], done # is async)
+	/**
+	 * @dataProvider provideTestCases
+	 */
+	public function testRunCases( $title, $tc ) {
+		$tc["output"] = $tc["output"] ?? $tc["input"];
+		if ( !array_key_exists( "skipJs", $tc ) || !$tc["skipJs"] ) {
+			$message = "output should be correct ($title)";
+			$result = $this->texVC->check( $tc["input"], [
+				"debug" => true,
+				"usemathrm" => $tc["usemathrm"] ?? false,
+				"oldtexvc" => $tc["oldtexvc"] ?? false
+			] );
+			$this->assertEquals( "+", $result["status"], $message );
+			$this->assertEquals( $tc["output"], $result["output"], $message );
+		}
+		if ( !array_key_exists( "skipReparse", $tc ) || !$tc["skipReparse"] ) {
+			// verify that the output doesn't change if we feed it
+			// through again.
+			$message = "should parse its own output ($title)";
+			$result1 = $this->texVC->check( $tc["output"],  [ "debug" => true ] );
+			$result2 = $this->texVC->check( $result1["output"], [ "debug" => true ] );
+			$this->assertEquals( '+', $result2["status"], $message );
+			$this->assertEquals( $result2["output"], $result1["output"], $message );
+		}
+		if ( !array_key_exists( "skipOcaml", $tc ) || !$tc["skipOcaml"] ) {
+			$message = "should match ocaml output ($title)";
+			// nyi tryocaml($tc["input"],$tc["output"], done # is async)
 
-			} elseif ( $tc["skipOcaml"] === "double spacing" ) {
-				$message = "should match ocaml output (except for spacing)";
-				// nyi tryocaml($tc["input"],$tc["output"], done # is async, true)
-			}
+		} elseif ( $tc["skipOcaml"] === "double spacing" ) {
+			$message = "should match ocaml output (except for spacing) ($title)";
+			// nyi tryocaml($tc["input"],$tc["output"], done # is async, true)
 		}
 	}
 }
