]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Get the .testcase and .check commands working in the CLI.
authordrh <>
Fri, 12 Dec 2025 18:22:07 +0000 (18:22 +0000)
committerdrh <>
Fri, 12 Dec 2025 18:22:07 +0000 (18:22 +0000)
FossilOrigin-Name: 1ca31e1a297c0d53d068afb658ce6602887cda0f3eaf42cd629c4d1b7204f0b0

manifest
manifest.uuid
src/shell.c.in
test/modeA.clitest

index b9cd93095473918cdaf3b8833abe4a43ab729c6e..fd79919a70e13dcbb100ab64d4368d40f4ff498b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\snew\sassert()\sstatements\sdesigned\sto\sdetect\sand\sprevent\sthe\skind\nof\ssystem\sinterface\snumbering\serrors\sthat\shappened\sin\s[108691a3cb2f296f]\nand\sthat\swere\snot\sfixed\suntil\s[fe49703034bd23fa].
-D 2025-12-12T12:06:43.000
+C Get\sthe\s.testcase\sand\s.check\scommands\sworking\sin\sthe\sCLI.
+D 2025-12-12T18:22:07.934
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -737,7 +737,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
 F src/resolve.c 8d53771eb51a4ab5f970150c3a70969d8db79cd04a8774c2d296bbcf471a0dd0
 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
 F src/select.c 344518c1bba9c4636bf651b7642304abd2e7075ba35feb4bae42a51e5efe991f
-F src/shell.c.in be8dc6e392cb16eb6b52cc950cc45d14bd1dc0f46aaa4eb33bb0f7b38e991205
+F src/shell.c.in f725bbdc4583c3728922b92281ccc44892c7b35bb665195ee7ca7d2374ecc7b4
 F src/sqlite.h.in 706cacea5308b0244fb6cec92e08310fb427a125375c64137cc1f878ae4cf5c0
 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
 F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
@@ -1441,7 +1441,7 @@ F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93
 F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465
 F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
 F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3
-F test/modeA.clitest aa4c12c45cf454a224258f42041677fbbd1c3d09e254900b8edbffc7c2b4c17f
+F test/modeA.clitest 2db42f2814ba45d5e886fead881360e689bc27346cfc37e6bb46f63334bebc61
 F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
 F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
 F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
@@ -2184,8 +2184,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P fe49703034bd23fa8f43ffdf0e23ec9613688d957adf96b3f1ea86fb02a8d86b
-R f52f4a9c387457edc2e18d724aa80ebc
+P 4e6e551c89c4f18299b3d60369f439e80f2fe2b0e70199f649821b1dbf20c668
+R 45ff11e2e11683b7a57e94205e0a1b3b
 U drh
-Z 9d4c0719badab3584588a1f120e6b6db
+Z 269e8ff8cb00c409106cfe616c6f8090
 # Remove this line to create a well-formed Fossil manifest.
