You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by GitBox <gi...@apache.org> on 2022/10/28 23:02:07 UTC

[GitHub] [daffodil] tuxji commented on a diff in pull request #865: RFC: Refactor integration tests for clarity and speed

tuxji commented on code in PR #865:
URL: https://github.com/apache/daffodil/pull/865#discussion_r1008562457


##########
daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala:
##########
@@ -1363,18 +1376,18 @@ object Main {
                 }
             }
             val formatStr = maxCols.map(max => "%" + -max + "s").mkString("  ")
-            println(formatStr.format(headers: _*))
+            STDOUT.println(formatStr.format(headers: _*))

Review Comment:
   We have a bunch of other places in other Daffodil source files which call `println` or `System.out.println` or `System.err.println` or `Console.out.println`.  These places probably get called only in special circumstances which won't affect the integration tests, but it's something we may have to watch out for and keep in mind.  If it turns out to be necessary, we can pass around a PrintStream to be called or define a smart Daffodil println function to replace some of these calls.



##########
daffodil-cli/src/it/scala/org/apache/daffodil/parsing/TestCLIParsing.scala:
##########
@@ -265,1124 +157,547 @@ class TestCLIparsing {
   //  verifies the expected output. If this test fails, it likely means we've
   //  broken our attempts to create consistent prefix mappings.
   @Test def test_1585_CLI_Parsing_MultifileSchema_methodImportSameDir(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_14.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output9))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_14.dfdl.xsd")
+
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output9.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1586_CLI_Parsing_MultifileSchema_methodIncludeSameDir(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
 
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output10))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output10.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1587_CLI_Parsing_MultifileSchema_methodImportSameDir2(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_16.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_16.dfdl.xsd")
 
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output10))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output10.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1317_IBMCompatibility_ABC_test_ibm_abc_cli(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/ABC_IBM.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo abcabcabc| %s parse -s %s -r ABC", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/ABC_IBM.dfdl.xsd")
 
-      shell.expect(contains(output8))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r ABC") { cli =>
+      cli.sendLine("abcabcabc", inputDone = true)
+      cli.expect(Util.getExpectedString("output8.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_977_CLI_Parsing_SimpleParse_stdOut(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_978_CLI_Parsing_SimpleParse_outFile(): Unit = {
-    val tmp_filename: String = (System.currentTimeMillis / 1000).toString()
-    val file = new File(tmp_filename)
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix -o %s", Util.binPath, testSchemaFile, tmp_filename)
-      shell.sendLine(cmd)
-
-      val catCmd = if (Util.isWindows) "type" else "cat"
-      val openCmd = String.format("%s %s", catCmd, tmp_filename)
-
-      shell.sendLine(openCmd)
-      shell.expect(contains("<tns:cell>2</tns:cell>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-      assertTrue("Failed to remove temporary file: %s".format(file), file.delete)
+    withTempFile { tempFile =>
+      val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+      val tempPath = tempFile.getAbsolutePath()
+
+      runCLI(args"parse -s $schema -r matrix -o $tempPath") { cli =>
+        cli.sendLine("0,1,2", inputDone = true)
+      } (ExitCode.LeftOverData)
+
+      val res = FileUtils.readFileToString(tempFile, StandardCharsets.UTF_8)
+      assertTrue(res.contains("<tns:cell>2</tns:cell>"))
     }
   }
 
   @Test def test_979_CLI_Parsing_SimpleParse_inFile(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
+
+    runCLI(args"parse -s $schema -r matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_980_CLI_Parsing_SimpleParse_stOutDash(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r matrix -o - %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
+
+    runCLI(args"parse -s $schema -r matrix -o - $input") { cli =>
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_981_CLI_Parsing_SimpleParse_stdInDash(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse -s %s -r matrix -", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output2))
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expect(Util.getExpectedString("output2.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_983_CLI_Parsing_SimpleParse_verboseMode(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      shell.sendLine(String.format("echo 0,1| %s -v parse -s %s -r matrix -", Util.binPath, testSchemaFile))
-      shell.expectIn(1, contains("[info]"))
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      shell.sendLine(String.format("echo 0,1| %s -vv parse -s %s -r matrix -", Util.binPath, testSchemaFile))
-      shell.expectIn(1, contains("[debug]"))
+    runCLI(args"-v parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1", inputDone = true)
+      cli.expectErr("[info]")
+    } (ExitCode.LeftOverData)
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.send("exit\n")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"-vv parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1", inputDone = true)
+      cli.expectErr("[debug]")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_984_CLI_Parsing_negativeTest(): Unit = {
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse", Util.binPath)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("There should be exactly one of the following options: schema, parser"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.send("exit\n")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expectErr("There should be exactly one of the following options: schema, parser")
+    } (ExitCode.Usage)
   }
 
   @Test def test_985_CLI_Parsing_SimpleParse_defaultRoot(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output2))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expect(Util.getExpectedString("output2.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_988_CLI_Parsing_SimpleParse_specifiedRoot(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      //val expected = """<tns:hcp2 xmlns:tns="http://www.example.org/example1/">12</tns:hcp2>"""
-      val cmd = String.format("echo 12| %s parse -s %s -r hcp2", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:hcp2"))
-      shell.expect(contains("12"))
-      shell.expect(contains("</tns:hcp2>"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r hcp2") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expect("<tns:hcp2")
+      cli.expect("12")
+      cli.expect("</tns:hcp2>")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_996_CLI_Parsing_negativeTest04(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -r unknown", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("No root element found for unknown in any available namespace"))
-
-      Util.expectExitCode(ExitCode.UnableToCreateProcessor, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r unknown") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("No root element found for unknown in any available namespace")
+    } (ExitCode.UnableToCreateProcessor)
   }
 
   @Test def test_997_CLI_Parsing_multSchemas(): Unit = {
-    val schemaFile1 = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val schemaFile2 = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val (testSchemaFile1, testSchemaFile2) = if (Util.isWindows) (Util.cmdConvert(schemaFile1), Util.cmdConvert(schemaFile2)) else (schemaFile1, schemaFile2)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -s %s  -r hcp2", Util.binPath, testSchemaFile1, testSchemaFile2)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments for option 'schema'"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema1 = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val schema2 = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+
+    runCLI(args"parse -s $schema1 -s $schema2 -r hcp2") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Bad arguments for option 'schema'")
+    } (ExitCode.Usage)
   }
 
   @Test def test_3661_CLI_Parsing_badSchemaPath(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/doesnotexist.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -r root", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments for option 'schema'"))
-      shell.expectIn(1, contains("Could not find file or resource"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/doesnotexist.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r root") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Bad arguments for option 'schema'")
+      cli.expectErr("Could not find file or resource")
+    } (ExitCode.Usage)
   }
 
   @Test def test_1002_CLI_Parsing_negativeTest03(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -P parserThatDoesNotExist", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      if (Util.isWindows) {
-        shell.expectIn(1, contains("parserThatDoesNotExist (The system cannot find the file specified)"))
-      } else {
-        shell.expectIn(1, contains("parserThatDoesNotExist (No such file or directory)"))
-      }
 
-      Util.expectExitCode(ExitCode.FileNotFound, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -P parserThatDoesNotExist") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("parserThatDoesNotExist")
+    } (ExitCode.FileNotFound)
   }
 
   @Test def test_1003_CLI_Parsing_SimpleParse_emptyNamespace(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r {}address %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output4))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
+
+    runCLI(args"parse -s $schema -r {}address $input") { cli =>
+      cli.expect(Util.getExpectedString("output4.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1004_CLI_Parsing_SimpleParse_namespaceUsed(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r {target}matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output6))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
+
+    runCLI(args"parse -s $schema -r {target}matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output6.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_2615_CLI_Parsing_SimpleParse_namespaceUsedLongOpt(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s --root {target}matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output6))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
+
+    runCLI(args"parse -s $schema --root {target}matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output6.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1005_CLI_Parsing_SimpleParse_rootPath(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.startNoConvert("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      //val expected = """<tns:hcp2 xmlns:tns="http://www.example.org/example1/">12</tns:hcp2>"""
-      val cmd = String.format(Util.echoN("12") + "| %s parse -s %s -r hcp2 -p /", Util.binPath, testSchemaFile)
-
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:hcp2 xmlns:tns=\"http://www.example.org/example1/\">"))
-      shell.expect(contains("12"))
-      shell.expect(contains("</tns:hcp2>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r hcp2 -p /") { cli =>
+      cli.send("12", inputDone = true)
+      cli.expect("<tns:hcp2 xmlns:tns=\"http://www.example.org/example1/\">")
+      cli.expect("12")
+      cli.expect("</tns:hcp2>")
+    } (ExitCode.Success)
   }
 
   @Test def test_1015_CLI_Parsing_SimpleParse_defaultRootMultSchema(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output4))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect(Util.getExpectedString("output4.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationOn(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate on", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate on") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validation_missing_mode(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments"))
-      shell.expectIn(1, contains("validate"))
-      shell.expectIn(1, contains("exactly one argument"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r matrix --validate") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("Bad arguments")
+      cli.expectErr("validate")
+      cli.expectErr("exactly one argument")
+    } (ExitCode.Usage)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationLimited(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate limited", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate limited") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationOff(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate off", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate off") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationFooBar(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse --validate FooBar -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("FooBar"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
-  }
-
-  /*
-  //On hold until I implement a way to set the classpath before executing
-  @Test def test_1313_CLI_Parsing_assertionFailure() {
-    val cmd = "echo unacceptable| " + Util.binPath + " parse -s daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_B_08.dfdl.xsd -s daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_C_08.dfdl.xsd --root bElem2\n"
-    val shell = Util.start(cmd)
-    shell.expect(contains("Parse Error: Assertion failed. Assertion failed for dfdl:checkConstraints(.)"))
-
-    shell.send("exit\n")
-    shell.expect(eof)
-    shell.close()
+    runCLI(args"parse --validate FooBar -s $schema -r matrix") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("FooBar")
+    } (ExitCode.Usage)
   }
-*/
 
   @Test def test_1319_CLI_Parsing_invalidElementSDE(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/ABC_IBM_invalid.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo ababababbaacccccb| %s parse -s %s -r ABC", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("'fixed' is not a valid"))
-
-      Util.expectExitCode(ExitCode.UnableToCreateProcessor, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/ABC_IBM_invalid.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r ABC") { cli =>
+      cli.sendLine("ababababbaacccccb", inputDone = true)
+      cli.expectErr("'fixed' is not a valid")
+    } (ExitCode.UnableToCreateProcessor)
   }
 
   @Test def test_1346_CLI_Parsing_SimpleParse_defaultRootMultSchemaMultiple(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
     for (x <- 1 to 10) {
-      val shell = Util.start("")
-
-      try {
-        println("Run " + x + " of 10")
-        shell.sendLine(cmd)
-        shell.expect(contains(output4))
-
-        Util.expectExitCode(ExitCode.Success, shell)
-        shell.sendLine("exit")
-        shell.expect(eof)
-      } finally {
-        shell.close()
-      }
+      runCLI(args"parse -s $schema $input") { cli =>
+        cli.expect(Util.getExpectedString("output4.txt"))
+      } (ExitCode.Success)
     }
   }
 
   @Test def test_1386_CLI_Parsing_negativeTest05(): Unit = {
-    val cmd = String.format("echo 12| %s", Util.binPath)
-    val shell = Util.start("")
-
-    try {
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Subcommand required"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Subcommand required")
+    } (ExitCode.Usage)
   }
 
   @Test def test_1971_CLI_Parsing_traceMode01(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo test| %s -t parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("parser: <Element name='rabbitHole'>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
+
+    runCLI(args"-t parse -s $schema") { cli =>
+      cli.sendLine("test", inputDone = true)
+      cli.expect("parser: <Element name='rabbitHole'>")
+    } (ExitCode.Success)
   }
 
   @Test def test_1973_CLI_Parsing_traceMode03(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3,,,,| %s -t parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 56 bit(s) with at least"))
-      shell.expectIn(1, contains("Left over data (Hex) starting at byte 8 is: ("))
-      shell.expectIn(1, contains("Left over data (UTF-8) starting at byte 8 is: ("))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-      //assert(shell.getExitValue() == 1)
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"-t parse -s $schema") { cli =>
+      cli.sendLine("0,1,2,3,,,,", inputDone = true)
+      cli.expectErr("Left over data. Consumed 56 bit(s) with at least")
+      cli.expectErr("Left over data (Hex) starting at byte 8 is: (")
+      cli.expectErr("Left over data (UTF-8) starting at byte 8 is: (")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_1941_CLI_Parsing_SimpleParse_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 1,2,3,4,,,| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 56 bit(s) with at least"))
-      shell.expectIn(1, contains("Left over data (Hex) starting at byte 8 is: ("))
-      shell.expectIn(1, contains("Left over data (UTF-8) starting at byte 8 is: ("))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r matrix") { cli =>
+      cli.sendLine("1,2,3,4,,,", inputDone = true)
+      cli.expectErr("Left over data. Consumed 56 bit(s) with at least")
+      cli.expectErr("Left over data (Hex) starting at byte 8 is: (")
+      cli.expectErr("Left over data (UTF-8) starting at byte 8 is: (")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_LSBPartialByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r lsbPartialByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r lsbPartialByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
         + "\nLeft over data starts with partial byte. Left over data (Binary) at byte 2 is: (0b011101xx)"
         + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_MSBPartialByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r msbPartialByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r msbPartialByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
         + "\nLeft over data starts with partial byte. Left over data (Binary) at byte 2 is: (0bxx110100)"
         + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_MSBFullByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r msbFullByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 16 bit(s) with at least 16 bit(s) remaining."
-        + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r msbFullByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 16 bit(s) with at least 16 bit(s) remaining."
+        + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_DFDL_714(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:elem xmlns:tns=\"http://baseSchema.com\">"))
-      shell.expect(contains("<content"))
-      shell.expect(contains("Hello World"))
-      shell.expect(contains("</tns:elem>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect("<tns:elem xmlns:tns=\"http://baseSchema.com\">")
+      cli.expect("<content")
+      cli.expect("Hello World")
+      cli.expect("</tns:elem>")
+    } (ExitCode.Success)
   }
 
   @Test def test_DFDL_1203_schema_from_jar(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("", envp = Map("DAFFODIL_CLASSPATH" -> Util.daffodilPath("daffodil-cli/target/scala-2.10/*")))
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:elem xmlns:tns=\"http://baseSchema.com\">"))
-      shell.expect(contains("<content"))
-      shell.expect(contains("Hello World"))
-      shell.expect(contains("</tns:elem>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect("<tns:elem xmlns:tns=\"http://baseSchema.com\">")
+      cli.expect("<content")
+      cli.expect("Hello World")
+      cli.expect("</tns:elem>")
+    } (ExitCode.Success)
   }
 
   @Test def test_3606_CLI_Parsing_SimpleParse_largeInfoset(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
+    runCLI(args"parse -s $schema -r matrix") { cli =>
       val longInput = "0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64"
-      val cmd = String.format("echo %s| %s parse -s %s -r matrix", longInput, Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      val result = shell.expect(contains("<tns:row")).getBefore()
-      println(result)
+      cli.sendLine(longInput, inputDone = true)
+      val result = cli.expect("<tns:row").getBefore()
       if (result.contains("""<tns:matrix xmlns:tns="http://www.example.org/example1/"><tns:matrix xmlns:tns="http://www.example.org/example1/">""")) {
         throw new Exception("Error - Root has been duplicated")
       }
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_built_in_formats(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_04.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r e %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_04.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
 
-      shell.expectIn(1, contains("Schema Definition Warning"))
-      shell.expectIn(1, contains("edu/illinois/ncsa/daffodil/xsd/built-in-formats.xsd"))
-      shell.expectIn(1, contains("org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("quit")
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r e $input") { cli =>
+      cli.expectErr("Schema Definition Warning")
+      cli.expectErr("edu/illinois/ncsa/daffodil/xsd/built-in-formats.xsd")
+      cli.expectErr("org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd")
+    } (ExitCode.Success)
   }
 
   // These DAFFODIL_JAVA_OPTS values change the Java defaults classes like
   // SAXParserFactory and SchemaFactory to be Java's internal classes instead
   // of those provided by dependencies (e.g. Xerces) included with Daffodil.
   // Some places require dependency version of these classes. This test ensures
   // that we override defaults when necesssary
+  /*
+   * TODO: Update new API to support a way to set DAFFODIL_JAVA_OPTS. Maybe
+   * runCLI optionally accepts environment options, and they are provided it
+   * switches from using a thread to forking a process.
+   *
   @Test def test_CLI_Parsing_JavaDefaults(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
     val java_opts = Map("DAFFODIL_JAVA_OPTS" ->
       ("-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl " +
         "-Djavax.xml.xml.validation.SchemaFactory=com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory"))

Review Comment:
   OK, this test is indeed another test which sets DAFFODIL_JAVA_OPTS in a purposeful way.  However, maybe these properties can be set at runtime with `System.setProperty` too.  I would try that first before having to keep the old API around or make runCLI switch from using a thread to starting a process.



##########
daffodil-cli/src/it/scala/org/apache/daffodil/CLI/Util.scala:
##########
@@ -17,18 +17,37 @@
 
 package org.apache.daffodil.CLI
 
+import scala.collection.mutable
+
 import org.apache.daffodil.util.Misc
 import net.sf.expectit.ExpectBuilder
 import net.sf.expectit.Expect
 import net.sf.expectit.filter.Filters.replaceInString
 import net.sf.expectit.matcher.Matchers.contains
+import net.sf.expectit.matcher.Matcher
+import net.sf.expectit.Result
 import org.apache.daffodil.Main.ExitCode
+import org.apache.daffodil.Main
 
 import java.nio.file.Paths
 import java.io.{File, PrintWriter}
 import java.util.concurrent.TimeUnit
 import org.apache.daffodil.xml.XMLUtils
 import org.junit.Assert.fail
+import org.junit.Assert.assertEquals
+
+import java.io.PipedInputStream
+import java.io.PipedOutputStream
+import java.io.PrintStream
+import java.io.InputStream
+import java.io.OutputStream
+
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.core.layout.PatternLayout
+import org.apache.logging.log4j.core.config.Configurator
+import org.apache.logging.log4j.core.appender.OutputStreamAppender
+import org.apache.logging.log4j.core.config.AbstractConfiguration
+import org.apache.logging.log4j.core.config.ConfigurationSource
 
 object Util {

Review Comment:
   Once all of the integration tests are converted to the new API, we should remove as many unused definitions from Util.scala as possible (daffodilPath, binPath, start, startNoConvert, getShell, cmdConvert, etc.).



##########
daffodil-cli/src/it/scala/org/apache/daffodil/parsing/TestCLIParsing.scala:
##########
@@ -265,1124 +157,547 @@ class TestCLIparsing {
   //  verifies the expected output. If this test fails, it likely means we've
   //  broken our attempts to create consistent prefix mappings.
   @Test def test_1585_CLI_Parsing_MultifileSchema_methodImportSameDir(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_14.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output9))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_14.dfdl.xsd")
+
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output9.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1586_CLI_Parsing_MultifileSchema_methodIncludeSameDir(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
 
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output10))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output10.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1587_CLI_Parsing_MultifileSchema_methodImportSameDir2(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_16.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_16.dfdl.xsd")
 
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output10))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output10.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1317_IBMCompatibility_ABC_test_ibm_abc_cli(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/ABC_IBM.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo abcabcabc| %s parse -s %s -r ABC", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/ABC_IBM.dfdl.xsd")
 
-      shell.expect(contains(output8))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r ABC") { cli =>
+      cli.sendLine("abcabcabc", inputDone = true)
+      cli.expect(Util.getExpectedString("output8.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_977_CLI_Parsing_SimpleParse_stdOut(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_978_CLI_Parsing_SimpleParse_outFile(): Unit = {
-    val tmp_filename: String = (System.currentTimeMillis / 1000).toString()
-    val file = new File(tmp_filename)
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix -o %s", Util.binPath, testSchemaFile, tmp_filename)
-      shell.sendLine(cmd)
-
-      val catCmd = if (Util.isWindows) "type" else "cat"
-      val openCmd = String.format("%s %s", catCmd, tmp_filename)
-
-      shell.sendLine(openCmd)
-      shell.expect(contains("<tns:cell>2</tns:cell>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-      assertTrue("Failed to remove temporary file: %s".format(file), file.delete)
+    withTempFile { tempFile =>
+      val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+      val tempPath = tempFile.getAbsolutePath()
+
+      runCLI(args"parse -s $schema -r matrix -o $tempPath") { cli =>
+        cli.sendLine("0,1,2", inputDone = true)
+      } (ExitCode.LeftOverData)
+
+      val res = FileUtils.readFileToString(tempFile, StandardCharsets.UTF_8)
+      assertTrue(res.contains("<tns:cell>2</tns:cell>"))
     }
   }
 
   @Test def test_979_CLI_Parsing_SimpleParse_inFile(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
+
+    runCLI(args"parse -s $schema -r matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_980_CLI_Parsing_SimpleParse_stOutDash(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r matrix -o - %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
+
+    runCLI(args"parse -s $schema -r matrix -o - $input") { cli =>
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_981_CLI_Parsing_SimpleParse_stdInDash(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse -s %s -r matrix -", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output2))
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expect(Util.getExpectedString("output2.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_983_CLI_Parsing_SimpleParse_verboseMode(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      shell.sendLine(String.format("echo 0,1| %s -v parse -s %s -r matrix -", Util.binPath, testSchemaFile))
-      shell.expectIn(1, contains("[info]"))
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      shell.sendLine(String.format("echo 0,1| %s -vv parse -s %s -r matrix -", Util.binPath, testSchemaFile))
-      shell.expectIn(1, contains("[debug]"))
+    runCLI(args"-v parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1", inputDone = true)
+      cli.expectErr("[info]")
+    } (ExitCode.LeftOverData)
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.send("exit\n")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"-vv parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1", inputDone = true)
+      cli.expectErr("[debug]")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_984_CLI_Parsing_negativeTest(): Unit = {
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse", Util.binPath)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("There should be exactly one of the following options: schema, parser"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.send("exit\n")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expectErr("There should be exactly one of the following options: schema, parser")
+    } (ExitCode.Usage)
   }
 
   @Test def test_985_CLI_Parsing_SimpleParse_defaultRoot(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output2))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expect(Util.getExpectedString("output2.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_988_CLI_Parsing_SimpleParse_specifiedRoot(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      //val expected = """<tns:hcp2 xmlns:tns="http://www.example.org/example1/">12</tns:hcp2>"""
-      val cmd = String.format("echo 12| %s parse -s %s -r hcp2", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:hcp2"))
-      shell.expect(contains("12"))
-      shell.expect(contains("</tns:hcp2>"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r hcp2") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expect("<tns:hcp2")
+      cli.expect("12")
+      cli.expect("</tns:hcp2>")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_996_CLI_Parsing_negativeTest04(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -r unknown", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("No root element found for unknown in any available namespace"))
-
-      Util.expectExitCode(ExitCode.UnableToCreateProcessor, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r unknown") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("No root element found for unknown in any available namespace")
+    } (ExitCode.UnableToCreateProcessor)
   }
 
   @Test def test_997_CLI_Parsing_multSchemas(): Unit = {
-    val schemaFile1 = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val schemaFile2 = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val (testSchemaFile1, testSchemaFile2) = if (Util.isWindows) (Util.cmdConvert(schemaFile1), Util.cmdConvert(schemaFile2)) else (schemaFile1, schemaFile2)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -s %s  -r hcp2", Util.binPath, testSchemaFile1, testSchemaFile2)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments for option 'schema'"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema1 = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val schema2 = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+
+    runCLI(args"parse -s $schema1 -s $schema2 -r hcp2") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Bad arguments for option 'schema'")
+    } (ExitCode.Usage)
   }
 
   @Test def test_3661_CLI_Parsing_badSchemaPath(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/doesnotexist.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -r root", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments for option 'schema'"))
-      shell.expectIn(1, contains("Could not find file or resource"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/doesnotexist.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r root") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Bad arguments for option 'schema'")
+      cli.expectErr("Could not find file or resource")
+    } (ExitCode.Usage)
   }
 
   @Test def test_1002_CLI_Parsing_negativeTest03(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -P parserThatDoesNotExist", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      if (Util.isWindows) {
-        shell.expectIn(1, contains("parserThatDoesNotExist (The system cannot find the file specified)"))
-      } else {
-        shell.expectIn(1, contains("parserThatDoesNotExist (No such file or directory)"))
-      }
 
-      Util.expectExitCode(ExitCode.FileNotFound, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -P parserThatDoesNotExist") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("parserThatDoesNotExist")
+    } (ExitCode.FileNotFound)
   }
 
   @Test def test_1003_CLI_Parsing_SimpleParse_emptyNamespace(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r {}address %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output4))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
+
+    runCLI(args"parse -s $schema -r {}address $input") { cli =>
+      cli.expect(Util.getExpectedString("output4.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1004_CLI_Parsing_SimpleParse_namespaceUsed(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r {target}matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output6))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
+
+    runCLI(args"parse -s $schema -r {target}matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output6.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_2615_CLI_Parsing_SimpleParse_namespaceUsedLongOpt(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s --root {target}matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output6))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
+
+    runCLI(args"parse -s $schema --root {target}matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output6.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1005_CLI_Parsing_SimpleParse_rootPath(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.startNoConvert("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      //val expected = """<tns:hcp2 xmlns:tns="http://www.example.org/example1/">12</tns:hcp2>"""
-      val cmd = String.format(Util.echoN("12") + "| %s parse -s %s -r hcp2 -p /", Util.binPath, testSchemaFile)
-
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:hcp2 xmlns:tns=\"http://www.example.org/example1/\">"))
-      shell.expect(contains("12"))
-      shell.expect(contains("</tns:hcp2>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r hcp2 -p /") { cli =>
+      cli.send("12", inputDone = true)
+      cli.expect("<tns:hcp2 xmlns:tns=\"http://www.example.org/example1/\">")
+      cli.expect("12")
+      cli.expect("</tns:hcp2>")
+    } (ExitCode.Success)
   }
 
   @Test def test_1015_CLI_Parsing_SimpleParse_defaultRootMultSchema(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output4))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect(Util.getExpectedString("output4.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationOn(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate on", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate on") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validation_missing_mode(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments"))
-      shell.expectIn(1, contains("validate"))
-      shell.expectIn(1, contains("exactly one argument"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r matrix --validate") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("Bad arguments")
+      cli.expectErr("validate")
+      cli.expectErr("exactly one argument")
+    } (ExitCode.Usage)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationLimited(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate limited", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate limited") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationOff(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate off", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate off") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationFooBar(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse --validate FooBar -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("FooBar"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
-  }
-
-  /*
-  //On hold until I implement a way to set the classpath before executing
-  @Test def test_1313_CLI_Parsing_assertionFailure() {
-    val cmd = "echo unacceptable| " + Util.binPath + " parse -s daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_B_08.dfdl.xsd -s daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_C_08.dfdl.xsd --root bElem2\n"
-    val shell = Util.start(cmd)
-    shell.expect(contains("Parse Error: Assertion failed. Assertion failed for dfdl:checkConstraints(.)"))
-
-    shell.send("exit\n")
-    shell.expect(eof)
-    shell.close()
+    runCLI(args"parse --validate FooBar -s $schema -r matrix") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("FooBar")
+    } (ExitCode.Usage)
   }
-*/
 
   @Test def test_1319_CLI_Parsing_invalidElementSDE(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/ABC_IBM_invalid.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo ababababbaacccccb| %s parse -s %s -r ABC", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("'fixed' is not a valid"))
-
-      Util.expectExitCode(ExitCode.UnableToCreateProcessor, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/ABC_IBM_invalid.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r ABC") { cli =>
+      cli.sendLine("ababababbaacccccb", inputDone = true)
+      cli.expectErr("'fixed' is not a valid")
+    } (ExitCode.UnableToCreateProcessor)
   }
 
   @Test def test_1346_CLI_Parsing_SimpleParse_defaultRootMultSchemaMultiple(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
     for (x <- 1 to 10) {
-      val shell = Util.start("")
-
-      try {
-        println("Run " + x + " of 10")
-        shell.sendLine(cmd)
-        shell.expect(contains(output4))
-
-        Util.expectExitCode(ExitCode.Success, shell)
-        shell.sendLine("exit")
-        shell.expect(eof)
-      } finally {
-        shell.close()
-      }
+      runCLI(args"parse -s $schema $input") { cli =>
+        cli.expect(Util.getExpectedString("output4.txt"))
+      } (ExitCode.Success)
     }
   }
 
   @Test def test_1386_CLI_Parsing_negativeTest05(): Unit = {
-    val cmd = String.format("echo 12| %s", Util.binPath)
-    val shell = Util.start("")
-
-    try {
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Subcommand required"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Subcommand required")
+    } (ExitCode.Usage)
   }
 
   @Test def test_1971_CLI_Parsing_traceMode01(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo test| %s -t parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("parser: <Element name='rabbitHole'>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
+
+    runCLI(args"-t parse -s $schema") { cli =>
+      cli.sendLine("test", inputDone = true)
+      cli.expect("parser: <Element name='rabbitHole'>")
+    } (ExitCode.Success)
   }
 
   @Test def test_1973_CLI_Parsing_traceMode03(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3,,,,| %s -t parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 56 bit(s) with at least"))
-      shell.expectIn(1, contains("Left over data (Hex) starting at byte 8 is: ("))
-      shell.expectIn(1, contains("Left over data (UTF-8) starting at byte 8 is: ("))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-      //assert(shell.getExitValue() == 1)
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"-t parse -s $schema") { cli =>
+      cli.sendLine("0,1,2,3,,,,", inputDone = true)
+      cli.expectErr("Left over data. Consumed 56 bit(s) with at least")
+      cli.expectErr("Left over data (Hex) starting at byte 8 is: (")
+      cli.expectErr("Left over data (UTF-8) starting at byte 8 is: (")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_1941_CLI_Parsing_SimpleParse_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 1,2,3,4,,,| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 56 bit(s) with at least"))
-      shell.expectIn(1, contains("Left over data (Hex) starting at byte 8 is: ("))
-      shell.expectIn(1, contains("Left over data (UTF-8) starting at byte 8 is: ("))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r matrix") { cli =>
+      cli.sendLine("1,2,3,4,,,", inputDone = true)
+      cli.expectErr("Left over data. Consumed 56 bit(s) with at least")
+      cli.expectErr("Left over data (Hex) starting at byte 8 is: (")
+      cli.expectErr("Left over data (UTF-8) starting at byte 8 is: (")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_LSBPartialByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r lsbPartialByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r lsbPartialByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
         + "\nLeft over data starts with partial byte. Left over data (Binary) at byte 2 is: (0b011101xx)"
         + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_MSBPartialByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r msbPartialByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r msbPartialByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
         + "\nLeft over data starts with partial byte. Left over data (Binary) at byte 2 is: (0bxx110100)"
         + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_MSBFullByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r msbFullByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 16 bit(s) with at least 16 bit(s) remaining."
-        + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r msbFullByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 16 bit(s) with at least 16 bit(s) remaining."
+        + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_DFDL_714(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:elem xmlns:tns=\"http://baseSchema.com\">"))
-      shell.expect(contains("<content"))
-      shell.expect(contains("Hello World"))
-      shell.expect(contains("</tns:elem>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect("<tns:elem xmlns:tns=\"http://baseSchema.com\">")
+      cli.expect("<content")
+      cli.expect("Hello World")
+      cli.expect("</tns:elem>")
+    } (ExitCode.Success)
   }
 
   @Test def test_DFDL_1203_schema_from_jar(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("", envp = Map("DAFFODIL_CLASSPATH" -> Util.daffodilPath("daffodil-cli/target/scala-2.10/*")))
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:elem xmlns:tns=\"http://baseSchema.com\">"))
-      shell.expect(contains("<content"))
-      shell.expect(contains("Hello World"))
-      shell.expect(contains("</tns:elem>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect("<tns:elem xmlns:tns=\"http://baseSchema.com\">")
+      cli.expect("<content")
+      cli.expect("Hello World")
+      cli.expect("</tns:elem>")
+    } (ExitCode.Success)
   }
 
   @Test def test_3606_CLI_Parsing_SimpleParse_largeInfoset(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
+    runCLI(args"parse -s $schema -r matrix") { cli =>
       val longInput = "0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64"
-      val cmd = String.format("echo %s| %s parse -s %s -r matrix", longInput, Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      val result = shell.expect(contains("<tns:row")).getBefore()
-      println(result)
+      cli.sendLine(longInput, inputDone = true)
+      val result = cli.expect("<tns:row").getBefore()
       if (result.contains("""<tns:matrix xmlns:tns="http://www.example.org/example1/"><tns:matrix xmlns:tns="http://www.example.org/example1/">""")) {
         throw new Exception("Error - Root has been duplicated")
       }
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_built_in_formats(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_04.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r e %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_04.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
 
-      shell.expectIn(1, contains("Schema Definition Warning"))
-      shell.expectIn(1, contains("edu/illinois/ncsa/daffodil/xsd/built-in-formats.xsd"))
-      shell.expectIn(1, contains("org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("quit")
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r e $input") { cli =>
+      cli.expectErr("Schema Definition Warning")
+      cli.expectErr("edu/illinois/ncsa/daffodil/xsd/built-in-formats.xsd")
+      cli.expectErr("org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd")
+    } (ExitCode.Success)
   }
 
   // These DAFFODIL_JAVA_OPTS values change the Java defaults classes like
   // SAXParserFactory and SchemaFactory to be Java's internal classes instead
   // of those provided by dependencies (e.g. Xerces) included with Daffodil.
   // Some places require dependency version of these classes. This test ensures
   // that we override defaults when necesssary
+  /*
+   * TODO: Update new API to support a way to set DAFFODIL_JAVA_OPTS. Maybe
+   * runCLI optionally accepts environment options, and they are provided it
+   * switches from using a thread to forking a process.
+   *
   @Test def test_CLI_Parsing_JavaDefaults(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
     val java_opts = Map("DAFFODIL_JAVA_OPTS" ->
       ("-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl " +
         "-Djavax.xml.xml.validation.SchemaFactory=com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory"))
 
-    val shell = Util.start("", envp = java_opts)
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix) { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
+  */
 
   @Test def test_XXX_CLI_Parsing_Stream_01(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("123") + "| %s parse --stream -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<a>1</a>"))
-      shell.expect(contains("<a>2</a>"))
-      shell.expect(contains("<a>3</a>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
+
+    runCLI(args"parse --stream -s $schema") { cli =>
+      cli.send("123", inputDone = true)
+      cli.expect("<a>1</a>")
+      cli.expect("<a>2</a>")
+      cli.expect("<a>3</a>")
+    } (ExitCode.Success)
   }
 
   @Test def test_XXX_CLI_Parsing_Stream_02(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("123ab") + "| %s parse --stream -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<a>1</a>"))
-      shell.expect(contains("<a>2</a>"))
-      shell.expect(contains("<a>3</a>"))
-      shell.expectIn(1, contains("Left over data after consuming 0 bits while streaming."))
-      shell.expectIn(1, contains("Stopped after consuming 24 bit(s) with at least 16 bit(s) remaining."))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
+
+    runCLI(args"parse --stream -s $schema") { cli =>
+      cli.send("123ab", inputDone = true)
+      cli.expect("<a>1</a>")
+      cli.expect("<a>2</a>")
+      cli.expect("<a>3</a>")
+      cli.expectErr("Left over data after consuming 0 bits while streaming.")
+      cli.expectErr("Stopped after consuming 24 bit(s) with at least 16 bit(s) remaining.")
+    } (ExitCode.LeftOverData)
   }
 
+  /*
+   * TODO: Update new API to support a way to set DAFFODIL_JAVA_OPTS. Maybe
+   * runCLI optionally accepts environment options, and they are provided it
+   * switches from using a thread to forking a process.
+   *
   @Test def test_CLI_Parsing_XCatalog_Resolution_Failure(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/xcatalog_import_failure.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/xcatalog_import_failure.dfdl.xsd")
 
-    val xcatalogFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/xcatalog_invalid.xml")
-    val testXcatalogFile = if (Util.isWindows) Util.cmdConvert(xcatalogFile) else xcatalogFile
+    val xcatalogFile = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/xcatalog_invalid.xml")
 
     val DAFFODIL_JAVA_OPTS = Map("DAFFODIL_JAVA_OPTS" -> ("-Dxml.catalog.files=" + testXcatalogFile + " -Xms256m -Xmx2048m -Dfile.encoding=UTF-8"))

Review Comment:
   This is the only test which deliberately passes `DAFFODIL_JAVA_OPTS` in such a purposeful way, and it's not even necessary since you can do the same thing with `System.setProperty`.
   
   (Later I found another test which also does a similar thing with properties, but maybe it can set these properties at runtime too.)



##########
daffodil-cli/src/it/scala/org/apache/daffodil/parsing/TestCLIParsing.scala:
##########
@@ -265,1124 +157,547 @@ class TestCLIparsing {
   //  verifies the expected output. If this test fails, it likely means we've
   //  broken our attempts to create consistent prefix mappings.
   @Test def test_1585_CLI_Parsing_MultifileSchema_methodImportSameDir(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_14.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output9))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_14.dfdl.xsd")
+
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output9.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1586_CLI_Parsing_MultifileSchema_methodIncludeSameDir(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
 
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output10))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output10.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1587_CLI_Parsing_MultifileSchema_methodImportSameDir2(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_16.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_16.dfdl.xsd")
 
-    try {
-      val cmd = String.format(Util.echoN("test") + "| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output10))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.send("test", inputDone = true)
+      cli.expect(Util.getExpectedString("output10.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1317_IBMCompatibility_ABC_test_ibm_abc_cli(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/ABC_IBM.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo abcabcabc| %s parse -s %s -r ABC", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/ABC_IBM.dfdl.xsd")
 
-      shell.expect(contains(output8))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r ABC") { cli =>
+      cli.sendLine("abcabcabc", inputDone = true)
+      cli.expect(Util.getExpectedString("output8.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_977_CLI_Parsing_SimpleParse_stdOut(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_978_CLI_Parsing_SimpleParse_outFile(): Unit = {
-    val tmp_filename: String = (System.currentTimeMillis / 1000).toString()
-    val file = new File(tmp_filename)
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix -o %s", Util.binPath, testSchemaFile, tmp_filename)
-      shell.sendLine(cmd)
-
-      val catCmd = if (Util.isWindows) "type" else "cat"
-      val openCmd = String.format("%s %s", catCmd, tmp_filename)
-
-      shell.sendLine(openCmd)
-      shell.expect(contains("<tns:cell>2</tns:cell>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-      assertTrue("Failed to remove temporary file: %s".format(file), file.delete)
+    withTempFile { tempFile =>
+      val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+      val tempPath = tempFile.getAbsolutePath()
+
+      runCLI(args"parse -s $schema -r matrix -o $tempPath") { cli =>
+        cli.sendLine("0,1,2", inputDone = true)
+      } (ExitCode.LeftOverData)
+
+      val res = FileUtils.readFileToString(tempFile, StandardCharsets.UTF_8)
+      assertTrue(res.contains("<tns:cell>2</tns:cell>"))
     }
   }
 
   @Test def test_979_CLI_Parsing_SimpleParse_inFile(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
+
+    runCLI(args"parse -s $schema -r matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_980_CLI_Parsing_SimpleParse_stOutDash(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r matrix -o - %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
+
+    runCLI(args"parse -s $schema -r matrix -o - $input") { cli =>
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_981_CLI_Parsing_SimpleParse_stdInDash(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse -s %s -r matrix -", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output2))
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expect(Util.getExpectedString("output2.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_983_CLI_Parsing_SimpleParse_verboseMode(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      shell.sendLine(String.format("echo 0,1| %s -v parse -s %s -r matrix -", Util.binPath, testSchemaFile))
-      shell.expectIn(1, contains("[info]"))
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-      shell.sendLine(String.format("echo 0,1| %s -vv parse -s %s -r matrix -", Util.binPath, testSchemaFile))
-      shell.expectIn(1, contains("[debug]"))
+    runCLI(args"-v parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1", inputDone = true)
+      cli.expectErr("[info]")
+    } (ExitCode.LeftOverData)
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.send("exit\n")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"-vv parse -s $schema -r matrix -") { cli =>
+      cli.sendLine("0,1", inputDone = true)
+      cli.expectErr("[debug]")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_984_CLI_Parsing_negativeTest(): Unit = {
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse", Util.binPath)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("There should be exactly one of the following options: schema, parser"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.send("exit\n")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expectErr("There should be exactly one of the following options: schema, parser")
+    } (ExitCode.Usage)
   }
 
   @Test def test_985_CLI_Parsing_SimpleParse_defaultRoot(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2,3| %s parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output2))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema") { cli =>
+      cli.sendLine("0,1,2,3", inputDone = true)
+      cli.expect(Util.getExpectedString("output2.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_988_CLI_Parsing_SimpleParse_specifiedRoot(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      //val expected = """<tns:hcp2 xmlns:tns="http://www.example.org/example1/">12</tns:hcp2>"""
-      val cmd = String.format("echo 12| %s parse -s %s -r hcp2", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:hcp2"))
-      shell.expect(contains("12"))
-      shell.expect(contains("</tns:hcp2>"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r hcp2") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expect("<tns:hcp2")
+      cli.expect("12")
+      cli.expect("</tns:hcp2>")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_996_CLI_Parsing_negativeTest04(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -r unknown", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("No root element found for unknown in any available namespace"))
-
-      Util.expectExitCode(ExitCode.UnableToCreateProcessor, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r unknown") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("No root element found for unknown in any available namespace")
+    } (ExitCode.UnableToCreateProcessor)
   }
 
   @Test def test_997_CLI_Parsing_multSchemas(): Unit = {
-    val schemaFile1 = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val schemaFile2 = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val (testSchemaFile1, testSchemaFile2) = if (Util.isWindows) (Util.cmdConvert(schemaFile1), Util.cmdConvert(schemaFile2)) else (schemaFile1, schemaFile2)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -s %s  -r hcp2", Util.binPath, testSchemaFile1, testSchemaFile2)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments for option 'schema'"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema1 = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val schema2 = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+
+    runCLI(args"parse -s $schema1 -s $schema2 -r hcp2") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Bad arguments for option 'schema'")
+    } (ExitCode.Usage)
   }
 
   @Test def test_3661_CLI_Parsing_badSchemaPath(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/doesnotexist.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 12| %s parse -s %s -r root", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments for option 'schema'"))
-      shell.expectIn(1, contains("Could not find file or resource"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/doesnotexist.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r root") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Bad arguments for option 'schema'")
+      cli.expectErr("Could not find file or resource")
+    } (ExitCode.Usage)
   }
 
   @Test def test_1002_CLI_Parsing_negativeTest03(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -P parserThatDoesNotExist", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      if (Util.isWindows) {
-        shell.expectIn(1, contains("parserThatDoesNotExist (The system cannot find the file specified)"))
-      } else {
-        shell.expectIn(1, contains("parserThatDoesNotExist (No such file or directory)"))
-      }
 
-      Util.expectExitCode(ExitCode.FileNotFound, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -P parserThatDoesNotExist") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("parserThatDoesNotExist")
+    } (ExitCode.FileNotFound)
   }
 
   @Test def test_1003_CLI_Parsing_SimpleParse_emptyNamespace(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r {}address %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output4))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
+
+    runCLI(args"parse -s $schema -r {}address $input") { cli =>
+      cli.expect(Util.getExpectedString("output4.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1004_CLI_Parsing_SimpleParse_namespaceUsed(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r {target}matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output6))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
+
+    runCLI(args"parse -s $schema -r {target}matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output6.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_2615_CLI_Parsing_SimpleParse_namespaceUsedLongOpt(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s --root {target}matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output6))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input8.txt")
+
+    runCLI(args"parse -s $schema --root {target}matrix $input") { cli =>
+      cli.expect(Util.getExpectedString("output6.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_1005_CLI_Parsing_SimpleParse_rootPath(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.startNoConvert("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      //val expected = """<tns:hcp2 xmlns:tns="http://www.example.org/example1/">12</tns:hcp2>"""
-      val cmd = String.format(Util.echoN("12") + "| %s parse -s %s -r hcp2 -p /", Util.binPath, testSchemaFile)
-
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:hcp2 xmlns:tns=\"http://www.example.org/example1/\">"))
-      shell.expect(contains("12"))
-      shell.expect(contains("</tns:hcp2>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r hcp2 -p /") { cli =>
+      cli.send("12", inputDone = true)
+      cli.expect("<tns:hcp2 xmlns:tns=\"http://www.example.org/example1/\">")
+      cli.expect("12")
+      cli.expect("</tns:hcp2>")
+    } (ExitCode.Success)
   }
 
   @Test def test_1015_CLI_Parsing_SimpleParse_defaultRootMultSchema(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output4))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect(Util.getExpectedString("output4.txt"))
+    } (ExitCode.Success)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationOn(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate on", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate on") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validation_missing_mode(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Bad arguments"))
-      shell.expectIn(1, contains("validate"))
-      shell.expectIn(1, contains("exactly one argument"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r matrix --validate") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("Bad arguments")
+      cli.expectErr("validate")
+      cli.expectErr("exactly one argument")
+    } (ExitCode.Usage)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationLimited(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate limited", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate limited") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationOff(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix --validate off", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix --validate off") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_XXX_CLI_Parsing_SimpleSchema_basicTest_validationFooBar(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse --validate FooBar -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("FooBar"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
-  }
-
-  /*
-  //On hold until I implement a way to set the classpath before executing
-  @Test def test_1313_CLI_Parsing_assertionFailure() {
-    val cmd = "echo unacceptable| " + Util.binPath + " parse -s daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_B_08.dfdl.xsd -s daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_C_08.dfdl.xsd --root bElem2\n"
-    val shell = Util.start(cmd)
-    shell.expect(contains("Parse Error: Assertion failed. Assertion failed for dfdl:checkConstraints(.)"))
-
-    shell.send("exit\n")
-    shell.expect(eof)
-    shell.close()
+    runCLI(args"parse --validate FooBar -s $schema -r matrix") { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expectErr("FooBar")
+    } (ExitCode.Usage)
   }
-*/
 
   @Test def test_1319_CLI_Parsing_invalidElementSDE(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/ABC_IBM_invalid.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo ababababbaacccccb| %s parse -s %s -r ABC", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("'fixed' is not a valid"))
-
-      Util.expectExitCode(ExitCode.UnableToCreateProcessor, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/ABC_IBM_invalid.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r ABC") { cli =>
+      cli.sendLine("ababababbaacccccb", inputDone = true)
+      cli.expectErr("'fixed' is not a valid")
+    } (ExitCode.UnableToCreateProcessor)
   }
 
   @Test def test_1346_CLI_Parsing_SimpleParse_defaultRootMultSchemaMultiple(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section07/defineFormat/defineFormat.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input7.txt")
     for (x <- 1 to 10) {
-      val shell = Util.start("")
-
-      try {
-        println("Run " + x + " of 10")
-        shell.sendLine(cmd)
-        shell.expect(contains(output4))
-
-        Util.expectExitCode(ExitCode.Success, shell)
-        shell.sendLine("exit")
-        shell.expect(eof)
-      } finally {
-        shell.close()
-      }
+      runCLI(args"parse -s $schema $input") { cli =>
+        cli.expect(Util.getExpectedString("output4.txt"))
+      } (ExitCode.Success)
     }
   }
 
   @Test def test_1386_CLI_Parsing_negativeTest05(): Unit = {
-    val cmd = String.format("echo 12| %s", Util.binPath)
-    val shell = Util.start("")
-
-    try {
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Subcommand required"))
-
-      Util.expectExitCode(ExitCode.Usage, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"") { cli =>
+      cli.sendLine("12", inputDone = true)
+      cli.expectErr("Subcommand required")
+    } (ExitCode.Usage)
   }
 
   @Test def test_1971_CLI_Parsing_traceMode01(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo test| %s -t parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("parser: <Element name='rabbitHole'>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/multi_base_15.dfdl.xsd")
+
+    runCLI(args"-t parse -s $schema") { cli =>
+      cli.sendLine("test", inputDone = true)
+      cli.expect("parser: <Element name='rabbitHole'>")
+    } (ExitCode.Success)
   }
 
   @Test def test_1973_CLI_Parsing_traceMode03(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 0,1,2,3,,,,| %s -t parse -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 56 bit(s) with at least"))
-      shell.expectIn(1, contains("Left over data (Hex) starting at byte 8 is: ("))
-      shell.expectIn(1, contains("Left over data (UTF-8) starting at byte 8 is: ("))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-      //assert(shell.getExitValue() == 1)
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"-t parse -s $schema") { cli =>
+      cli.sendLine("0,1,2,3,,,,", inputDone = true)
+      cli.expectErr("Left over data. Consumed 56 bit(s) with at least")
+      cli.expectErr("Left over data (Hex) starting at byte 8 is: (")
+      cli.expectErr("Left over data (UTF-8) starting at byte 8 is: (")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_1941_CLI_Parsing_SimpleParse_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("echo 1,2,3,4,,,| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 56 bit(s) with at least"))
-      shell.expectIn(1, contains("Left over data (Hex) starting at byte 8 is: ("))
-      shell.expectIn(1, contains("Left over data (UTF-8) starting at byte 8 is: ("))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r matrix") { cli =>
+      cli.sendLine("1,2,3,4,,,", inputDone = true)
+      cli.expectErr("Left over data. Consumed 56 bit(s) with at least")
+      cli.expectErr("Left over data (Hex) starting at byte 8 is: (")
+      cli.expectErr("Left over data (UTF-8) starting at byte 8 is: (")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_LSBPartialByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r lsbPartialByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r lsbPartialByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
         + "\nLeft over data starts with partial byte. Left over data (Binary) at byte 2 is: (0b011101xx)"
         + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_MSBPartialByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r msbPartialByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
+
+    runCLI(args"parse -s $schema -r msbPartialByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 10 bit(s) with at least 16 bit(s) remaining."
         + "\nLeft over data starts with partial byte. Left over data (Binary) at byte 2 is: (0bxx110100)"
         + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_BitParse_MSBFullByte_leftOverData(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("stri") + "| %s parse -s %s -r msbFullByte", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expectIn(1, contains("Left over data. Consumed 16 bit(s) with at least 16 bit(s) remaining."
-        + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
-        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)"))
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/bits_parsing.dfdl.xsd")
 
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r msbFullByte") { cli =>
+      cli.send("stri", inputDone = true)
+      cli.expectErr("Left over data. Consumed 16 bit(s) with at least 16 bit(s) remaining."
+        + "\nLeft over data (Hex) starting at byte 3 is: (0x7269...)"
+        + "\nLeft over data (UTF-8) starting at byte 3 is: (ri...)")
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_DFDL_714(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:elem xmlns:tns=\"http://baseSchema.com\">"))
-      shell.expect(contains("<content"))
-      shell.expect(contains("Hello World"))
-      shell.expect(contains("</tns:elem>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect("<tns:elem xmlns:tns=\"http://baseSchema.com\">")
+      cli.expect("<content")
+      cli.expect("Hello World")
+      cli.expect("</tns:elem>")
+    } (ExitCode.Success)
   }
 
   @Test def test_DFDL_1203_schema_from_jar(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("", envp = Map("DAFFODIL_CLASSPATH" -> Util.daffodilPath("daffodil-cli/target/scala-2.10/*")))
-
-    try {
-      val cmd = String.format("%s parse -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<tns:elem xmlns:tns=\"http://baseSchema.com\">"))
-      shell.expect(contains("<content"))
-      shell.expect(contains("Hello World"))
-      shell.expect(contains("</tns:elem>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/global_element.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/test_DFDL-714.txt")
+
+    runCLI(args"parse -s $schema $input") { cli =>
+      cli.expect("<tns:elem xmlns:tns=\"http://baseSchema.com\">")
+      cli.expect("<content")
+      cli.expect("Hello World")
+      cli.expect("</tns:elem>")
+    } (ExitCode.Success)
   }
 
   @Test def test_3606_CLI_Parsing_SimpleParse_largeInfoset(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
 
-    try {
+    runCLI(args"parse -s $schema -r matrix") { cli =>
       val longInput = "0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64,0,1,24,5,64"
-      val cmd = String.format("echo %s| %s parse -s %s -r matrix", longInput, Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      val result = shell.expect(contains("<tns:row")).getBefore()
-      println(result)
+      cli.sendLine(longInput, inputDone = true)
+      val result = cli.expect("<tns:row").getBefore()
       if (result.contains("""<tns:matrix xmlns:tns="http://www.example.org/example1/"><tns:matrix xmlns:tns="http://www.example.org/example1/">""")) {
         throw new Exception("Error - Root has been duplicated")
       }
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.LeftOverData)
   }
 
   @Test def test_CLI_Parsing_built_in_formats(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_04.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format("%s parse -s %s -r e %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_04.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
 
-      shell.expectIn(1, contains("Schema Definition Warning"))
-      shell.expectIn(1, contains("edu/illinois/ncsa/daffodil/xsd/built-in-formats.xsd"))
-      shell.expectIn(1, contains("org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("quit")
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r e $input") { cli =>
+      cli.expectErr("Schema Definition Warning")
+      cli.expectErr("edu/illinois/ncsa/daffodil/xsd/built-in-formats.xsd")
+      cli.expectErr("org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd")
+    } (ExitCode.Success)
   }
 
   // These DAFFODIL_JAVA_OPTS values change the Java defaults classes like
   // SAXParserFactory and SchemaFactory to be Java's internal classes instead
   // of those provided by dependencies (e.g. Xerces) included with Daffodil.
   // Some places require dependency version of these classes. This test ensures
   // that we override defaults when necesssary
+  /*
+   * TODO: Update new API to support a way to set DAFFODIL_JAVA_OPTS. Maybe
+   * runCLI optionally accepts environment options, and they are provided it
+   * switches from using a thread to forking a process.
+   *
   @Test def test_CLI_Parsing_JavaDefaults(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+
     val java_opts = Map("DAFFODIL_JAVA_OPTS" ->
       ("-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl " +
         "-Djavax.xml.xml.validation.SchemaFactory=com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory"))
 
-    val shell = Util.start("", envp = java_opts)
-
-    try {
-      val cmd = String.format("echo 0,1,2| %s parse -s %s -r matrix", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains(output1))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    runCLI(args"parse -s $schema -r matrix) { cli =>
+      cli.sendLine("0,1,2", inputDone = true)
+      cli.expect(Util.getExpectedString("output1.txt"))
+    } (ExitCode.LeftOverData)
   }
+  */
 
   @Test def test_XXX_CLI_Parsing_Stream_01(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("123") + "| %s parse --stream -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<a>1</a>"))
-      shell.expect(contains("<a>2</a>"))
-      shell.expect(contains("<a>3</a>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
+
+    runCLI(args"parse --stream -s $schema") { cli =>
+      cli.send("123", inputDone = true)
+      cli.expect("<a>1</a>")
+      cli.expect("<a>2</a>")
+      cli.expect("<a>3</a>")
+    } (ExitCode.Success)
   }
 
   @Test def test_XXX_CLI_Parsing_Stream_02(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
-    val testSchemaFile = if (Util.isWindows) Util.cmdConvert(schemaFile) else schemaFile
-    val shell = Util.start("")
-
-    try {
-      val cmd = String.format(Util.echoN("123ab") + "| %s parse --stream -s %s", Util.binPath, testSchemaFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("<a>1</a>"))
-      shell.expect(contains("<a>2</a>"))
-      shell.expect(contains("<a>3</a>"))
-      shell.expectIn(1, contains("Left over data after consuming 0 bits while streaming."))
-      shell.expectIn(1, contains("Stopped after consuming 24 bit(s) with at least 16 bit(s) remaining."))
-
-      Util.expectExitCode(ExitCode.LeftOverData, shell)
-      shell.sendLine("exit")
-      shell.expect(eof)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_02.dfdl.xsd")
+
+    runCLI(args"parse --stream -s $schema") { cli =>
+      cli.send("123ab", inputDone = true)
+      cli.expect("<a>1</a>")
+      cli.expect("<a>2</a>")
+      cli.expect("<a>3</a>")
+      cli.expectErr("Left over data after consuming 0 bits while streaming.")
+      cli.expectErr("Stopped after consuming 24 bit(s) with at least 16 bit(s) remaining.")
+    } (ExitCode.LeftOverData)
   }
 
+  /*
+   * TODO: Update new API to support a way to set DAFFODIL_JAVA_OPTS. Maybe
+   * runCLI optionally accepts environment options, and they are provided it
+   * switches from using a thread to forking a process.

Review Comment:
   I have evidence from both another Daffodil source file (see `daffodil-lib/src/test/scala/org/apache/daffodil/util/TestXMLCatalogAndValidate.scala`) and a web search that you can set the Java system property `xml.catalog.files` at runtime and still see the change take effect upon the next catalog lookup.  This particular test could call `System.setProperty("xml.catalog.files", testXcatalogFile)` and check for the desired result without needing to fork a process, although it would have to reset the property afterwards.
   
   I don't see any other test using `DAFFODIL_JAVA_OPTS` in such a deliberate and meaningful way, so I think we can avoid the need to accept an optional environment map and switch runCLI from using a thread to forking a process.  This would allow us to clean up Util.java by removing all the old API functions once all of the integration tests are converted to the new API (all the process-related code simply goes away).



##########
daffodil-cli/src/it/scala/org/apache/daffodil/debugger/TestCLIDebugger.scala:
##########
@@ -19,1477 +19,1120 @@ package org.apache.daffodil.debugger
 
 import org.junit.Test
 
+import java.nio.file.Files
+import java.nio.charset.StandardCharsets
+
 import net.sf.expectit.matcher.Matchers.allOf
 import net.sf.expectit.matcher.Matchers.contains
 import net.sf.expectit.matcher.Matchers.regexp
 import net.sf.expectit.matcher.Matchers.times
-import org.apache.daffodil.Main.ExitCode
 
 import org.apache.daffodil.CLI.Util
+import org.apache.daffodil.CLI.Util.ArgsHelper
+import org.apache.daffodil.CLI.Util.devNull
+import org.apache.daffodil.CLI.Util.path
+import org.apache.daffodil.CLI.Util.runCLI
+import org.apache.daffodil.CLI.Util.withTempFile
+import org.apache.daffodil.Main.ExitCode
 
 class TestCLIdebugger {
 
-  val DAFFODIL_JAVA_OPTS = Map("DAFFODIL_JAVA_OPTS" -> "-Xms256m -Xmx2048m -Dfile.encoding=UTF-8")
-
   @Test def test_3385_CLI_Debugger_invalidExpressions(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
 
-      shell.expect(contains("(debug)"))
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-      shell.sendLine("eval (/invalid)")
-      shell.expect(contains("error: expression evaluation failed: Schema Definition Error:"))
-      shell.expect(contains("(debug)"))
+      cli.sendLine("eval (/invalid)")
+      cli.expect("error: expression evaluation failed: Schema Definition Error:")
+      cli.expect("(debug)")
 
-      shell.sendLine("eval (func())")
-      shell.expect(contains("error: expression evaluation failed: Schema Definition Error: Unsupported function:"))
-      shell.expect(contains("(debug)"))
+      cli.sendLine("eval (func())")
+      cli.expect("error: expression evaluation failed: Schema Definition Error: Unsupported function:")
+      cli.expect("(debug)")
 
-      shell.sendLine("eval (/invalid!)")
-      shell.expect(contains("error: expression evaluation failed: Schema Definition Error:"))
-      shell.expect(contains("(debug)"))
+      cli.sendLine("eval (/invalid!)")
+      cli.expect("error: expression evaluation failed: Schema Definition Error:")
+      cli.expect("(debug)")
 
-      shell.sendLine("eval (!)")
-      shell.expect(contains("error: expression evaluation failed: Schema Definition Error:"))
-      shell.expect(contains("(debug)"))
+      cli.sendLine("eval (!)")
+      cli.expect("error: expression evaluation failed: Schema Definition Error:")
+      cli.expect("(debug)")
 
-      shell.sendLine("eval (././.\\/)")
-      shell.expect(contains("error: expression evaluation failed: Schema Definition Error:"))
-      shell.expect(contains("(debug)"))
+      cli.sendLine("eval (././.\\/)")
+      cli.expect("error: expression evaluation failed: Schema Definition Error:")
+      cli.expect("(debug)")
 
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_1591_CLI_Debugger_invalidCommandError(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-      shell.sendLine("garbage")
-      shell.expect(contains("error: undefined command: garbage"))
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
+
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
+      cli.sendLine("garbage")
+      cli.expect("error: undefined command: garbage")
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_1335_CLI_Debugger_dataAndWrapLength(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("debug"))
-
-      shell.sendLine("info data")
-      shell.expect(contains("0~,~1~,~2~,~3~,~4~,~5~,~6~"))
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
 
-      //      shell.sendLine("set dataLength 5")
-      //      shell.sendLine("info data")
-      //      shell.expect(contains("0,1,2"))
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("debug")
 
-      shell.sendLine("set dataLength -938")
-      shell.sendLine("info data")
-      shell.expect(contains("0~,~1~,~2~,~3~,~4~,~5~,~6~"))
+      cli.sendLine("info data")
+      cli.expect("0~,~1~,~2~,~3~,~4~,~5~,~6~")
 
-      //      shell.sendLine("set wrapLength 2")
-      //      shell.sendLine("info data")
-      //      shell.expect(contains("0,\n    1,\n    2,\n    3,\n    4,\n    5,\n    6\n"))
+      cli.sendLine("set dataLength -938")
+      cli.sendLine("info data")
+      cli.expect("0~,~1~,~2~,~3~,~4~,~5~,~6~")
 
-      shell.sendLine("continue")
+      cli.sendLine("continue")
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_982_CLI_Debugger_simpleDebugger(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-      shell.sendLine("continue")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
+      cli.sendLine("continue")
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1326_CLI_Debugger_displaysTesting(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
+      cli.sendLine("display eval (.)")
+      cli.sendLine("step")
+      cli.expect("matrix")
 
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("info displays")
+      cli.expect("1: eval (.)")
 
-      shell.sendLine("display eval (.)")
-      shell.sendLine("step")
-      shell.expect(contains("matrix"))
+      cli.sendLine("disable display 1")
+      cli.sendLine("info displays")
+      cli.expect("1*: eval (.)")
+      cli.sendLine("step")
+      cli.sendLine("enable display 1")
 
-      shell.sendLine("info displays")
-      shell.expect(contains("1: eval (.)"))
+      cli.sendLine("step")
+      cli.expect("</tns:cell>")
 
-      shell.sendLine("disable display 1")
-      shell.sendLine("info displays")
-      shell.expect(contains("1*: eval (.)"))
-      shell.sendLine("step")
-      shell.sendLine("enable display 1")
+      cli.sendLine("delete display 1")
+      cli.sendLine("step")
 
-      shell.sendLine("step")
-      shell.expect(contains("</tns:cell>"))
+      cli.sendLine("enable display 1")
+      cli.expect("error: 1 is not a valid display id")
 
-      shell.sendLine("delete display 1")
-      shell.sendLine("step")
+      cli.sendLine("continue")
+      cli.expect("matrix")
 
-      shell.sendLine("enable display 1")
-      shell.expect(contains("error: 1 is not a valid display id"))
-
-      shell.sendLine("continue")
-      shell.expect(contains("matrix"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1339_CLI_Debugger_removeHidden(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r e %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains("(debug)"))
-      shell.sendLine("set removeHidden false")
-      shell.sendLine("display info infoset")
-      shell.sendLine("step")
-      shell.sendLine("step")
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
+
+    runCLI(args"-d parse -s $schema -r e $input") { cli =>
+      cli.expect("(debug)")
+      cli.sendLine("set removeHidden false")
+      cli.sendLine("display info infoset")
+      cli.sendLine("step")
+      cli.sendLine("step")
       // intentionally look for a newline to make sure normally hidden elements
       // are output with a trailing newline when the debugger displays them
-      shell.expect(contains("<sneaky></sneaky>\n"))
-      shell.sendLine("break g")
-      shell.sendLine("continue")
-      shell.expect(contains("<sneaky>5</sneaky>\n"))
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+      cli.expect("<sneaky></sneaky>\n")
+      cli.sendLine("break g")
+      cli.sendLine("continue")
+      cli.expect("<sneaky>5</sneaky>\n")
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_3268_CLI_Debugger_removeHidden2(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r e %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-
-      shell.expect(contains("(debug)"))
-      shell.sendLine("set removeHidden false")
-      shell.sendLine("display info infoset")
-      shell.sendLine("break g")
-      shell.sendLine("continue")
-      shell.expect(contains("<sneaky>5</sneaky>"))
-      shell.sendLine("continue")
-      val result = shell.expect(contains("</ex:e>")).getBefore();
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input6.txt")
+
+    runCLI(args"-d parse -s $schema -r e $input") { cli =>
+      cli.expect("(debug)")
+      cli.sendLine("set removeHidden false")
+      cli.sendLine("display info infoset")
+      cli.sendLine("break g")
+      cli.sendLine("continue")
+      cli.expect("<sneaky>5</sneaky>")
+      cli.sendLine("continue")
+      val result = cli.expect("</ex:e>").getBefore();
       assert(!result.contains("sneaky"))
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1331_CLI_Debugger_breakpointTesting4(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input3.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input3.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("break cell")
+      cli.sendLine("break cell")
 
-      shell.sendLine("break cell")
-      shell.sendLine("break cell")
+      cli.sendLine("condition 1 dfdl:occursIndex() mod 2 eq 1")
+      cli.sendLine("condition 2 dfdl:occursIndex() mod 2 eq 0")
 
-      shell.sendLine("condition 1 dfdl:occursIndex() mod 2 eq 1")
-      shell.sendLine("condition 2 dfdl:occursIndex() mod 2 eq 0")
+      cli.sendLine("info breakpoints")
+      cli.expect("2: cell   { dfdl:occursIndex() mod 2 eq 0 }")
 
-      shell.sendLine("info breakpoints")
-      shell.expect(contains("2: cell   { dfdl:occursIndex() mod 2 eq 0 }"))
+      cli.sendLine("display info occursIndex")
 
-      shell.sendLine("display info occursIndex")
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 1")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 1"))
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 2")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 2"))
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 3")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 3"))
+      cli.sendLine("disable breakpoint 2")
 
-      shell.sendLine("disable breakpoint 2")
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 5")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 5"))
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 7")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 7"))
+      cli.sendLine("enable breakpoint 2")
 
-      shell.sendLine("enable breakpoint 2")
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 8")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 8"))
+      cli.sendLine("disable breakpoint 1")
+      cli.sendLine("disable breakpoint 2")
 
-      shell.sendLine("disable breakpoint 1")
-      shell.sendLine("disable breakpoint 2")
+      cli.sendLine("continue")
+      cli.expect("<tns:cell>3</tns:cell>")
 
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:cell>3</tns:cell>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1463_CLI_Debugger_breakOnValueOfElement(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input3.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("set breakOnlyOnCreation false")
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("display info infoset")
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("break cell")
-      shell.expect(contains("1: cell"))
-      shell.sendLine("condition 1 xsd:string(.) eq '3'")
-      shell.expect(contains("1: cell   { xsd:string(.) eq '3' }"))
-
-      shell.sendLine("info breakpoints")
-      shell.expect(allOf(contains("breakpoints:"), contains("1: cell   { xsd:string(.) eq '3' }")))
-
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:cell>3</tns:cell>"))
-      shell.expect(contains("</tns:row>"))
-      shell.expect(contains("</tns:matrix>"))
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:cell>3</tns:cell>"))
-      shell.expect(contains("</tns:row>"))
-      shell.expect(contains("</tns:matrix>"))
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:cell>3</tns:cell>"))
-      shell.expect(contains("</tns:row>"))
-      shell.expect(contains("</tns:matrix>"))
-
-      shell.sendLine("continue")
-      shell.expect(times(1, contains("<tns:cell>3</tns:cell>")))
-      shell.expect(contains("<tns:cell>3</tns:cell>"))
-      shell.expect(contains("</tns:row>"))
-      shell.expect(contains("</tns:matrix>"))
-      shell.sendLine("continue")
-      shell.expect(times(1, contains("<tns:cell>3</tns:cell>")))
-      shell.expect(contains("<tns:cell>3</tns:cell>"))
-      shell.expect(contains("</tns:row>"))
-      shell.expect(contains("</tns:matrix>"))
-
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input3.txt")
+
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
+
+      cli.sendLine("set breakOnlyOnCreation false")
+      cli.expect("(debug)")
+
+      cli.sendLine("display info infoset")
+      cli.expect("(debug)")
+
+      cli.sendLine("break cell")
+      cli.expect("1: cell")
+      cli.sendLine("condition 1 xsd:string(.) eq '3'")
+      cli.expect("1: cell   { xsd:string(.) eq '3' }")
+
+      cli.sendLine("info breakpoints")
+      cli.expect(allOf(contains("breakpoints:"), contains("1: cell   { xsd:string(.) eq '3' }")))
+
+      cli.sendLine("continue")
+      cli.expect("<tns:cell>3</tns:cell>")
+      cli.expect("</tns:row>")
+      cli.expect("</tns:matrix>")
+      cli.sendLine("continue")
+      cli.expect("<tns:cell>3</tns:cell>")
+      cli.expect("</tns:row>")
+      cli.expect("</tns:matrix>")
+      cli.sendLine("continue")
+      cli.expect("<tns:cell>3</tns:cell>")
+      cli.expect("</tns:row>")
+      cli.expect("</tns:matrix>")
+
+      cli.sendLine("continue")
+      cli.expect(times(1, contains("<tns:cell>3</tns:cell>")))
+      cli.expect("<tns:cell>3</tns:cell>")
+      cli.expect("</tns:row>")
+      cli.expect("</tns:matrix>")
+      cli.sendLine("continue")
+      cli.expect(times(1, contains("<tns:cell>3</tns:cell>")))
+      cli.expect("<tns:cell>3</tns:cell>")
+      cli.expect("</tns:row>")
+      cli.expect("</tns:matrix>")
+
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_1338_CLI_Debugger_pointsOfUncertaintyInfo(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input5.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input5.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r Item2 $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r Item2 %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("display info pointsOfUncertainty")
 
-      shell.sendLine("display info pointsOfUncertainty")
+      cli.sendLine("step")
+      cli.expect("pointsOfUncertainty:")
+      cli.expect("(none)")
 
-      shell.sendLine("step")
-      shell.expect(contains("pointsOfUncertainty:"))
-      shell.expect(contains("(none)"))
+      cli.sendLine("step")
+      cli.expect("pointsOfUncertainty:")
+      cli.expect("bitPos: 0, context: choice[1]")
 
-      shell.sendLine("step")
-      shell.expect(contains("pointsOfUncertainty:"))
-      shell.expect(contains("bitPos: 0, context: choice[1]"))
+      cli.sendLine("step")
+      cli.expect("pointsOfUncertainty:")
+      cli.expect("(none)")
 
-      shell.sendLine("step")
-      shell.expect(contains("pointsOfUncertainty:"))
-      shell.expect(contains("(none)"))
-
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_1328_CLI_Debugger_breakpointTesting(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-      shell.sendLine("display info infoset")
-      shell.sendLine("break cell")
+      cli.sendLine("display info infoset")
+      cli.sendLine("break cell")
 
-      shell.sendLine("continue")
-      shell.expect(contains("</tns:cell>"))
+      cli.sendLine("continue")
+      cli.expect("</tns:cell>")
 
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.expect(contains("<tns:cell>0</tns:cell>"))
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.expect("<tns:cell>0</tns:cell>")
 
-      shell.sendLine("continue")
-      shell.expect(contains("</tns:cell>"))
+      cli.sendLine("continue")
+      cli.expect("</tns:cell>")
 
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.expect(contains("<tns:cell>1</tns:cell>"))
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.expect("<tns:cell>1</tns:cell>")
 
-      shell.sendLine("delete breakpoint 1")
-      shell.sendLine("continue")
+      cli.sendLine("delete breakpoint 1")
+      cli.sendLine("continue")
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1329_CLI_Debugger_breakpointTesting2(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-      shell.sendLine("display info infoset")
-      shell.sendLine("break cell")
-      shell.sendLine("condition 1 dfdl:occursIndex() eq 3")
+      cli.sendLine("display info infoset")
+      cli.sendLine("break cell")
+      cli.sendLine("condition 1 dfdl:occursIndex() eq 3")
 
-      shell.sendLine("info breakpoints")
-      shell.expect(contains("1: cell   { dfdl:occursIndex() eq 3 }"))
+      cli.sendLine("info breakpoints")
+      cli.expect("1: cell   { dfdl:occursIndex() eq 3 }")
 
-      shell.sendLine("continue")
-      shell.expect(contains("</tns:cell>"))
+      cli.sendLine("continue")
+      cli.expect("</tns:cell>")
 
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.sendLine("step")
-      shell.expect(contains("<tns:cell>2</tns:cell>")) // lacks tns: prefix because debugger explicitly strips them.
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.sendLine("step")
+      cli.expect("<tns:cell>2</tns:cell>") // lacks tns: prefix because debugger explicitly strips them.
 
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:cell>6</tns:cell>")) // has tns prefix because this is the final infoset, not the debugger printing this.
+      cli.sendLine("continue")
+      cli.expect("<tns:cell>6</tns:cell>") // has tns prefix because this is the final infoset, not the debugger printing this.
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_CLI_Debugger_SDE_message(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("display info infoset")
+      cli.sendLine("break cell")
+      cli.sendLine("condition 1 fn:count(../cell) eq 3") // ../cell is wrong. Needs to be ../tns:cell
 
-      shell.sendLine("display info infoset")
-      shell.sendLine("break cell")
-      shell.sendLine("condition 1 fn:count(../cell) eq 3") // ../cell is wrong. Needs to be ../tns:cell
+      cli.sendLine("continue")
+      cli.expect(allOf(contains("Schema Definition Error"), contains("{}cell"), contains("tns:cell")))
 
-      shell.sendLine("continue")
-      shell.expect(allOf(contains("Schema Definition Error"), contains("{}cell"), contains("tns:cell")))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1330_CLI_Debugger_breakpointTesting3(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("display info occursIndex")
+      cli.expect("(debug)")
+      cli.sendLine("break cell")
+      cli.expect("(debug)")
+      cli.sendLine("info breakpoints")
+      cli.expect("1: cell")
 
-      shell.sendLine("display info occursIndex")
-      shell.expect(contains("(debug)"))
-      shell.sendLine("break cell")
-      shell.expect(contains("(debug)"))
-      shell.sendLine("info breakpoints")
-      shell.expect(contains("1: cell"))
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 1")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 1"))
+      cli.sendLine("continue")
+      cli.expect("occursIndex: 2")
 
-      shell.sendLine("continue")
-      shell.expect(contains("occursIndex: 2"))
+      cli.sendLine("disable breakpoint 1")
+      cli.sendLine("info breakpoints")
+      cli.expect("1*: cell")
 
-      shell.sendLine("disable breakpoint 1")
-      shell.sendLine("info breakpoints")
-      shell.expect(contains("1*: cell"))
+      cli.sendLine("info data")
+      // cli.expect("(2 to 2)")
+      cli.expect("0~,~1~,~2~,~3~,~4~,~5~,~6~")
 
-      shell.sendLine("info data")
-      // shell.expect(contains("(2 to 2)"))
-      shell.expect(contains("0~,~1~,~2~,~3~,~4~,~5~,~6~"))
+      cli.sendLine("continue")
+      cli.expect("<tns:cell>6</tns:cell>")
 
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:cell>6</tns:cell>"))
-
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1333_CLI_Debugger_settingInfosetLines(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input3.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("display info infoset")
-      shell.sendLine("set infosetLines 1")
-
-      shell.sendLine("break cell")
-      shell.sendLine("continue")
-      shell.expect(contains("..."))
-      shell.expect(contains("</tns:matrix>"))
-
-      shell.sendLine("set infosetLines 4")
-      shell.sendLine("continue")
-      shell.expect(contains("..."))
-      shell.expect(contains("<tns:cell>3</tns:cell>"))
-      shell.expect(contains("</tns:matrix>"))
-
-      shell.sendLine("set infosetLines 10")
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:matrix"))
-
-      shell.sendLine("set infosetLines -900")
-      shell.sendLine("continue")
-      shell.expect(contains("<tns:matrix"))
-      shell.expect(contains("</tns:matrix>"))
-
-      shell.sendLine("disable breakpoint 1")
-      shell.sendLine("continue")
-
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input3.txt")
+
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
+
+      cli.sendLine("display info infoset")
+      cli.sendLine("set infosetLines 1")
+
+      cli.sendLine("break cell")
+      cli.sendLine("continue")
+      cli.expect("...")
+      cli.expect("</tns:matrix>")
+
+      cli.sendLine("set infosetLines 4")
+      cli.sendLine("continue")
+      cli.expect("...")
+      cli.expect("<tns:cell>3</tns:cell>")
+      cli.expect("</tns:matrix>")
+
+      cli.sendLine("set infosetLines 10")
+      cli.sendLine("continue")
+      cli.expect("<tns:matrix")
+
+      cli.sendLine("set infosetLines -900")
+      cli.sendLine("continue")
+      cli.expect("<tns:matrix")
+      cli.expect("</tns:matrix>")
+
+      cli.sendLine("disable breakpoint 1")
+      cli.sendLine("continue")
+
+    } (ExitCode.Success)
   }
 
   @Test def test_1334_CLI_Debugger_infoBitPosition(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("display info bitPosition")
+      cli.sendLine("display info data")
+      cli.sendLine("break cell")
 
-      shell.sendLine("display info bitPosition")
-      shell.sendLine("display info data")
-      shell.sendLine("break cell")
+      cli.sendLine("continue")
+      cli.expect("bitPosition: 0")
 
-      shell.sendLine("continue")
-      shell.expect(contains("bitPosition: 0"))
+      cli.sendLine("continue")
+      cli.expect("bitPosition: 16")
 
-      shell.sendLine("continue")
-      shell.expect(contains("bitPosition: 16"))
+      cli.sendLine("continue")
+      cli.expect("bitPosition: 32")
 
-      shell.sendLine("continue")
-      shell.expect(contains("bitPosition: 32"))
+      cli.sendLine("continue")
 
-      shell.sendLine("continue")
-
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1337_CLI_Debugger_childIndex(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input4.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input4.txt")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-      shell.sendLine("break cell")
-      shell.sendLine("display info childIndex")
-      shell.sendLine("display info infoset")
+      cli.sendLine("break cell")
+      cli.sendLine("display info childIndex")
+      cli.sendLine("display info infoset")
 
-      shell.sendLine("continue")
-      shell.expect(contains("childIndex: 1"))
+      cli.sendLine("continue")
+      cli.expect("childIndex: 1")
 
-      shell.sendLine("continue")
-      shell.expect(contains("childIndex: 2"))
+      cli.sendLine("continue")
+      cli.expect("childIndex: 2")
 
-      shell.sendLine("continue")
-      shell.expect(contains("childIndex: 4"))
+      cli.sendLine("continue")
+      cli.expect("childIndex: 4")
 
-      shell.sendLine("disable breakpoint 1")
-      shell.sendLine("continue")
+      cli.sendLine("disable breakpoint 1")
+      cli.sendLine("continue")
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1340_CLI_Debugger_infoPath(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
     val output1 = Util.getExpectedString("output1.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-      shell.sendLine("break cell")
-      shell.sendLine("display info path")
+      cli.sendLine("break cell")
+      cli.sendLine("display info path")
 
-      shell.sendLine("continue")
-      shell.expect(contains("matrixType::sequence[1]::row::LocalComplexTypeDef::sequence[1]::cell"))
+      cli.sendLine("continue")
+      cli.expect("matrixType::sequence[1]::row::LocalComplexTypeDef::sequence[1]::cell")
 
-      shell.sendLine("delete breakpoint 1")
-      shell.expect(contains("debug"))
-      shell.sendLine("continue")
-      shell.expect(contains(output1))
+      cli.sendLine("delete breakpoint 1")
+      cli.expect("debug")
+      cli.sendLine("continue")
+      cli.expect(contains(output1))
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_1382_CLI_Debugger_dataAndWrapLength2(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("break cell")
-      shell.sendLine("continue")
-      shell.sendLine("info data")
-      shell.expect(contains("0~,~1~,~2~,~3~,~4~,~5~,~6~"))
-
-      //      shell.sendLine("set dataLength 2")
-      //      shell.sendLine("info data")
-      //      shell.expect(contains("0,"))
-
-      shell.sendLine("set dataLength -938")
-      shell.sendLine("info data")
-      shell.expect(contains("0~,~1~,~2~,~3~,~4~,~5~,~6~"))
-
-      //      shell.sendLine("set wrapLength 2")
-      //      shell.sendLine("info data")
-      //      shell.expect(contains("    0,"))
-      //      shell.expect(contains("    1,"))
-      //      shell.expect(contains("    2,"))
-      //      shell.expect(contains("    3,"))
-      //      shell.expect(contains("    4,"))
-      //      shell.expect(contains("    5,"))
-      //      shell.expect(contains("    6"))
-
-      shell.sendLine("disable breakpoint 1")
-      shell.sendLine("continue")
-
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
+
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
+
+      cli.sendLine("break cell")
+      cli.sendLine("continue")
+      cli.sendLine("info data")
+      cli.expect("0~,~1~,~2~,~3~,~4~,~5~,~6~")
+
+      cli.sendLine("set dataLength -938")
+      cli.sendLine("info data")
+      cli.expect("0~,~1~,~2~,~3~,~4~,~5~,~6~")
+
+      cli.sendLine("disable breakpoint 1")
+      cli.sendLine("continue")
+
+    } (ExitCode.Success)
   }
 
   @Test def test_1863_CLI_Debugger_groupIndex01(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_03.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input9.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -r list -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("display info groupIndex")
-      shell.sendLine("break price")
-      shell.expect(contains("1: price"))
-      shell.sendLine("break comment")
-      shell.expect(contains("2: comment"))
-
-      shell.sendLine("continue")
-      shell.expect(contains("groupIndex: 2"))
-      shell.sendLine("continue")
-      shell.expect(contains("groupIndex: 4"))
-      shell.sendLine("continue")
-      shell.expect(contains("groupIndex: 2"))
-      shell.sendLine("continue")
-      shell.expect(contains("groupIndex: 4"))
-      shell.sendLine("continue")
-      shell.expect(contains("<ex:price>89.99</ex:price>"))
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_03.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input9.txt")
+
+    runCLI(args"-d parse -r list -s $schema $input") { cli =>
+      cli.expect("(debug)")
+
+      cli.sendLine("display info groupIndex")
+      cli.sendLine("break price")
+      cli.expect("1: price")
+      cli.sendLine("break comment")
+      cli.expect("2: comment")
+
+      cli.sendLine("continue")
+      cli.expect("groupIndex: 2")
+      cli.sendLine("continue")
+      cli.expect("groupIndex: 4")
+      cli.sendLine("continue")
+      cli.expect("groupIndex: 2")
+      cli.sendLine("continue")
+      cli.expect("groupIndex: 4")
+      cli.sendLine("continue")
+      cli.expect("<ex:price>89.99</ex:price>")
+    } (ExitCode.Success)
   }
 
   @Test def test_1029_CLI_Debugger_validation1(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_03.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input9.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -r list -s %s %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("display info dne1")
-      shell.expect(contains("error: undefined info command: dne1"))
-      shell.sendLine("display info bitLimit dne2")
-      shell.expect(contains("error: bitLimit command requires zero arguments"))
-      shell.sendLine("display break")
-      shell.expect(contains("error: undefined command: break"))
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/cli_schema_03.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input9.txt")
+
+    runCLI(args"-d parse -r list -s $schema $input") { cli =>
+      cli.expect("(debug)")
+
+      cli.sendLine("display info dne1")
+      cli.expect("error: undefined info command: dne1")
+      cli.sendLine("display info bitLimit dne2")
+      cli.expect("error: bitLimit command requires zero arguments")
+      cli.sendLine("display break")
+      cli.expect("error: undefined command: break")
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_3258_CLI_Debugger_infodata(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
-
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("display info data")
-      shell.sendLine("step")
-      shell.expect(contains("│")) //  (0 to 0)
-      shell.expect(contains("0~,~1~,~2~,~3~,~4~,~5~,~6~"))
-
-      shell.sendLine("break cell")
-      shell.sendLine("condition 1 dfdl:occursIndex() eq 5")
-      shell.sendLine("continue")
-
-      // Gaak. Eclipse default font isn't monospaced. The visible space character is wider than a regular character!
-      shell.expect(contains("""                                  │                                    │"""))
-      shell.expect(contains("""    87654321  0011 2233 4455 6677 8899 aabb ccdd eeff  0~1~2~3~4~5~6~7~8~9~a~b~c~d~e~f~"""))
-      shell.expect(contains("""    00000000: 302c 312c 322c 332c 342c 352c 36         0~,~1~,~2~,~3~,~4~,~5~,~6~      """))
-      shell.sendLine("continue")
-//      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
+
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
+
+      cli.sendLine("display info data")
+      cli.sendLine("step")
+      cli.expect("│") //  (0 to 0)
+      cli.expect("0~,~1~,~2~,~3~,~4~,~5~,~6~")
+
+      cli.sendLine("break cell")
+      cli.sendLine("condition 1 dfdl:occursIndex() eq 5")
+      cli.sendLine("continue")
+
+      cli.expect(contains("""                                  │                                    │"""))
+      cli.expect(contains("""    87654321  0011 2233 4455 6677 8899 aabb ccdd eeff  0~1~2~3~4~5~6~7~8~9~a~b~c~d~e~f~"""))
+      cli.expect(contains("""    00000000: 302c 312c 322c 332c 342c 352c 36         0~,~1~,~2~,~3~,~4~,~5~,~6~      """))
+      cli.sendLine("continue")
+    } (ExitCode.Success)
   }
 
   @Test def test_3264_CLI_Debugger_undefined_command(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
-
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-      shell.sendLine("display data")
-      shell.expect(contains("error: undefined command: data"))
+      cli.sendLine("display data")
+      cli.expect("error: undefined command: data")
 
-      shell.sendLine("set breakonfailure true")
-      shell.expect(contains("error: undefined command: breakonfailure"))
+      cli.sendLine("set breakonfailure true")
+      cli.expect("error: undefined command: breakonfailure")
 
-      shell.sendLine("continue")
+      cli.sendLine("continue")
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
-    }
+    } (ExitCode.Success)
   }
 
   @Test def test_CLI_Debugger_delimiterStack(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input2.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
+      cli.sendLine("break row")
+      cli.expect("(debug)")
 
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("continue")
+      cli.expect("(debug)")
 
-      shell.sendLine("break row")
-      shell.expect(contains("(debug)"))
+      cli.sendLine("info delimiterStack")
+      cli.expect(contains("""local:  %NL; (separator)"""))
+      cli.expect("(debug)")
 
-      shell.sendLine("continue")
-      shell.expect(contains("(debug)"))
+      cli.sendLine("break cell")
+      cli.expect("(debug)")
 
-      shell.sendLine("info delimiterStack")
-      shell.expect(contains("""local:  %NL; (separator)"""))
-      shell.expect(contains("(debug)"))
+      cli.sendLine("continue")
+      cli.expect("(debug)")
 
-      shell.sendLine("break cell")
-      shell.expect(contains("(debug)"))
+      cli.sendLine("info delimiterStack")
+      cli.expect(contains("""remote: %NL; (separator)"""))
+      cli.expect(contains("""local:  , (separator)"""))
 
-      shell.sendLine("continue")
-      shell.expect(contains("(debug)"))
-
-      shell.sendLine("info delimiterStack")
-      shell.expect(contains("""remote: %NL; (separator)"""))
-      shell.expect(contains("""local:  , (separator)"""))
-
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_CLI_Debugger_utf16_encoding(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/utf16schema.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/hextest.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/utf16schema.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/hextest.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r e2 $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r e2 %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("info data")
+      cli.expect("\u240A")
 
-      shell.sendLine("info data")
-      shell.expect(contains("\u240A"))
-
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_1337_CLI_Debugger_info_infoset(): Unit = {
-    val schemaFile = Util.daffodilPath("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
-    val inputFile = Util.daffodilPath("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile)) else (schemaFile, inputFile)
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section06/entities/charClassEntities.dfdl.xsd")
+    val input = path("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/input/input1.txt")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    runCLI(args"-d parse -s $schema -r matrix $input") { cli =>
+      cli.expect("(debug)")
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r matrix %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      cli.sendLine("info infoset")
+      cli.expect("No Infoset")
 
-      shell.sendLine("info infoset")
-      shell.expect(contains("No Infoset"))
+      cli.sendLine("step")
+      cli.sendLine("info infoset")
+      cli.expect("matrix")
 
-      shell.sendLine("step")
-      shell.sendLine("info infoset")
-      shell.expect(contains("matrix"))
-
-      shell.sendLine("quit")
-      Util.expectExitCode(ExitCode.Failure, shell)
-    } finally {
-      shell.close()
-    }
+      cli.sendLine("quit")
+    } (ExitCode.Failure)
   }
 
   @Test def test_CLI_Debugger_InfoHidden_1(): Unit = {
-    val schemaFile = Util.daffodilPath(
-      "daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequencesWithHiddenRefs.dfdl.xsd")
-    val inputFile = Util.newTempFile("testInput_", ".tmp", optFileContents = Some("2~3"))
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) {
-      (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile.getAbsolutePath))
-    } else {
-      (schemaFile, inputFile)
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequencesWithHiddenRefs.dfdl.xsd")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    withTempFile { tempFile =>
+      Files.write(tempFile.toPath, "2~3".getBytes(StandardCharsets.UTF_8))
+      val tempPath = tempFile.getAbsolutePath
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r e5 %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      runCLI(args"-d parse -s $schema -r e5 $tempPath") { cli =>
+        cli.expect("(debug)")
 
-      shell.sendLine("break f")
-      shell.sendLine("display info hidden")
+        cli.sendLine("break f")
+        cli.sendLine("display info hidden")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains("<f xmlns=\"\">2</f>"))
+        cli.sendLine("continue")
+        cli.expect(contains("<f xmlns=\"\">2</f>"))
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
+      } (ExitCode.Success)
     }
   }
 
   @Test def test_CLI_Debugger_InfoHidden_2(): Unit = {
-    val schemaFile = Util.daffodilPath(
-      "daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequencesWithHiddenRefs.dfdl.xsd")
-    val inputFile = Util.newTempFile("testInput_", ".tmp", optFileContents = Some("2~3"))
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) {
-      (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile.getAbsolutePath))
-    } else {
-      (schemaFile, inputFile)
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequencesWithHiddenRefs.dfdl.xsd")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    withTempFile { tempFile =>
+      Files.write(tempFile.toPath, "2~3".getBytes(StandardCharsets.UTF_8))
+      val tempPath = tempFile.getAbsolutePath
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r e4 %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      runCLI(args"-d parse -s $schema -r e4 $tempPath") { cli =>
+        cli.expect("(debug)")
 
-      shell.sendLine("break f")
-      shell.sendLine("display info hidden")
+        cli.sendLine("break f")
+        cli.sendLine("display info hidden")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains("<f xmlns=\"\">3</f>"))
+        cli.sendLine("continue")
+        cli.expect(contains("<f xmlns=\"\">3</f>"))
 
-      Util.expectExitCode(ExitCode.Success, shell)
-    } finally {
-      shell.close()
+      } (ExitCode.Success)
     }
   }
 
   @Test def test_CLI_Debugger_InfoHidden_3(): Unit = {
-    val schemaFile = Util.daffodilPath(
-      "daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/ChoicesInHiddenContexts.dfdl.xsd")
-    val inputFile = Util.newTempFile("testInput_", ".tmp", optFileContents = Some("2,3"))
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) {
-      (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile.getAbsolutePath))
-    } else {
-      (schemaFile, inputFile)
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/ChoicesInHiddenContexts.dfdl.xsd")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    withTempFile { tempFile =>
+      Files.write(tempFile.toPath, "2,3".getBytes(StandardCharsets.UTF_8))
+      val tempPath = tempFile.getAbsolutePath
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r e8 %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      runCLI(args"-d parse -s $schema -r e8 $tempPath") { cli =>
+        cli.expect("(debug)")
 
-      shell.sendLine("break a")
-      shell.sendLine("break h")
-      shell.sendLine("break g")
-      shell.sendLine("break e")
-      shell.sendLine("break f")
-      shell.sendLine("display info hidden")
+        cli.sendLine("break a")
+        cli.sendLine("break h")
+        cli.sendLine("break g")
+        cli.sendLine("break e")
+        cli.sendLine("break f")
+        cli.sendLine("display info hidden")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains("<a>2</a>"))
-      shell.expect(contains("<g></g>"))
-    } finally {
-      shell.close()
+        cli.sendLine("continue")
+        cli.expect("<a>2</a>")
+        cli.expect("<g></g>")
+      } (ExitCode.Success)
     }
   }
 
   @Test def test_CLI_Debugger_InfoHidden_4(): Unit = {
-    val schemaFile = Util.daffodilPath(
-      "daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/ChoicesInHiddenContexts.dfdl.xsd")
-    val inputFile = Util.newTempFile("testInput_", ".tmp", optFileContents = Some("[6~]9"))
-    val (testSchemaFile, testInputFile) = if (Util.isWindows) {
-      (Util.cmdConvert(schemaFile), Util.cmdConvert(inputFile.getAbsolutePath))
-    } else {
-      (schemaFile, inputFile)
-    }
+    val schema = path("daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/ChoicesInHiddenContexts.dfdl.xsd")
 
-    val shell = if (Util.isWindows) Util.start("", envp = DAFFODIL_JAVA_OPTS) else Util.start("")
+    withTempFile { tempFile =>
+      Files.write(tempFile.toPath, "[6~]9".getBytes(StandardCharsets.UTF_8))
+      val tempPath = tempFile.getAbsolutePath
 
-    try {
-      val cmd = String.format("%s -d parse -s %s -r e9 %s", Util.binPath, testSchemaFile, testInputFile)
-      shell.sendLine(cmd)
-      shell.expect(contains("(debug)"))
+      runCLI(args"-d parse -s $schema -r e9 $tempPath") { cli =>
+        cli.expect("(debug)")
 
-      shell.sendLine("break e")
-      shell.sendLine("break f")
-      shell.sendLine("break g")
-      shell.sendLine("break h")
-      shell.sendLine("break i")
-      shell.sendLine("display info path hidden")
+        cli.sendLine("break e")
+        cli.sendLine("break f")
+        cli.sendLine("break g")
+        cli.sendLine("break h")
+        cli.sendLine("break i")
+        cli.sendLine("display info path hidden")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":f"))
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect(":f")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":i"))
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect(":i")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":h"))
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect(":h")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":e"))
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect(":e")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":f"))
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect(":f")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":f"))
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect(":f")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":g"))
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect(":g")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":i"))
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect(":i")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":h"))
-      shell.expect(contains("hidden: false"))
+        cli.sendLine("continue")
+        cli.expect(":h")
+        cli.expect("hidden: false")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":e"))
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect(":e")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains(":f"))
-      shell.expect(contains("hidden: true"))
+        cli.sendLine("continue")
+        cli.expect(":f")
+        cli.expect("hidden: true")
 
-      shell.sendLine("continue")
-      shell.expect(contains("<h></h>"))
-    } finally {
-      shell.close()
+        cli.sendLine("continue")
+        cli.expect("<h></h>")
+      } (ExitCode.Success)
     }
   }
+
   /* See DFDL-1264

Review Comment:
   When you see some commented out tests, I hope you will uncomment them to verify the tests still behave the same way before you comment them back out again.  We may find that the tests no longer behave the same way due to other changes in Daffodil and should be deleted or changed anyway.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@daffodil.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org