@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2023 Isabella Basso do Amaral <isabbasso@riseup.net>
+ * Copyright © 2023 Intel Corporation
*/
#include <ctype.h>
@@ -8,12 +9,310 @@
#include <libkmod.h>
#include <pthread.h>
#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include "igt_aux.h"
#include "igt_core.h"
#include "igt_ktap.h"
#include "igt_list.h"
+enum ktap_phase {
+ KTAP_START,
+ SUITE_COUNT,
+ SUITE_START,
+ SUITE_NAME,
+ CASE_COUNT,
+ CASE_NAME,
+ SUB_RESULT,
+ CASE_RESULT,
+ SUITE_RESULT,
+};
+
+struct igt_ktap_results {
+ enum ktap_phase expect;
+ unsigned int suite_count;
+ unsigned int suite_last;
+ char *suite_name;
+ unsigned int case_count;
+ unsigned int case_last;
+ char *case_name;
+ unsigned int sub_last;
+ struct igt_list_head *results;
+};
+
+/**
+ * igt_ktap_parse:
+ *
+ * This function parses a line of text for KTAP report data
+ * and passes results back to IGT kunit layer.
+ */
+int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap)
+{
+ char *suite_name = NULL, *case_name = NULL, *msg = NULL;
+ struct igt_ktap_result *result;
+ int code = IGT_EXIT_INVALID;
+ unsigned int n, len;
+ char s[2];
+
+ /* KTAP report header */
+ if (igt_debug_on(sscanf(buf, "KTAP%*[ ]version%*[ ]%u %n",
+ &n, &len) == 1 && len == strlen(buf))) {
+ if (igt_debug_on(ktap->expect != KTAP_START))
+ return -EPROTO;
+
+ ktap->suite_count = 0;
+ ktap->expect = SUITE_COUNT;
+
+ /* malformed TAP test plan? */
+ } else if (len = 0,
+ igt_debug_on(sscanf(buf, " 1..%1[ ]", s) == 1)) {
+ return -EINPROGRESS;
+
+ /* valid test plan of a KTAP report */
+ } else if (igt_debug_on(sscanf(buf, "1..%u %n", &n, &len) == 1 &&
+ len == strlen(buf))) {
+ if (igt_debug_on(ktap->expect != SUITE_COUNT))
+ return -EPROTO;
+
+ if (!n)
+ return 0;
+
+ ktap->suite_count = n;
+ ktap->suite_last = 0;
+ ktap->suite_name = NULL;
+ ktap->expect = SUITE_START;
+
+ /* KTAP test suite header */
+ } else if (len = 0,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
+ &n, &len) == 1 && len == strlen(buf))) {
+ /*
+ * TODO: drop the following workaround as soon as
+ * kernel side issue of missing lines with top level
+ * KTAP version and test suite plan is fixed.
+ */
+ if (ktap->expect == KTAP_START) {
+ ktap->suite_count = 1;
+ ktap->suite_last = 0;
+ ktap->suite_name = NULL;
+ ktap->expect = SUITE_START;
+ }
+
+ if (igt_debug_on(ktap->expect != SUITE_START))
+ return -EPROTO;
+
+ ktap->expect = SUITE_NAME;
+
+ /* KTAP test suite name */
+ } else if (len = 0,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]#%*[ ]Subtest:%*[ ]%ms %n",
+ &suite_name, &len) == 1 && len == strlen(buf))) {
+ if (igt_debug_on(ktap->expect != SUITE_NAME))
+ return -EPROTO;
+
+ ktap->suite_name = suite_name;
+ suite_name = NULL;
+ ktap->case_count = 0;
+ ktap->expect = CASE_COUNT;
+
+ /* valid test plan of a KTAP test suite */
+ } else if (len = 0, free(suite_name), suite_name = NULL,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]1..%u %n",
+ &n, &len) == 1 && len == strlen(buf))) {
+ if (igt_debug_on(ktap->expect != CASE_COUNT))
+ return -EPROTO;
+
+ if (n) {
+ ktap->case_count = n;
+ ktap->case_last = 0;
+ ktap->case_name = NULL;
+ ktap->expect = CASE_RESULT;
+ } else {
+ ktap->expect = SUITE_RESULT;
+ }
+
+ /* KTAP parametrized test case header */
+ } else if (len = 0,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]KTAP%*[ ]version%*[ ]%u %n",
+ &n, &len) == 1 && len == strlen(buf))) {
+ if (igt_debug_on(ktap->expect != CASE_RESULT))
+ return -EPROTO;
+
+ ktap->sub_last = 0;
+ ktap->expect = CASE_NAME;
+
+ /* KTAP parametrized test case name */
+ } else if (len = 0,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]#%*[ ]Subtest:%*[ ]%ms %n",
+ &case_name, &len) == 1 && len == strlen(buf))) {
+ if (igt_debug_on(ktap->expect != CASE_NAME))
+ return -EPROTO;
+
+ n = ktap->case_last + 1;
+ ktap->expect = SUB_RESULT;
+
+ /* KTAP parametrized subtest result */
+ } else if (len = 0, free(case_name), case_name = NULL,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]ok%*[ ]%u%*[ ]%*[^#\n]%1[#\n]",
+ &n, s) == 2) ||
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]%*1[ ]not%*1[ ]ok%*[ ]%u%*[ ]%*[^#\n]%1[#\n]",
+ &n, s) == 2)) {
+ /* at lease one result of a parametrised subtest expected */
+ if (igt_debug_on(ktap->expect == SUB_RESULT &&
+ ktap->sub_last == 0))
+ ktap->expect = CASE_RESULT;
+
+ if (igt_debug_on(ktap->expect != CASE_RESULT) ||
+ igt_debug_on(n != ++ktap->sub_last))
+ return -EPROTO;
+
+ /* KTAP test case skip result */
+ } else if ((igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]ok%*[ ]%u%*[ ]%ms%*[ ]#%*[ ]SKIP %n",
+ &n, &case_name, &len) == 2 &&
+ len == strlen(buf))) ||
+ (len = 0, free(case_name), case_name = NULL,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]ok%*[ ]%u%*[ ]%ms%*[ ]#%*[ ]SKIP%*[ ]%m[^\n]",
+ &n, &case_name, &msg) == 3))) {
+ code = IGT_EXIT_SKIP;
+
+ /* KTAP test case pass result */
+ } else if ((free(case_name), case_name = NULL, free(msg), msg = NULL,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]ok%*[ ]%u%*[ ]%ms %n",
+ &n, &case_name, &len) == 2 &&
+ len == strlen(buf))) ||
+ (len = 0, free(case_name), case_name = NULL,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]ok%*[ ]%u%*[ ]%ms%*[ ]#%*[ ]%m[^\n]",
+ &n, &case_name, &msg) == 3))) {
+ code = IGT_EXIT_SUCCESS;
+
+ /* KTAP test case fail result */
+ } else if ((free(case_name), case_name = NULL, free(msg), msg = NULL,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]not%*1[ ]ok%*[ ]%u%*[ ]%ms %n",
+ &n, &case_name, &len) == 2 &&
+ len == strlen(buf))) ||
+ (len = 0, free(case_name), case_name = NULL,
+ igt_debug_on(sscanf(buf,
+ "%*1[ ]%*1[ ]%*1[ ]%*1[ ]not%*1[ ]ok%*[ ]%u%*[ ]%ms%*[ ]#%*[ ]%m[^\n]",
+ &n, &case_name, &msg) == 3))) {
+ code = IGT_EXIT_FAILURE;
+
+ /* KTAP test suite result */
+ } else if ((free(case_name), free(msg),
+ igt_debug_on(sscanf(buf, "ok%*[ ]%u%*[ ]%ms %n",
+ &n, &suite_name, &len) == 2 &&
+ len == strlen(buf))) ||
+ (len = 0, free(suite_name), suite_name = NULL,
+ igt_debug_on(sscanf(buf, "ok%*[ ]%u%*[ ]%ms%*[ ]%1[#]",
+ &n, &suite_name, s) == 3)) ||
+ (free(suite_name), suite_name = NULL,
+ igt_debug_on(sscanf(buf,
+ "not%*[ ]ok%*[ ]%u%*[ ]%ms %n",
+ &n, &suite_name, &len) == 2 &&
+ len == strlen(buf))) ||
+ (len = 0, free(suite_name), suite_name = NULL,
+ igt_debug_on(sscanf(buf,
+ "not%*[ ]ok%*[ ]%u%*[ ]%ms%*[ ]%1[#]",
+ &n, &suite_name, s) == 3))) {
+ if (igt_debug_on(ktap->expect != SUITE_RESULT) ||
+ igt_debug_on(strcmp(suite_name, ktap->suite_name)) ||
+ igt_debug_on(n != ++ktap->suite_last) ||
+ igt_debug_on(n > ktap->suite_count)) {
+ free(suite_name);
+ return -EPROTO;
+ }
+ free(suite_name);
+
+ /* last test suite? */
+ if (igt_debug_on(n == ktap->suite_count))
+ return 0;
+
+ ktap->suite_name = NULL;
+ ktap->expect = SUITE_START;
+
+ } else {
+ return -EINPROGRESS;
+ }
+
+ /* neither a test case name nor result */
+ if (ktap->expect != SUB_RESULT && code == IGT_EXIT_INVALID)
+ return -EINPROGRESS;
+
+ if (igt_debug_on(ktap->expect == SUB_RESULT &&
+ code != IGT_EXIT_INVALID) ||
+ igt_debug_on(code != IGT_EXIT_INVALID &&
+ ktap->expect != CASE_RESULT) ||
+ igt_debug_on(!ktap->suite_name) || igt_debug_on(!case_name) ||
+ igt_debug_on(ktap->expect == CASE_RESULT && ktap->case_name &&
+ strcmp(case_name, ktap->case_name)) ||
+ igt_debug_on(n > ktap->case_count) ||
+ igt_debug_on(n != (ktap->expect == SUB_RESULT ?
+ ktap->case_last + 1: ++ktap->case_last))) {
+ free(case_name);
+ free(msg);
+ return -EPROTO;
+ }
+
+ if (ktap->expect == SUB_RESULT) {
+ /* KTAP parametrized test case name */
+ ktap->case_name = case_name;
+
+ } else {
+ /* KTAP test case result */
+ ktap->case_name = NULL;
+
+ /* last test case in a suite */
+ if (n == ktap->case_count)
+ ktap->expect = SUITE_RESULT;
+ }
+
+ if (igt_debug_on((result = calloc(1, sizeof(*result)), !result))) {
+ free(case_name);
+ free(msg);
+ return -ENOMEM;
+ }
+
+ result->suite_name = ktap->suite_name;
+ result->case_name = case_name;
+ result->code = code;
+ result->msg = msg;
+ igt_list_add_tail(&result->link, ktap->results);
+
+ return -EINPROGRESS;
+}
+
+struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results)
+{
+ struct igt_ktap_results *ktap = calloc(1, sizeof(*ktap));
+
+ if (!ktap)
+ return NULL;
+
+ ktap->expect = KTAP_START;
+ ktap->results = results;
+
+ return ktap;
+}
+
+void igt_ktap_free(struct igt_ktap_results *ktap)
+{
+ free(ktap);
+}
+
#define DELIMITER "-"
struct ktap_parser_args {
@@ -332,6 +631,7 @@ static int parse_kmsg_for_tap(int fd, char *record, char *test_name)
* 0 if succeded
* -1 if error occurred
*/
+__maybe_unused
static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *failed_tests,
bool *found_tests, bool is_builtin)
{
@@ -445,62 +745,106 @@ static int parse_tap_level(int fd, char *base_test_name, int test_count, bool *f
*/
void *igt_ktap_parser(void *unused)
{
+ char record[BUF_LEN + 1], *buf, *suite_name = NULL, *case_name = NULL;
+ struct igt_ktap_results *ktap = NULL;
int fd = ktap_args.fd;
- char record[BUF_LEN + 1];
- bool is_builtin = ktap_args.is_builtin;
- char test_name[BUF_LEN + 1];
- bool failed_tests, found_tests;
- int test_count;
+ IGT_LIST_HEAD(list);
+ int err;
- failed_tests = false;
- found_tests = false;
+ ktap = igt_ktap_alloc(&list);
+ if (igt_debug_on(!ktap))
+ goto igt_ktap_parser_end;
-igt_ktap_parser_start:
- test_name[0] = '\0';
- test_name[BUF_LEN] = '\0';
+ while (err = read(fd, record, BUF_LEN), err > 0) {
+ struct igt_ktap_result *r, *rn;
- if (read(fd, record, BUF_LEN) < 0) {
- if (errno == EPIPE)
- igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
- else
- igt_warn("error reading kmsg (%m)\n");
+ /* skip kmsg continuation lines */
+ if (igt_debug_on(*record == ' '))
+ continue;
- goto igt_ktap_parser_end;
- }
+ /* NULL-terminate the record */
+ record[err] = '\0';
- test_count = find_next_tap_subtest(fd, record, test_name, is_builtin);
+ /* detect start of log message, continue if not found */
+ buf = strchrnul(record, ';');
+ if (igt_debug_on(*buf == '\0'))
+ continue;
+ buf++;
- switch (test_count) {
- case -2:
- /* Problems while reading the file */
- goto igt_ktap_parser_end;
- case -1:
- /* No test found */
- goto igt_ktap_parser_start;
- case 0:
- /* Tests found, but they're missing info */
- found_tests = true;
- goto igt_ktap_parser_end;
- default:
- found_tests = true;
+ err = igt_ktap_parse(buf, ktap);
- if (parse_tap_level(fd, test_name, test_count, &failed_tests, &found_tests,
- is_builtin) == -1)
+ /* parsing error */
+ if (err && err != -EINPROGRESS)
goto igt_ktap_parser_end;
- break;
+ igt_list_for_each_entry_safe(r, rn, &list, link) {
+ struct ktap_test_results_element *result = NULL;
+ int code = r->code;
+
+ if (code != IGT_EXIT_INVALID)
+ result = calloc(1, sizeof(*result));
+
+ if (result) {
+ snprintf(result->test_name, sizeof(result->test_name),
+ "%s-%s", r->suite_name, r->case_name);
+
+ if (code == IGT_EXIT_SUCCESS)
+ result->passed = true;
+ }
+
+ igt_list_del(&r->link);
+ if (r->suite_name != suite_name) {
+ free(suite_name);
+ suite_name = r->suite_name;
+ }
+ if (r->case_name != case_name) {
+ free(case_name);
+ case_name = r->case_name;
+ }
+ free(r->msg);
+ free(r);
+
+ /*
+ * no extra result record expected on start
+ * of parametrized test case -- skip it
+ */
+ if (code == IGT_EXIT_INVALID)
+ continue;
+
+ if (!result) {
+ err = -ENOMEM;
+ goto igt_ktap_parser_end;
+ }
+
+ pthread_mutex_lock(&results.mutex);
+ igt_list_add_tail(&result->link, &results.list);
+ pthread_mutex_unlock(&results.mutex);
+ }
+
+ /* end of KTAP report */
+ if (!err)
+ goto igt_ktap_parser_end;
}
- /* Parse topmost level */
- test_name[0] = '\0';
- parse_tap_level(fd, test_name, test_count, &failed_tests, &found_tests, is_builtin);
+ if (err < 0) {
+ if (errno == EPIPE)
+ igt_warn("kmsg truncated: too many messages. You may want to increase log_buf_len in kmcdline\n");
+ else
+ igt_warn("error reading kmsg (%m)\n");
+ }
igt_ktap_parser_end:
- results.still_running = false;
+ free(suite_name);
+ free(case_name);
- if (found_tests && !failed_tests)
+ if (!err)
ktap_args.ret = IGT_EXIT_SUCCESS;
+ results.still_running = false;
+
+ if (ktap)
+ igt_ktap_free(ktap);
+
return NULL;
}
@@ -1,5 +1,6 @@
/*
* Copyright © 2022 Isabella Basso do Amaral <isabbasso@riseup.net>
+ * Copyright © 2023 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -30,6 +31,20 @@
#include "igt_list.h"
+struct igt_ktap_result {
+ struct igt_list_head link;
+ char *suite_name;
+ char *case_name;
+ char *msg;
+ int code;
+};
+
+struct igt_ktap_results;
+
+struct igt_ktap_results *igt_ktap_alloc(struct igt_list_head *results);
+int igt_ktap_parse(const char *buf, struct igt_ktap_results *ktap);
+void igt_ktap_free(struct igt_ktap_results *ktap);
+
void *igt_ktap_parser(void *unused);
typedef struct ktap_test_results_element {
new file mode 100644
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: MIT
+/*
+* Copyright © 2023 Intel Corporation
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "igt_core.h"
+#include "igt_ktap.h"
+#include "igt_list.h"
+
+static void ktap_list(void)
+{
+ struct igt_ktap_result *result, *rn;
+ struct igt_ktap_results *ktap;
+ int suite = 1, test = 1;
+ IGT_LIST_HEAD(results);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+
+ igt_assert_eq(igt_ktap_parse("KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse("1..3\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_suite_1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" 1..3\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 1 test_case_1 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 2 test_case_2 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 3 test_case_3 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse("ok 1 test_suite_1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_suite_2\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" 1..1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 1 test_case_1 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse("ok 2 test_suite_2\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_suite_3\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" 1..4\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 1 test_case_1 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 2 test_case_2 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 3 test_case_3 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 4 test_case_4 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse("ok 3 test_suite_3\n", ktap), 0);
+
+ igt_ktap_free(ktap);
+
+ igt_assert_eq(igt_list_length(&results), 8);
+
+ igt_list_for_each_entry_safe(result, rn, &results, link) {
+ char *case_name, *suite_name;
+
+ igt_list_del(&result->link);
+
+ igt_assert_lt(0, asprintf(&case_name, "test_case_%u", test));
+ igt_assert_lt(0, asprintf(&suite_name, "test_suite_%u", suite));
+
+ igt_assert(result->case_name);
+ igt_assert_eq(strcmp(result->case_name, case_name), 0);
+ free(result->case_name);
+ free(case_name);
+
+ igt_assert(result->suite_name);
+ igt_assert_eq(strcmp(result->suite_name, suite_name), 0);
+ free(suite_name);
+
+ igt_assert(!result->msg);
+ igt_assert_eq(result->code, IGT_EXIT_SKIP);
+
+ if ((suite == 1 && test < 3) || (suite == 3 && test < 4)) {
+ test++;
+ } else {
+ free(result->suite_name);
+ suite++;
+ test = 1;
+ }
+
+ free(result);
+ }
+}
+
+static void ktap_results(void)
+{
+ struct igt_ktap_result *result;
+ struct igt_ktap_results *ktap;
+ char *suite_name, *case_name;
+ IGT_LIST_HEAD(results);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+
+ igt_assert_eq(igt_ktap_parse("KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse("1..1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_suite\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" 1..1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_case\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 1 parameter 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 2 parameter 2 # a comment\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 3 parameter 3 # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 4 parameter 4 # SKIP with a message\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" not ok 5 parameter 5\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" not ok 6 parameter 6 # failure message\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" ok 1 test_case\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse("not ok 1 test_suite\n", ktap), 0);
+
+ igt_ktap_free(ktap);
+
+ igt_assert_eq(igt_list_length(&results), 2);
+
+ result = igt_list_first_entry(&results, result, link);
+ igt_list_del(&result->link);
+ igt_assert_eq(strcmp(result->suite_name, "test_suite"), 0);
+ igt_assert_eq(strcmp(result->case_name, "test_case"), 0);
+ igt_assert_eq(result->code, IGT_EXIT_INVALID);
+ igt_assert(!result->msg);
+ free(result->msg);
+ suite_name = result->suite_name;
+ case_name = result->case_name;
+ free(result);
+
+ result = igt_list_first_entry(&results, result, link);
+ igt_list_del(&result->link);
+ igt_assert_eq(strcmp(result->suite_name, suite_name), 0);
+ igt_assert_eq(strcmp(result->case_name, case_name), 0);
+ igt_assert_neq(result->code, IGT_EXIT_INVALID);
+ free(result->msg);
+ free(suite_name);
+ free(case_name);
+ free(result);
+}
+
+static void ktap_success(void)
+{
+ struct igt_ktap_result *result;
+ struct igt_ktap_results *ktap;
+ IGT_LIST_HEAD(results);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+
+ igt_assert_eq(igt_ktap_parse("KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse("1..1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_suite\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" 1..1\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_assert(igt_list_empty(&results));
+
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_case\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_list_length(&results), 1);
+
+ igt_assert_eq(igt_ktap_parse(" ok 1 parameter # SKIP\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_list_length(&results), 1);
+
+ igt_assert_eq(igt_ktap_parse(" ok 1 test_case\n", ktap), -EINPROGRESS);
+ igt_assert_eq(igt_list_length(&results), 2);
+
+ igt_assert_eq(igt_ktap_parse("not ok 1 test_suite\n", ktap), 0);
+ igt_assert_eq(igt_list_length(&results), 2);
+
+ igt_ktap_free(ktap);
+
+ result = igt_list_last_entry(&results, result, link);
+ igt_list_del(&result->link);
+ igt_assert_eq(result->code, IGT_EXIT_SUCCESS);
+ free(result->msg);
+ free(result);
+
+ result = igt_list_last_entry(&results, result, link);
+ igt_list_del(&result->link);
+ free(result->suite_name);
+ free(result->case_name);
+ free(result->msg);
+ free(result);
+}
+
+static void ktap_top_version(void)
+{
+ struct igt_ktap_results *ktap;
+ IGT_LIST_HEAD(results);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse("1..1\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ /* TODO: change to -EPROTO as soon as related workaround is dropped */
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EINPROGRESS);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_suite\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse(" 1..1\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse(" KTAP version 1\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse(" # Subtest: test_case\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse(" ok 1 parameter 1\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse(" ok 1 test_case\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+
+ ktap = igt_ktap_alloc(&results);
+ igt_require(ktap);
+ igt_assert_eq(igt_ktap_parse("ok 1 test_suite\n", ktap), -EPROTO);
+ igt_ktap_free(ktap);
+}
+
+igt_main
+{
+ igt_subtest("list")
+ ktap_list();
+
+ igt_subtest("results")
+ ktap_results();
+
+ igt_subtest("success")
+ ktap_success();
+
+ igt_subtest("top-ktap-version")
+ ktap_top_version();
+}
@@ -10,6 +10,7 @@ lib_tests = [
'igt_exit_handler',
'igt_fork',
'igt_fork_helper',
+ 'igt_ktap_parser',
'igt_list_only',
'igt_invalid_subtest_name',
'igt_nesting',