index d3ff44e01bd6dc1f535ea010f7d00bb0d92f1f14..3750dbb60442aefcd7df16eed14ef98ea8081cb3 100644 (file)
@@ -1 +1 @@
-4e6e551c89c4f18299b3d60369f439e80f2fe2b0e70199f649821b1dbf20c668
+1ca31e1a297c0d53d068afb658ce6602887cda0f3eaf42cd629c4d1b7204f0b0
index ccf3cd2c19f3618c1ce119cf07b99959e7c13360..989024e87335abe873fefd8c2f3726444b085516 100644 (file)
@@ -3682,8 +3682,8 @@ static const char *(azHelp[]) = {
   ".cd DIRECTORY            Change the working directory to DIRECTORY",
 #endif
   ".changes on|off          Show number of rows changed by SQL",
+  ".check OPTIONS ...       Verify the results of a .testcase",
 #ifndef SQLITE_SHELL_FIDDLE
-  ".check GLOB              Fail if output since .testcase does not match",
   ".clone NEWDB             Clone data into NEWDB from the existing database",
 #endif
   ".connection [close] [#]  Open or close an auxiliary database connection",
@@ -3863,9 +3863,7 @@ static const char *(azHelp[]) = {
   ".system CMD ARGS...      Run CMD ARGS... in a system shell",
 #endif
   ".tables ?TABLE?          List names of tables matching LIKE pattern TABLE",
-#ifndef SQLITE_SHELL_FIDDLE
-  ",testcase NAME           Begin redirecting output to 'testcase-out.txt'",
-#endif
+  ".testcase NAME           Begin a test case.",
   ",testctrl CMD ...        Run various sqlite3_test_control() operations",
   "                           Run \".testctrl\" with no arguments for details",
   ".timeout MS              Try opening locked tables for MS milliseconds",
@@ -8260,19 +8258,13 @@ static int dotCmdMode(ShellState *p){
 **                     launch a text editor when the redirection ends.
 **   --error-prefix X  Use X as the left-margin prefix for error messages.
 **                     Set to an empty string to restore the default.
-**   --glob GLOB       Raise an error if the memory buffer does not match
-**                     the GLOB pattern.
-**   --keep            Continue using the same "memory" buffer.  Do not
-**                     reset it or delete it.  Useful in combination with
-**                     --glob, --not-glob, and/or --verify.
-**   ---notglob GLOB   Raise an error if the memory buffer does not match
-**                     the GLOB pattern.
+**   --keep            Keep redirecting output to its current destination.
+**                     Use this option in combination with --show or
+**                     with --error-prefix when you do not want to stop
+**                     a current redirection.
 **   --plain           Use plain text rather than HTML tables with -w
-**   --show            Write the memory buffer to the screen, for debugging.
-**   --verify ENDMARK  Read subsequent lines of text until the first line
-**                     that matches ENDMARK.  Discard the ENDMARK.  Compare
-**                     the text against the accumulated output in memory and
-**                     raise an error if there are any differences.
+**   --show            Show output text captured by .testcase or by
+**                     redirecting to "memory".
 **   -w                Show the output in a web browser.  Output is
 **                     written into a temporary HTML file until the
 **                     redirect ends, then the web browser is launched.
@@ -8314,9 +8306,7 @@ static int dotCmdOutput(ShellState *p){
   int eMode = 0;          /* 0: .outout/.once, 'x'=.excel, 'w'=.www */
   int bOnce = 0;          /* 0: .output, 1: .once, 2: .excel/.www */
   int bPlain = 0;         /* --plain option */
-  int bKeep = 0;          /* --keep option */
-  char *zCheck = 0;       /* Argument to --glob, --notglob, --verify */
-  int eCheck = 0;         /* 1: --glob,  2: --notglob,  3: --verify */
+  int bKeep = 0;          /* Keep redirecting */
   static const char *zBomUtf8 = "\357\273\277";
   const char *zBom = 0;
   char c = azArg[0][0];
@@ -8342,32 +8332,17 @@ static int dotCmdOutput(ShellState *p){
         bPlain = 1;
       }else if( c=='o' && z[0]=='1' && z[1]!=0 && z[2]==0
              && (z[1]=='x' || z[1]=='e' || z[1]=='w') ){
-        if( bKeep || eMode || eCheck ){
+        if( bKeep || eMode ){
           dotCmdError(p, i, "incompatible with prior options",0);
           goto dotCmdOutput_error;
         }
         eMode = z[1];
-      }else if( cli_strcmp(z,"-keep")==0 ){
-        bKeep = 1;
       }else if( cli_strcmp(z,"-show")==0 ){
         if( cli_output_capture ){
           sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture));
         }
+      }else if( cli_strcmp(z,"-keep")==0 ){
         bKeep = 1;
-      }else if( cli_strcmp(z,"-glob")==0
-             || cli_strcmp(z,"-notglob")==0
-             || cli_strcmp(z,"-verify")==0
-      ){
-        if( eCheck || eMode ){
-          dotCmdError(p, i, "incompatible with prior options",0);
-          goto dotCmdOutput_error;
-        }
-        if( i+1>=nArg ){
-          dotCmdError(p, i, "missing argument", 0);
-          goto dotCmdOutput_error;
-        }
-        zCheck = azArg[++i];
-        eCheck = z[1]=='g' ? 1 : z[1]=='n' ? 2 : 3;
       }else if( optionMatch(z,"error-prefix") ){
         if( i+1>=nArg ){
           dotCmdError(p, i, "missing argument", 0);
@@ -8382,7 +8357,7 @@ static int dotCmdOutput(ShellState *p){
         return 1;
       }
     }else if( zFile==0 && eMode==0 ){
-      if( bKeep || eCheck ){
+      if( bKeep ){
         dotCmdError(p, i, "incompatible with prior options",0);
         goto dotCmdOutput_error;
       }
@@ -8418,49 +8393,6 @@ static int dotCmdOutput(ShellState *p){
   }else{
     p->nPopOutput = 0;
   }
-  if( eCheck ){
-    char *zTest;
-    if( cli_output_capture ){
-      zTest = sqlite3_str_value(cli_output_capture);
-    }else{
-      zTest = "";
-    }
-    p->nTestRun++;
-    if( eCheck==3 ){
-      int nCheck = strlen30(zCheck);
-      sqlite3_str *pPattern = sqlite3_str_new(p->db);
-      char *zPattern;
-      sqlite3_int64 iStart = p->lineno;
-      char zLine[2000];
-      while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){
-        if( strchr(zLine,'\n') ) p->lineno++;
-        if( cli_strncmp(zCheck,zLine,nCheck)==0 ) break;
-        sqlite3_str_appendall(pPattern, zLine);
-      }
-      zPattern = sqlite3_str_finish(pPattern);
-      if( cli_strcmp(zPattern,zTest)!=0 ){
-        sqlite3_fprintf(stderr,
-            "%s:%lld: --verify does matches prior output\n",
-          p->zInFile, iStart);
-        p->nTestErr++;
-      }
-      sqlite3_free(zPattern);
-    }else{
-      char *zGlob = sqlite3_mprintf("*%s*", zCheck);
-      if( eCheck==1 && sqlite3_strglob(zGlob, zTest)!=0 ){
-        sqlite3_fprintf(stderr,
-            "%s:%lld: --glob \"%s\" does not match prior output\n",
-            p->zInFile, p->lineno, zCheck);
-        p->nTestErr++;       
-      }else if( eCheck==2 && sqlite3_strglob(zGlob, zTest)==0 ){
-        sqlite3_fprintf(stderr,
-            "%s:%lld: --notglob \"%s\" matches prior output\n",
-            p->zInFile, p->lineno, zCheck);
-        p->nTestErr++;       
-      }
-      sqlite3_free(zGlob);
-    }
-  }
   if( !bKeep ) output_reset(p);
 #ifndef SQLITE_NOHAVE_SYSTEM
   if( eMode=='e' || eMode=='x' || eMode=='w' ){
@@ -8542,6 +8474,176 @@ dotCmdOutput_error:
   return 1;
 }
 
+/*
+** DOT-COMMAND: .check
+** USAGE: .check [OPTIONS] PATTERN
+**
+** Verify results of commands since the most recent .testcase command.
+** Restore output to the console, unless --keep is used.
+**
+** If PATTERN starts with "<<ENDMARK" then the actual pattern is taken from
+** subsequent lines of text up to the first line that begins with ENDMARK.
+** All pattern lines and the ENDMARK are discarded.
+**
+** Options:
+**   --error-prefix TEXT    Change error message prefix text to TEXT
+**   --glob                 Treat PATTERN as a GLOB
+**   --keep                 Do not reset the testcase.  More .check commands
+**                          will follow.
+**   --notglob              Output should not match PATTERN
+**   --show                 Write testcase output to the screen, for debugging.
+*/
+static int dotCmdCheck(ShellState *p){
+  int nArg = p->dot.nArg;            /* Number of arguments */
+  char **azArg = p->dot.azArg;       /* Text of the arguments */
+  int i;                             /* Loop counter */
+  int k;                             /* Result of pickStr() */
+  char *zTest;                       /* Textcase result */
+  int bKeep = 0;                     /* --keep option */
+  char *zCheck = 0;                  /* PATTERN argument */
+  char *zPattern = 0;                /* Actual test pattern */
+  int eCheck = 0;                    /* 1: --glob,  2: --notglob,  3: --exact */
+  int isOk;                          /* True if results are OK */
+  sqlite3_int64 iStart = p->lineno;  /* Line number of .check statement */
+
+  if( p->zTestcase[0]==0 ){
+    dotCmdError(p, 0, "no .testcase is active", 0);
+    return 1;
+  }
+  for(i=1; i<nArg; i++){
+    char *z = azArg[i];
+    if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++;
+    if( cli_strcmp(z,"-keep")==0 ){
+      bKeep = 1;
+    }else if( cli_strcmp(z,"-show")==0 ){
+      if( cli_output_capture ){
+        sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture));
+      }
+      bKeep = 1;
+    }else if( z[0]=='-'
+          && (k = pickStr(&z[1],0,"glob","notglob","exact",""))>=0
+    ){
+      if( eCheck && eCheck!=k+1 ){
+        dotCmdError(p, i, "incompatible with prior options",0);
+        return 1;
+      }
+      eCheck = k+1;
+    }else if( zCheck ){
+      dotCmdError(p, i, "unknown option", 0);
+      return 1;
+    }else{
+      zCheck = azArg[i];
+    }
+  }
+  if( zCheck==0 ){
+    dotCmdError(p, 0, "no PATTERN specified", 0);
+    return 1;
+  }
+  if( cli_output_capture ){
+    zTest = sqlite3_str_value(cli_output_capture);
+    shell_check_oom(zTest);
+  }else{
+    zTest = "";
+  }
+  p->nTestRun++;
+  if( zCheck[0]=='<' && zCheck[1]=='<' && zCheck[2]!=0 ){
+    int nCheck = strlen30(zCheck);
+    sqlite3_str *pPattern = sqlite3_str_new(p->db);
+    char zLine[2000];
+    while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){
+      if( strchr(zLine,'\n') ) p->lineno++;
+      if( cli_strncmp(&zCheck[2],zLine,nCheck-2)==0 ) break;
+      sqlite3_str_appendall(pPattern, zLine);
+    }
+    zPattern = sqlite3_str_finish(pPattern);
+  }else{
+    zPattern = zCheck;
+  }
+  switch( eCheck ){
+    case 1: {
+      char *zGlob = sqlite3_mprintf("*%s*", zPattern);
+      isOk = testcase_glob(zGlob, zTest)!=0;
+      sqlite3_free(zGlob);
+      break;
+    }
+    case 2: {
+      char *zGlob = sqlite3_mprintf("*%s*", zPattern);
+      isOk = testcase_glob(zGlob, zTest)==0;
+      sqlite3_free(zGlob);
+      break;
+    }
+    default: {
+      isOk = cli_strcmp(zPattern,zTest)==0;
+      break;
+    }
+  }
+  if( !isOk ){
+    sqlite3_fprintf(stderr,
+          "%s:%lld: .check failed for testcase %s\n",
+        p->zInFile, iStart, p->zTestcase);
+    p->nTestErr++;
+    sqlite3_fprintf(stderr, "Expected: [%s]\n", zPattern);
+    sqlite3_fprintf(stderr, "Got:      [%s]\n", zTest);
+  }
+  if( zPattern!=zCheck ){
+    sqlite3_free(zPattern);
+  }
+  if( !bKeep ){
+    output_reset(p);
+    p->zTestcase[0] = 0;
+  }
+  return 0;
+}
+
+/*
+** DOT-COMMAND: .testcase
+** USAGE: .testcase [OPTIONS] NAME
+**
+** Start a new test case identified by NAME.  All output
+** through the next ".check" command is captured for comparison. See the
+** ".check" commandn for additional informatioon.
+**
+** Options:
+**   --error-prefix TEXT       Change error message prefix text to TEXT
+*/
+static int dotCmdTestcase(ShellState *p){
+  int nArg = p->dot.nArg;        /* Number of arguments */
+  char **azArg = p->dot.azArg;   /* Text of the arguments */
+  int i;                         /* Loop counter */
+  const char *zName = 0;         /* Testcase name */
+
+  for(i=1; i<nArg; i++){
+    char *z = azArg[i];
+    if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++;
+    if( optionMatch(z,"error-prefix") ){
+      if( i+1>=nArg ){
+        dotCmdError(p, i, "missing argument", 0);
+        return 1;
+      }
+      free(p->zErrPrefix);
+      i++;
+      p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]);
+    }else if( zName ){
+      dotCmdError(p, i, "unknown option", 0);
+      return 1;
+    }else{
+      zName = azArg[i];
+    }
+  }
+  output_reset(p);
+  if( cli_output_capture ){
+    sqlite3_str_free(cli_output_capture);
+  }
+  cli_output_capture = sqlite3_str_new(0);
+  if( zName ){
+    sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", zName);
+  }else{
+    sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s:%lld",
+                     p->zInFile, p->lineno);
+  }
+  return 0;
+}
+
 /*
 ** Enlarge the space allocated in p->dot so that it can hold more
 ** than nArg parsed command-line arguments.
@@ -8793,31 +8895,13 @@ static int do_meta_command(const char *zLine, ShellState *p){
     }
   }else
 
-#ifndef SQLITE_SHELL_FIDDLE
   /* Cancel output redirection, if it is currently set (by .testcase)
   ** Then read the content of the testcase-out.txt file and compare against
   ** azArg[1].  If there are differences, report an error and exit.
   */
   if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){
-    char *zRes = 0;
-    output_reset(p);
-    if( nArg!=2 ){
-      eputz("Usage: .check GLOB-PATTERN\n");
-      rc = 2;
-    }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
-      rc = 2;
-    }else if( testcase_glob(azArg[1],zRes)==0 ){
-      cli_printf(stderr,
-            "testcase-%s FAILED\n Expected: [%s]\n      Got: [%s]\n",
-            p->zTestcase, azArg[1], zRes);
-      rc = 1;
-    }else{
-      cli_printf(p->out, "testcase-%s ok\n", p->zTestcase);
-      p->nCheck++;
-    }
-    sqlite3_free(zRes);
+    rc = dotCmdCheck(p);
   }else
-#endif /* !defined(SQLITE_SHELL_FIDDLE) */
 
 #ifndef SQLITE_SHELL_FIDDLE
   if( c=='c' && cli_strncmp(azArg[0], "clone", n)==0 ){
@@ -10949,21 +11033,11 @@ static int do_meta_command(const char *zLine, ShellState *p){
     if( rc ) return shellDatabaseError(p->db);
   }else
 
-#ifndef SQLITE_SHELL_FIDDLE
-  /* Begin redirecting output to the file "testcase-out.txt" */
+  /* Set the p->zTestcase name and begin redirecting output into
+  ** the cli_output_capture sqlite3_str */
   if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){
-    output_reset(p);
-    p->out = output_file_open(p, "testcase-out.txt");
-    if( p->out==0 ){
-      eputz("Error: cannot open 'testcase-out.txt'\n");
-    }
-    if( nArg>=2 ){
-      sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]);
-    }else{
-      sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?");
-    }
+    rc = dotCmdTestcase(p);
   }else
-#endif /* !defined(SQLITE_SHELL_FIDDLE) */
 
 #ifndef SQLITE_UNTESTABLE
   if( c=='t' && n>=8 && cli_strncmp(azArg[0], "testctrl", n)==0 ){
index d5f2185bdc1e04f7f02ba0ea5222f063c619d947..2dcbf0c45cdda671a36388c2c9c606d2c2bc1eef 100644 (file)
@@ -21,9 +21,9 @@ INSERT INTO t1 SELECT b,c,d,e,a FROM t1;
 INSERT INTO t1 SELECT d,e,a,b,c FROM t1;
 .mode box
 
-.output memory
+.testcase 100
 SELECT * FROM t1;
-.output --verify END
+.check <<END
 ╭─────┬───────┬───────┬───────┬───────╮
 │  a  │   b   │   c   │   d   │   e   │
 ╞═════╪═══════╪═══════╪═══════╪═══════╡
@@ -34,10 +34,10 @@ SELECT * FROM t1;
 ╰─────┴───────┴───────┴───────┴───────╯
 END
 
-.output memory
+.testcase 110
 .mode --null xyz
 SELECT * FROM t1;
-.output --verify END
+.check <<END
 ╭─────┬───────┬───────┬───────┬───────╮
 │  a  │   b   │   c   │   d   │   e   │
 ╞═════╪═══════╪═══════╪═══════╪═══════╡
@@ -56,9 +56,9 @@ INSERT INTO t2 VALUES(1,2.5,'three',x'4444');
 INSERT INTO t2 VALUES('The quick fox jumps over the lazy brown dog',2,3,4);
 INSERT INTO t2 VALUES('10','', -1.25,NULL);
 INSERT INTO t2 VALUES('a,b,c','"Double-Quoted"','-1.25','NULL');
-.output memory
+.testcase 120
 SELECT * FROM t2;
-.output --verify END
+.check <<END
 ╭────────────┬────────────┬─────────┬─────────╮
 │     a      │     b      │    c    │    d    │
 ╞════════════╪════════════╪═════════╪═════════╡
@@ -76,152 +76,152 @@ SELECT * FROM t2;
 │            │ Quoted"    │         │         │
 ╰────────────┴────────────┴─────────┴─────────╯
 END
-.output memory
+.testcase 130
 .mode
-.output --verify END
+.check <<END
 .mode qbox --limits on --quote relaxed --sw auto --textjsonb on
 END
-.output memory
+.testcase 140
 .mode -v
-.output --verify END
+.check <<END
 .mode qbox --align "" --border on --blob-quote auto --colsep "" --escape auto --limits on --null "NULL" --quote relaxed --rowsep "" --sw auto --tablename "" --textjsonb on --titles on --widths "" --wordwrap off --wrap 10
 END
-.output memory --error-prefix "Error:"
+.testcase 150 --error-prefix "Error:"
 .mode foo
-.output --verify END
+.check <<END
 Error: .mode foo
 Error:       ^--- unknown mode
 Error: Use ".help .mode" for more info
 END
 
-.output memory
+.testcase 160
 .mode --null xyzzy -v
 .output -glob ' --null "xyzzy"'
-.output memory
+.testcase 170
 .mode -null abcde -v
 .output -glob ' --null "abcde"'
 
 # Test cases for the ".explain off" command
 .mode box -reset
-.output memory
+.testcase 180
 EXPLAIN SELECT * FROM t1;
 .output --notglob *────* --keep
 .output --notglob "* id │ parent │ notused │ detail *" --keep
 .output --glob "*   Init  *"
-.output memory
+.testcase 190
 EXPLAIN QUERY PLAN SELECT * FROM t1;
 .output --glob "*`--SCAN *"
 .explain off
-.output memory
+.testcase 200
 EXPLAIN SELECT * FROM t1;
 .output --glob *────*
-.output memory
+.testcase 210
 EXPLAIN QUERY PLAN SELECT * FROM t1;
 .output --glob "* id │ parent │ notused │ detail *"
 .explain auto
 
 # Test cases for limit settings in the .mode command.
-.output memory
+.testcase 300
 .mode box --reset
 .mode
-.output --verify END
+.check <<END
 .mode box
 END
-.output memory
+.testcase 310
 .mode --limits 5,300,20
 .mode
-.output --verify END
+.check <<END
 .mode box --limits on
 END
-.output memory
+.testcase 320
 .mode --limits 5,300,19
 .mode
-.output --verify END
+.check <<END
 .mode box --limits 5,300,19
 END
-.output memory
+.testcase 330
 .mode --limits 0,0,0
 .mode -v
-.output --verify END
+.check <<END
 .mode box --align "" --border on --blob-quote auto --colsep "" --escape auto --limits off --null "" --quote off --rowsep "" --sw 0 --tablename "" --textjsonb off --titles on --widths "" --wordwrap off
 END
 
-.output memory
+.testcase 400
 .mode --linelimit 123
 .mode
-.output --verify END
+.check <<END
 .mode box --limits 123,0,0
 END
 
-.output memory
+.testcase 410
 .mode --linelimit 0 -charlimit 123
 .mode
-.output --verify END
+.check <<END
 .mode box --limits 0,123,0
 END
 
-.output memory
+.testcase 420
 .mode --charlimit 0 -titlelimit 123
 .mode
-.output --verify END
+.check <<END
 .mode box --limits 0,0,123
 END
 
-.output memory
+.testcase 430
 .mode list
 .mode
-.output --verify END
+.check <<END
 .mode list
 END
 
-.output memory
+.testcase 440
 .mode -limits 0,123,0
 .mode
-.output --verify END
+.check <<END
 .mode list --limits 0,123,0
 END
 
-.output memory
+.testcase 450
 .mode -limits 123,0,0
 .mode
-.output --verify END
+.check <<END
 .mode list
 END
 
 # --titlelimit functionality
 #
-.output memory
+.testcase 500
 .mode line --limits off --titlelimit 20
 SELECT a AS 'abcdefghijklmnopqrstuvwxyz', b FROM t2 WHERE c=3;
-.output --verify END
+.check <<END
 abcdefghijklmnopq...: The quick fox jumps over the lazy brown dog
                    b: 2
 END
-.output memory
+.testcase 510
 .mode line --titlelimit 10
 SELECT a AS 'abcdefghijklmnopqrstuvwxyz', b FROM t2 WHERE c=3;
-.output --verify END
+.check <<END
 abcdefg...: The quick fox jumps over the lazy brown dog
          b: 2
 END
-.output memory
+.testcase 520
 .mode line --titlelimit 2
 SELECT a AS 'abcdefghijklmnopqrstuvwxyz', b FROM t2 WHERE c=3;
-.output --verify END
+.check <<END
 ab: The quick fox jumps over the lazy brown dog
  b: 2
 END
-.output memory
+.testcase 530
 .mode line --titlelimit 4
 SELECT a AS 'abcd', b FROM t2 WHERE c=3;
-.output --verify END
+.check <<END
 abcd: The quick fox jumps over the lazy brown dog
    b: 2
 END
-.output memory
+.testcase 540
 .mode line --titlelimit 3
 SELECT a AS 'abcd', b FROM t2 WHERE c=3;
-.output --verify END
+.check <<END
 ...: The quick fox jumps over the lazy brown dog
   b: 2
 END