Commit b87f63cb authored by Rusty Russell's avatar Rusty Russell

ccanlint: enhance and streamline "output" testing lines.

1) Require "" around input
2) Make them optional around output: if not there, loose match whitespace
3) Handle \n in output.
4) Document that "Given xxx" is optional.
5) Reject any non-matching comment lines starting with "given" or "outputs"
6) Fix missed test in ccan/cast
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent c3e9a058
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* macro or constant. * macro or constant.
* *
* Example: * Example:
* // Outputs "Initialized 32 values" * // Outputs "Initialized 32 values\n"
* #include <ccan/array_size/array_size.h> * #include <ccan/array_size/array_size.h>
* #include <stdlib.h> * #include <stdlib.h>
* #include <stdio.h> * #include <stdio.h>
......
...@@ -92,8 +92,8 @@ ...@@ -92,8 +92,8 @@
* printf("verbose mode on\n"); * printf("verbose mode on\n");
* return 0; * return 0;
* } * }
* // Given -v outputs 'verbose mode on' * // Given "-v" outputs "verbose mode on\n"
* // Given -v -C / outputs 'chdir to /. verbose mode on' * // Given "-v -C /" outputs "chdir to /. verbose mode on\n"
*/ */
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* License: LGPL (v2.1 or any later version) * License: LGPL (v2.1 or any later version)
* *
* Example: * Example:
* // Given "test" contains "3 t's in 'test string' * // Given "test" output contains "3 t's in 'test string'"
* #include <ccan/cast/cast.h> * #include <ccan/cast/cast.h>
* #include <stdint.h> * #include <stdint.h>
* #include <stdio.h> * #include <stdio.h>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* #include <stdio.h> * #include <stdio.h>
* #include <stdlib.h> * #include <stdlib.h>
* *
* // Given IHATEMATH outputs 0x98a3b8df * // Given "IHATEMATH" outputs 0x98a3b8df
* int main(int argc, char *argv[]) * int main(int argc, char *argv[])
* { * {
* if (argc != 2) { * if (argc != 2) {
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* plans. * plans.
* *
* Example: * Example:
* // Given tr A-Z a-z outputs tr a-z a-z * // Given "tr A-Z a-z" outputs tr a-z a-z
* #include <ccan/io/io.h> * #include <ccan/io/io.h>
* #include <ccan/err/err.h> * #include <ccan/err/err.h>
* #include <assert.h> * #include <assert.h>
......
...@@ -80,9 +80,9 @@ ...@@ -80,9 +80,9 @@
* jmap_free(arg); * jmap_free(arg);
* return 0; * return 0;
* } * }
* // Given "--help" output contains "Arg 1 ('--help') is a long opt of 4 chars" * // Given "--help" output contains "Arg 1 ('--help') is a long opt of 4 chars\n"
* // Given "-h" output contains "Arg 1 ('-h') is a short opt of 1 chars" * // Given "-h" output contains "Arg 1 ('-h') is a short opt of 1 chars\n"
* // Given "foo" output contains "Arg 1 ('foo') is a normal arg of 3 chars" * // Given "foo" output contains "Arg 1 ('foo') is a normal arg of 3 chars\n"
* *
* License: LGPL (v2.1 or any later version) * License: LGPL (v2.1 or any later version)
* Author: Rusty Russell <rusty@rustcorp.com.au> * Author: Rusty Russell <rusty@rustcorp.com.au>
......
...@@ -41,9 +41,9 @@ ...@@ -41,9 +41,9 @@
* } * }
* return 0; * return 0;
* } * }
* // Given 'a b c' outputs No arguments start with -. * // Given "a b c" outputs No arguments start with -.
* // Given 'a -b c' outputs 2, * // Given "a -b c" outputs 2,
* // Given 'a -b -c d' outputs 2,3, * // Given "a -b -c d" outputs 2,3,
*/ */
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
......
...@@ -25,9 +25,9 @@ ...@@ -25,9 +25,9 @@
* it too). * it too).
* *
* Example: * Example:
* // Given '' outputs 'body' * // Outputs "body\n"
* // Given 'From' outputs ' <from@example.com>' * // Given "From" outputs <from@example.com>
* // Given 'To' outputs ' <to@example.com>' * // Given "To" outputs <to@example.com>
* char buf[] = "From: <from@example.com>\n" * char buf[] = "From: <from@example.com>\n"
* "To: <to@example.com>\n\n" * "To: <to@example.com>\n\n"
* "body\n"; * "body\n";
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
* struct bytestring out; * struct bytestring out;
* *
* msg = rfc822_start(NULL, buf, sizeof(buf)); * msg = rfc822_start(NULL, buf, sizeof(buf));
* if (!argv[1] || !argv[1][0]) * if (!argv[1])
* out = rfc822_body(msg); * out = rfc822_body(msg);
* else { * else {
* struct rfc822_header *hdr; * struct rfc822_header *hdr;
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
* Returns one 64-bit word as the hash function result. * Returns one 64-bit word as the hash function result.
* *
* Example: * Example:
* // Outputs "cf2794e0277187b7" * // Outputs cf2794e0277187b7
* #include <stdio.h> * #include <stdio.h>
* #include <ccan/siphash/siphash.h> * #include <ccan/siphash/siphash.h>
* *
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
* Returns one 64-bit word as the hash function result. * Returns one 64-bit word as the hash function result.
* *
* Example: * Example:
* // Outputs "cf2794e0277187b7" * // Outputs cf2794e0277187b7
* #include <stdio.h> * #include <stdio.h>
* #include <ccan/siphash/siphash.h> * #include <ccan/siphash/siphash.h>
* *
......
...@@ -40,9 +40,9 @@ ...@@ -40,9 +40,9 @@
* printf("\n"); * printf("\n");
* return 0; * return 0;
* } * }
* // Given 'foo' outputs 'foo at 1. ' * // Given "foo" outputs "foo at 1. \n"
* // Given 'foo bar' outputs 'bar at 2. foo at 1. ' * // Given "foo bar" outputs "bar at 2. foo at 1. \n"
* // Given 'foo foo bar zebra' outputs 'bar at 3. foo at 1. zebra at 4. ' * // Given "foo foo bar zebra" outputs "bar at 3. foo at 1. zebra at 4. \n"
*/ */
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
......
...@@ -45,8 +45,8 @@ ...@@ -45,8 +45,8 @@
* printf("\n"); * printf("\n");
* return 0; * return 0;
* } * }
* // Given "foo bar" outputs "bar foo " * // Given "foo bar" outputs "bar foo \n"
* // Given "foo foo bar" outputs "bar foo " * // Given "foo foo bar" outputs "bar foo \n"
* *
* License: CC0 (but some dependencies are LGPL!) * License: CC0 (but some dependencies are LGPL!)
* Author: Rusty Russell <rusty@rustcorp.com.au> * Author: Rusty Russell <rusty@rustcorp.com.au>
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* License: CC0 (Public domain) * License: CC0 (Public domain)
* *
* Example: * Example:
* // Given foo/bar.c outputs basename is bar.c * // Given "foo/bar.c" outputs basename is bar.c
* #include <ccan/take/take.h> * #include <ccan/take/take.h>
* #include <string.h> * #include <string.h>
* *
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
* // Silly program which keeps a cache of uppercased strings. * // Silly program which keeps a cache of uppercased strings.
* // The cache wants to keep strings around even after they may have * // The cache wants to keep strings around even after they may have
* // been "freed" by the caller. * // been "freed" by the caller.
* // Given 'hello' outputs '1 cache hits HELLO ' * // Given "hello" outputs "1 cache hits HELLO \n"
* // Given 'hello hello there' outputs '4 cache hits HELLO HELLO THERE ' * // Given "hello hello there" outputs "4 cache hits HELLO HELLO THERE \n"
* #include <stdio.h> * #include <stdio.h>
* #include <err.h> * #include <err.h>
* #include <string.h> * #include <string.h>
......
...@@ -164,9 +164,9 @@ char *tal_strjoin(const void *ctx, char *strings[], const char *delim, ...@@ -164,9 +164,9 @@ char *tal_strjoin(const void *ctx, char *strings[], const char *delim,
* regcomp(3), regex(3). * regcomp(3), regex(3).
* *
* Example: * Example:
* // Given 'My name is Rusty' outputs 'Hello Rusty!' * // Given "My name is Rusty" outputs "Hello Rusty!\n"
* // Given 'my first name is Rusty Russell' outputs 'Hello Rusty Russell!' * // Given "my first name is Rusty Russell" outputs "Hello Rusty Russell!\n"
* // Given 'My name isnt Rusty Russell' outputs 'Hello there!' * // Given "My name isnt Rusty Russell" outputs "Hello there!\n"
* int main(int argc, char *argv[]) * int main(int argc, char *argv[])
* { * {
* char *person, *input; * char *person, *input;
......
...@@ -54,8 +54,8 @@ ...@@ -54,8 +54,8 @@
* container_get(&sc), *container_get(&ic) - 1); * container_get(&sc), *container_get(&ic) - 1);
* return 0; * return 0;
* } * }
* // Given "foo" outputs "Last arg is foo of 1 arguments" * // Given "foo" outputs "Last arg is foo of 1 arguments\n"
* // Given "foo bar" outputs "Last arg is bar of 2 arguments" * // Given "foo bar" outputs "Last arg is bar of 2 arguments\n"
* *
* License: CC0 (Public domain) * License: CC0 (Public domain)
* *
......
'\" t '\" t
.\" Title: ccanlint .\" Title: ccanlint
.\" Author: [see the "AUTHOR" section] .\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 12/05/2011 .\" Date: 09/28/2015
.\" Manual: \ \& .\" Manual: \ \&
.\" Source: \ \& .\" Source: \ \&
.\" Language: English .\" Language: English
.\" .\"
.TH "CCANLINT" "1" "12/05/2011" "\ \&" "\ \&" .TH "CCANLINT" "1" "09/28/2015" "\ \&" "\ \&"
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * Define some portability stuff .\" * Define some portability stuff
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
...@@ -36,7 +36,7 @@ ccanlint \- Make CCAN code modules, and the brightness up\&. ...@@ -36,7 +36,7 @@ ccanlint \- Make CCAN code modules, and the brightness up\&.
.sp .sp
No encoder? No need to \fBccanlint\fR\&. You programmer? Excited to \fBccanlint\fR! No encoder? No need to \fBccanlint\fR\&. You programmer? Excited to \fBccanlint\fR!
.sp .sp
CCAN module is small code of the song\&. \fBccanlint\fR full CCAN testing tool\&. Each test spray bit of wisdom\&. Also score\&. Good score good\&. Bad bad score\&. CCAN module is small code of the song\&. \fBccanlint\fR full CCAN testing tool\&. Each test spray bit of wisdom\&. Also score\&. Good score good\&. Bad score bad\&.
.sp .sp
\fBccanlint\fR expect the source code in this directory, or command line can be more than one\&. Exit 0 happy if all modules all tests happy\&. \fBccanlint\fR expect the source code in this directory, or command line can be more than one\&. Exit 0 happy if all modules all tests happy\&.
.SH "OPTIONS" .SH "OPTIONS"
...@@ -73,14 +73,12 @@ Graphviz, then die happy\&. ...@@ -73,14 +73,12 @@ Graphviz, then die happy\&.
.PP .PP
\fB\-k, \-\-keep\fR \fB\-k, \-\-keep\fR
.RS 4 .RS 4
\fBccanlint\fR \fBccanlint\fR
normally make mess temporary directory, but now it later in forensic\&. normally make mess temporary directory, but now it later in forensic\&.
.RE .RE
.PP .PP
\fB\-s, \-\-summary\fR \fB\-s, \-\-summary\fR
.RS 4 .RS 4
\fBccanlint\fR \fBccanlint\fR
just realized there is no message unless you die horrible\&. just realized there is no message unless you die horrible\&.
.RE .RE
...@@ -107,7 +105,6 @@ Do not run all tests\&. Run this test, and the proof you need\&. Used many times ...@@ -107,7 +105,6 @@ Do not run all tests\&. Run this test, and the proof you need\&. Used many times
.PP .PP
\fB\-\-compiler\fR=\fICOMPILER\fR \fB\-\-compiler\fR=\fICOMPILER\fR
.RS 4 .RS 4
\fBccanlint\fR \fBccanlint\fR
read config\&.h about finding read config\&.h about finding
\fICCAN_COMPILER\fR\&. Otherwise use the default when it was built\&. The change, to use this compiler\&. \fICCAN_COMPILER\fR\&. Otherwise use the default when it was built\&. The change, to use this compiler\&.
...@@ -148,7 +145,6 @@ question may help to write one\&. ...@@ -148,7 +145,6 @@ question may help to write one\&.
.PP .PP
\fBdepends_exist\fR \fBdepends_exist\fR
.RS 4 .RS 4
\fI_info\fR \fI_info\fR
file CCAN other module without saying, must find\&. It is not score 0\&. file CCAN other module without saying, must find\&. It is not score 0\&.
.RE .RE
...@@ -197,8 +193,7 @@ unhappy\&. ...@@ -197,8 +193,7 @@ unhappy\&.
\fBhash_if\fR \fBhash_if\fR
.RS 4 .RS 4
Module wants Module wants
\fBccanlint\fR \fBccanlint\fR\fIconfig\&.h\fR
\fIconfig\&.h\fR
"#define HAVE_FEATURE" for all feature\&. Function test "#if HAVE_FEATURE" no "#ifdef HAVE_FEATURE" because user might not know about the role at all\&. Intelligent GCC flag "#define HAVE_FEATURE" for all feature\&. Function test "#if HAVE_FEATURE" no "#ifdef HAVE_FEATURE" because user might not know about the role at all\&. Intelligent GCC flag
\fI\-Wundef\fR \fI\-Wundef\fR
say HAVE_FEATURE not 0, not 1! but only if the use of say HAVE_FEATURE not 0, not 1! but only if the use of
...@@ -207,7 +202,6 @@ say HAVE_FEATURE not 0, not 1! but only if the use of ...@@ -207,7 +202,6 @@ say HAVE_FEATURE not 0, not 1! but only if the use of
.PP .PP
\fBinfo_documentation_exists\fR \fBinfo_documentation_exists\fR
.RS 4 .RS 4
\fI_info\fR \fI_info\fR
file format is pretty comments\&. Copying someone\&. It is not difficult write documentation! file format is pretty comments\&. Copying someone\&. It is not difficult write documentation!
.RE .RE
...@@ -250,7 +244,6 @@ Hostile to BSD license module, but requires another module of the GPL\&. Perhaps ...@@ -250,7 +244,6 @@ Hostile to BSD license module, but requires another module of the GPL\&. Perhaps
.PP .PP
\fBmain_header_exists\fR \fBmain_header_exists\fR
.RS 4 .RS 4
\fBccanlint\fR \fBccanlint\fR
know the module name directory name\&. Expect the same name for header\&. know the module name directory name\&. Expect the same name for header\&.
.RE .RE
...@@ -283,7 +276,6 @@ Linux kernel programmers more, solve the problem for the space of the final ban ...@@ -283,7 +276,6 @@ Linux kernel programmers more, solve the problem for the space of the final ban
.PP .PP
\fBexamples_compile\fR \fBexamples_compile\fR
.RS 4 .RS 4
\fBccanlint\fR \fBccanlint\fR
very smart! Take very smart! Take
\fIExample:\fR \fIExample:\fR
...@@ -302,15 +294,16 @@ says wow! ...@@ -302,15 +294,16 @@ says wow!
\fBexamples_run\fR \fBexamples_run\fR
.RS 4 .RS 4
If the example program that comments like If the example program that comments like
\fI// given foo outputs bar\fR \fI// Given "foo" outputs "bar"\fR\fBccanlint\fR
\fBccanlint\fR will run the program with
will run the food program
\fIfoo\fR \fIfoo\fR
in the command line and standard input\&. Happy if in the command line and standard input\&. Happy if
\fIbar\fR \fIbar\fR
are out\&. You can do \*(Aq or " about the production or determined\&. You can also are out\&. If quotes around
\fIoutput contains\fR \fIbar\fR
more relaxed controls\&. exact match needed; without quotes whitespace matches any other space and trailing ignored\&. \en is also supported for matching\&. You can also
\fI"output contains"\fR
to pass if the output contains the string\&.
.RE .RE
.PP .PP
\fBmodule_links\fR \fBmodule_links\fR
...@@ -364,7 +357,6 @@ Other files ...@@ -364,7 +357,6 @@ Other files
.PP .PP
\fBtests_pass\fR \fBtests_pass\fR
.RS 4 .RS 4
\fIrun\fR \fIrun\fR
and and
\fIapi\fR \fIapi\fR
...@@ -373,7 +365,6 @@ test happy departure\&. If not happy, offer debugger\&. ...@@ -373,7 +365,6 @@ test happy departure\&. If not happy, offer debugger\&.
.PP .PP
\fBtests_pass_valgrind\fR \fBtests_pass_valgrind\fR
.RS 4 .RS 4
\fBvalgrind\fR \fBvalgrind\fR
the tool of all the tool of all
\fIrun\fR \fIrun\fR
...@@ -390,7 +381,6 @@ section, make "tests_pass_valgrind test/TESTNAME:FAIL"\&. If required valgrind a ...@@ -390,7 +381,6 @@ section, make "tests_pass_valgrind test/TESTNAME:FAIL"\&. If required valgrind a
.PP .PP
\fBtests_pass_valgrind_noleaks\fR \fBtests_pass_valgrind_noleaks\fR
.RS 4 .RS 4
\fBvalgrind\fR \fBvalgrind\fR
complain if the memory leak test\&. complain if the memory leak test\&.
\fI_info\fR \fI_info\fR
......
...@@ -175,11 +175,13 @@ test, but happy: ...@@ -175,11 +175,13 @@ test, but happy:
bad example *ccanlint* says wow! bad example *ccanlint* says wow!
*examples_run*:: *examples_run*::
If the example program that comments like '// given foo outputs bar' If the example program that comments like '// [Given "foo"] outputs
*ccanlint* will run the food program 'foo' in the command line and "bar"' then *ccanlint* will run the program with 'foo' in the
standard input. Happy if 'bar' are out. You can do ' or " about command line and standard input. Happy if 'bar' are out and exit 0.
the production or determined. You can also 'output contains' more If quotes around 'bar' exact match needed; without quotes whitespace matches
relaxed controls. any other space and trailing ignored. \n is also supported for
matching. You can also '"output contains"' to pass if the output
contains the string.
*module_links*:: *module_links*::
CCAN link to the program module simply no error. CCAN link to the program module simply no error.
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <tools/tools.h> #include <tools/tools.h>
#include <ccan/foreach/foreach.h> #include <ccan/foreach/foreach.h>
#include <ccan/str/str.h> #include <ccan/str/str.h>
#include <ccan/tal/str/str.h>
#include <ccan/cast/cast.h> #include <ccan/cast/cast.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -81,146 +82,108 @@ static bool scan_for(const void *ctx, const char *input, const char *fmt, ...) ...@@ -81,146 +82,108 @@ static bool scan_for(const void *ctx, const char *input, const char *fmt, ...)
} }
static char *find_expect(struct ccan_file *file, static char *find_expect(struct ccan_file *file,
char **lines, char **input, bool *exact, char **lines, char **input,
bool *contains, bool *whitespace, bool *error,
unsigned *line) unsigned *line)
{ {
char *expect; char *rest, *expect;
const char *fmt;
*error = false;
for (; lines[*line]; (*line)++) { for (; lines[*line]; (*line)++) {
char *p = lines[*line] + strspn(lines[*line], " \t"); char *p = lines[*line] + strspn(lines[*line], " \t");
if (!strstarts(p, "//")) if (!strstarts(p, "//"))
continue; continue;
p += strspn(p, "/ "); p += strspn(p, "/ ");
foreach_ptr(fmt,
"given '%s', outputs '%s'",
"given '%s' outputs '%s'",
"given \"%s\", outputs \"%s\"",
"given \"%s\" outputs \"%s\"") {
if (scan_for(file, p, fmt, input, &expect)) {
*exact = true;
return expect;
}
}
foreach_ptr(fmt,
"given '%s', output contains '%s'",
"given '%s' output contains '%s'",
"given \"%s\", output contains \"%s\"",
"given \"%s\" output contains \"%s\"") {
if (scan_for(file, p, fmt, input, &expect)) {
*exact = false;
return expect;
}
}
foreach_ptr(fmt, "outputs '%s'", "outputs \"%s\"") { /* With or without input? */
if (scan_for(file, p, fmt, &expect)) { if (strncasecmp(p, "given", strlen("given")) == 0) {
*input = cast_const(char *, ""); /* Must be of form <given "X"> */
*exact = true; if (!scan_for(file, p, "given \"%s\" %s", input, &p)) {
return expect; *error = true;
return p;
} }
} else {
*input = NULL;
} }
foreach_ptr(fmt, if (scan_for(file, p, "outputs \"%s\"", &expect)) {
"given '%s', output contains '%s'", *whitespace = true;
"given '%s' output contains '%s'", *contains = false;
"given \"%s\", output contains \"%s\"",
"given \"%s\" output contains \"%s\"") {
if (scan_for(file, p, fmt, input, &expect)) {
*exact = false;
return expect; return expect;
} }
}
/* Unquoted versions... we can get this wrong! */ if (scan_for(file, p, "output contains \"%s\"", &expect)) {
foreach_ptr(fmt, *whitespace = true;
"given %s, outputs '%s'", *contains = true;
"given '%s', outputs %s",
"given %s, outputs \"%s\"",
"given \"%s\", outputs %s",
"given %s, outputs %s",
"given %s outputs '%s'",
"given '%s' outputs %s",
"given %s outputs \"%s\"",
"given \"%s\" outputs %s",
"given %s outputs %s") {
if (scan_for(file, p, fmt, input, &expect)) {
*exact = true;
return expect; return expect;
} }
}
foreach_ptr(fmt, /* Whitespace-ignoring versions. */
"given %s, output contains '%s'", if (scan_for(file, p, "outputs %s", &expect)) {
"given '%s', output contains %s", *whitespace = false;
"given %s, output contains \"%s\"", *contains = false;
"given \"%s\", output contains %s",
"given %s, output contains %s",
"given %s output contains '%s'",
"given '%s' output contains %s",
"given %s output contains \"%s\"",
"given \"%s\" output contains %s",
"given %s output contains %s") {
if (scan_for(file, p, fmt, input, &expect)) {
*exact = false;
return expect; return expect;
} }
}
foreach_ptr(fmt, if (scan_for(file, p, "output contains %s", &expect)) {
"outputs '%s'", *whitespace = false;
"outputs \"%s\"", *contains = true;
"outputs %s") {
if (scan_for(file, p, fmt, &expect)) {
*input = cast_const(char *, "");
*exact = true;
return expect; return expect;
} }
}
foreach_ptr(fmt, /* Other malformed line? */
"output contains '%s'", if (*input || !strncasecmp(p, "output", strlen("output"))) {
"output contains \"%s\"", *error = true;
"output contains %s") { return p;
if (scan_for(file, p, fmt, &expect)) {
*input = cast_const(char *, "");
*exact = false;
return expect;
}
} }
} }
return NULL; return NULL;
} }
static char *trim(char *string)
{
while (strends(string, "\n"))
string[strlen(string)-1] = '\0';
return string;
}
static char *unexpected(struct ccan_file *i, const char *input, static char *unexpected(struct ccan_file *i, const char *input,
const char *expect, bool exact) const char *expect, bool contains, bool whitespace)
{ {
char *output, *cmd; char *output, *cmd;
const char *p;
bool ok; bool ok;
unsigned int default_time = default_timeout_ms; unsigned int default_time = default_timeout_ms;
if (input)
cmd = tal_fmt(i, "echo '%s' | %s %s", cmd = tal_fmt(i, "echo '%s' | %s %s",
input, i->compiled[COMPILE_NORMAL], input); input, i->compiled[COMPILE_NORMAL], input);
else
cmd = tal_fmt(i, "%s", i->compiled[COMPILE_NORMAL]);
output = run_with_timeout(i, cmd, &ok, &default_time); output = run_with_timeout(i, cmd, &ok, &default_time);
if (!ok) if (!ok)
return tal_fmt(i, "Exited with non-zero status\n"); return tal_fmt(i, "Exited with non-zero status\n");
if (exact) { /* Substitute \n */
if (streq(output, expect) || streq(trim(output), expect)) while ((p = strstr(expect, "\\n")) != NULL) {
expect = tal_fmt(cmd, "%.*s\n%s", (int)(p - expect), expect,
p+2);
}
if (!whitespace) {
/* Normalize to spaces. */
expect = tal_strjoin(cmd,
tal_strsplit(cmd, expect, " \n\t",
STR_NO_EMPTY),
" ", STR_NO_TRAIL);
output = tal_strjoin(cmd,
tal_strsplit(cmd, output, " \n\t",
STR_NO_EMPTY),
" ", STR_NO_TRAIL);
}
if (contains) {
if (strstr(output, expect))
return NULL; return NULL;
} else { } else {
if (strstr(output, expect)) if (streq(output, expect))
return NULL; return NULL;
} }
return output; return output;
} }
...@@ -237,21 +200,32 @@ static void run_examples(struct manifest *m, ...@@ -237,21 +200,32 @@ static void run_examples(struct manifest *m,
list_for_each(list, i, list) { list_for_each(list, i, list) {
char **lines, *expect, *input, *output; char **lines, *expect, *input, *output;
unsigned int linenum = 0; unsigned int linenum = 0;
bool exact; bool contains, whitespace, error;
lines = get_ccan_file_lines(i); lines = get_ccan_file_lines(i);
for (expect = find_expect(i, lines, &input, &exact, for (expect = find_expect(i, lines, &input,
&contains, &whitespace, &error,
&linenum); &linenum);
expect; expect;
linenum++, linenum++,
expect = find_expect(i, lines, &input, expect = find_expect(i, lines, &input,
&exact, &linenum)) { &contains, &whitespace,
&error, &linenum)) {
if (error) {
score_file_error(score, i, linenum+1,
"Unparsable test line '%s'",
lines[linenum]);
score->pass = false;
break;
}
if (i->compiled[COMPILE_NORMAL] == NULL) if (i->compiled[COMPILE_NORMAL] == NULL)
continue; continue;
score->total++; score->total++;
output = unexpected(i, input, expect, exact); output = unexpected(i, input, expect,
contains, whitespace);
if (!output) { if (!output) {
score->score++; score->score++;
continue; continue;
...@@ -259,7 +233,7 @@ static void run_examples(struct manifest *m, ...@@ -259,7 +233,7 @@ static void run_examples(struct manifest *m,
score_file_error(score, i, linenum+1, score_file_error(score, i, linenum+1,
"output '%s' didn't %s '%s'\n", "output '%s' didn't %s '%s'\n",
output, output,
exact ? "match" : "contain", contains ? "contain" : "match",
expect); expect);
score->pass = false; score->pass = false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment