Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 17 Mar 2021 14:05:17 GMT
From:      Alex Richardson <arichardson@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: cb8330371482 - stable/13 - Import atf 0.22 snapshot ca73d08c3fc1ecffc1f1c97458c31ab82c12bb01
Message-ID:  <202103171405.12HE5HQN085994@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by arichardson:

URL: https://cgit.FreeBSD.org/src/commit/?id=cb8330371482613e150f29c8527153705fa47797

commit cb8330371482613e150f29c8527153705fa47797
Author:     Alex Richardson <arichardson@FreeBSD.org>
AuthorDate: 2021-02-04 14:48:10 +0000
Commit:     Alex Richardson <arichardson@FreeBSD.org>
CommitDate: 2021-03-17 12:21:06 +0000

    Import atf 0.22 snapshot ca73d08c3fc1ecffc1f1c97458c31ab82c12bb01
    
    This includes improvements to the atf-sh helper functions that
    significantly reduce the number of spawned processes for each test
    and therefore speeds up running the testsuite noticeably.
    
    (cherry picked from commit c203bd70b5957f85616424b6fa374479372d06e3)
---
 contrib/atf/.cirrus.yml                     |  26 ++++++
 contrib/atf/.gitignore                      |  25 ++++++
 contrib/atf/.travis.yml                     |  25 ++++++
 contrib/atf/NEWS                            |  18 ++++
 contrib/atf/README.md                       |  47 ++++++++++
 contrib/atf/atf-c++/atf-c++.3               |   4 +-
 contrib/atf/atf-c++/detail/test_helpers.hpp |   5 ++
 contrib/atf/atf-c++/tests.hpp               |   2 +-
 contrib/atf/atf-c++/utils.cpp               |   7 ++
 contrib/atf/atf-c++/utils.hpp               |   1 +
 contrib/atf/atf-c++/utils_test.cpp          |   1 +
 contrib/atf/atf-c/.gitignore                |   1 +
 contrib/atf/atf-c/check.c                   |   3 +-
 contrib/atf/atf-c/detail/fs_test.c          |   9 +-
 contrib/atf/atf-c/detail/list.c             |   2 +-
 contrib/atf/atf-c/detail/process.c          |   3 +-
 contrib/atf/atf-c/detail/test_helpers.h     |   7 ++
 contrib/atf/atf-c/tc.c                      | 116 +++++++++++++++++++------
 contrib/atf/atf-c/utils.c                   |  10 +++
 contrib/atf/atf-c/utils.h                   |   1 +
 contrib/atf/atf-c/utils_test.c              |   1 +
 contrib/atf/atf-sh/.gitignore               |   2 +
 contrib/atf/atf-sh/atf-check.1              |  15 +++-
 contrib/atf/atf-sh/atf-check.cpp            | 128 +++++++++++++++++++++++++---
 contrib/atf/atf-sh/atf-sh.3                 |  11 ++-
 contrib/atf/atf-sh/atf_check_test.sh        |  24 ++++++
 contrib/atf/atf-sh/libatf-sh.subr           |  32 ++++++-
 contrib/atf/atf-sh/misc_helpers.sh          |  48 +++++++++++
 contrib/atf/doc/.gitignore                  |   1 +
 contrib/atf/doc/atf-test-case.4             |  17 ++--
 lib/atf/libatf-c++/tests/Makefile           |   2 +
 31 files changed, 541 insertions(+), 53 deletions(-)

diff --git a/contrib/atf/.cirrus.yml b/contrib/atf/.cirrus.yml
new file mode 100644
index 000000000000..fd9b6e4a47df
--- /dev/null
+++ b/contrib/atf/.cirrus.yml
@@ -0,0 +1,26 @@
+env:
+  CIRRUS_CLONE_DEPTH: 1
+  ARCH: amd64
+
+task:
+  matrix:
+    - name: 13.0-CURRENT
+      freebsd_instance:
+        image_family: freebsd-13-0-snap
+    - name: 12.2-STABLE
+      freebsd_instance:
+        image_family: freebsd-12-2-snap
+    - name: 12.1-RELEASE
+      freebsd_instance:
+        image_family: freebsd-12-1
+  install_script:
+    - sed -i.bak -e 's,pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly,pkg+http://pkg.FreeBSD.org/\${ABI}/latest,' /etc/pkg/FreeBSD.conf
+    - ASSUME_ALWAYS_YES=yes pkg bootstrap -f
+    - pkg install -y autoconf automake libtool kyua
+  script:
+    - env JUNIT_OUTPUT=$(pwd)/test-results.xml ./admin/travis-build.sh
+  always:
+    junit_artifacts:
+      path: "test-results.xml"
+      type: text/xml
+      format: junit
diff --git a/contrib/atf/.gitignore b/contrib/atf/.gitignore
new file mode 100644
index 000000000000..396785ce2052
--- /dev/null
+++ b/contrib/atf/.gitignore
@@ -0,0 +1,25 @@
+*.la
+*.lo
+*.o
+*.pc
+*_helper
+*_helpers
+*_test
+.deps
+.dirstamp
+.libs
+
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.h
+config.h.in
+config.h.in~
+config.log
+config.status
+configure
+installcheck.log
+libtool
+stamp-h1
+testsuite.log
diff --git a/contrib/atf/.travis.yml b/contrib/atf/.travis.yml
new file mode 100644
index 000000000000..1949aae54468
--- /dev/null
+++ b/contrib/atf/.travis.yml
@@ -0,0 +1,25 @@
+language: cpp
+
+compiler:
+    - gcc
+    - clang
+
+before_install:
+    - ./admin/travis-install-deps.sh
+
+env:
+    - ARCH=amd64 AS_ROOT=no
+    - ARCH=amd64 AS_ROOT=yes
+    - ARCH=i386 AS_ROOT=no
+
+matrix:
+    exclude:
+        - compiler: clang
+          env: ARCH=i386 AS_ROOT=no
+
+script:
+    - ./admin/travis-build.sh
+
+notifications:
+    email:
+        - atf-log@googlegroups.com
diff --git a/contrib/atf/NEWS b/contrib/atf/NEWS
index f1764e0d9dda..671ee81ff6ff 100644
--- a/contrib/atf/NEWS
+++ b/contrib/atf/NEWS
@@ -1,6 +1,24 @@
 Major changes between releases                  Automated Testing Framework
 ===========================================================================
 
+Changes in version 0.22
+***********************
+
+STILL UNDER DEVELOPMENT; NOT RELEASED YET.
+DON'T FORGET TO BUMP THE -version-info PRE-RELEASE IF NECESSARY!
+
+* Issue #23: Fix double-free triggered by atf_map_insert in low memory
+  scenarios, caused by an overlook in the atf_list code.
+
+* Issue #29: Fixed various typos and formatting errors in manual pages.
+
+* Issue #31: Added require.progs metadata properties to the tests that
+  need a compiler to run.
+
+* Added the atf_check_not_equal function to atf-sh to check for
+  unequal values.
+
+
 Changes in version 0.21
 ***********************
 
diff --git a/contrib/atf/README.md b/contrib/atf/README.md
new file mode 100644
index 000000000000..d245552f35c9
--- /dev/null
+++ b/contrib/atf/README.md
@@ -0,0 +1,47 @@
+# Welcome to the ATF project!
+
+ATF, or Automated Testing Framework, is a **collection of libraries** to
+write test programs in **C, C++ and POSIX shell**.
+
+The ATF libraries offer a simple API.  The API is orthogonal through the
+various bindings, allowing developers to quickly learn how to write test
+programs in different languages.
+
+ATF-based test programs offer a **consistent end-user command-line
+interface** to allow both humans and automation to run the tests.
+
+ATF-based test programs **rely on an execution engine** to be run and
+this execution engine is *not* shipped with ATF.
+**[Kyua](https://github.com/jmmv/kyua/) is the engine of choice.**
+
+## Download
+
+Formal releases for source files are available for download from GitHub:
+
+* [atf 0.20](../../releases/tag/atf-0.20), released on February 7th, 2014.
+
+## Installation
+
+You are encouraged to install binary packages for your operating system
+wherever available:
+
+* Fedora 20 and above: install the `atf` package with `yum install atf`.
+
+* FreeBSD 10.0 and above: install the `atf` package with `pkg install atf`.
+
+* NetBSD with pkgsrc: install the `pkgsrc/devel/atf` package.
+
+* OpenBSD: install the `devel/atf` package with `pkg_add atf`.
+
+Should you want to build and install ATF from the source tree provided
+here, follow the instructions in the [INSTALL file](INSTALL).
+
+## Support
+
+Please use the
+[atf-discuss mailing list](https://groups.google.com/forum/#!forum/atf-discuss)
+for any support inquiries related to `atf-c`, `atf-c++` or `atf-sh`.
+
+If you have any questions on Kyua proper, please use the
+[kyua-discuss mailing list](https://groups.google.com/forum/#!forum/kyua-discuss)
+instead.
diff --git a/contrib/atf/atf-c++/atf-c++.3 b/contrib/atf/atf-c++/atf-c++.3
index 601efaf6fd5b..fead776755af 100644
--- a/contrib/atf/atf-c++/atf-c++.3
+++ b/contrib/atf/atf-c++/atf-c++.3
@@ -403,8 +403,8 @@ in the collection.
 takes the name of an exception and a statement and raises a failure if
 the statement does not throw the specified exception.
 .Fn ATF_REQUIRE_THROW_RE
-takes the name of an exception, a regular expresion and a statement and raises a
-failure if the statement does not throw the specified exception and if the
+takes the name of an exception, a regular expression and a statement, and raises
+a failure if the statement does not throw the specified exception and if the
 message of the exception does not match the regular expression.
 .Pp
 .Fn ATF_CHECK_ERRNO
diff --git a/contrib/atf/atf-c++/detail/test_helpers.hpp b/contrib/atf/atf-c++/detail/test_helpers.hpp
index f166ee218a13..c1171801a3a7 100644
--- a/contrib/atf/atf-c++/detail/test_helpers.hpp
+++ b/contrib/atf/atf-c++/detail/test_helpers.hpp
@@ -36,6 +36,7 @@
 
 #include <atf-c++.hpp>
 
+#include <atf-c++/detail/env.hpp>
 #include <atf-c++/detail/process.hpp>
 
 #define HEADER_TC(name, hdrname) \
@@ -44,6 +45,8 @@
     { \
         set_md_var("descr", "Tests that the " hdrname " file can be " \
             "included on its own, without any prerequisites"); \
+        const std::string cxx = atf::env::get("ATF_BUILD_CXX", ATF_BUILD_CXX); \
+        set_md_var("require.progs", cxx); \
     } \
     ATF_TEST_CASE_BODY(name) \
     { \
@@ -55,6 +58,8 @@
     ATF_TEST_CASE_HEAD(name) \
     { \
         set_md_var("descr", descr); \
+        const std::string cxx = atf::env::get("ATF_BUILD_CXX", ATF_BUILD_CXX); \
+        set_md_var("require.progs", cxx); \
     } \
     ATF_TEST_CASE_BODY(name) \
     { \
diff --git a/contrib/atf/atf-c++/tests.hpp b/contrib/atf/atf-c++/tests.hpp
index ce2fb1d165c8..a03cc852dcf8 100644
--- a/contrib/atf/atf-c++/tests.hpp
+++ b/contrib/atf/atf-c++/tests.hpp
@@ -73,7 +73,7 @@ class tc {
     tc(const tc&);
     tc& operator=(const tc&);
 
-    std::auto_ptr< tc_impl > pimpl;
+    std::unique_ptr< tc_impl > pimpl;
 
 protected:
     virtual void head(void);
diff --git a/contrib/atf/atf-c++/utils.cpp b/contrib/atf/atf-c++/utils.cpp
index a6ab08f0d770..995d78c6542e 100644
--- a/contrib/atf/atf-c++/utils.cpp
+++ b/contrib/atf/atf-c++/utils.cpp
@@ -70,6 +70,13 @@ atf::utils::fork(void)
     return atf_utils_fork();
 }
 
+void
+atf::utils::reset_resultsfile(void)
+{
+
+    atf_utils_reset_resultsfile();
+}
+
 bool
 atf::utils::grep_file(const std::string& regex, const std::string& path)
 {
diff --git a/contrib/atf/atf-c++/utils.hpp b/contrib/atf/atf-c++/utils.hpp
index 8f5c5e337455..34d77a126df7 100644
--- a/contrib/atf/atf-c++/utils.hpp
+++ b/contrib/atf/atf-c++/utils.hpp
@@ -41,6 +41,7 @@ void copy_file(const std::string&, const std::string&);
 void create_file(const std::string&, const std::string&);
 bool file_exists(const std::string&);
 pid_t fork(void);
+void reset_resultsfile(void);
 bool grep_file(const std::string&, const std::string&);
 bool grep_string(const std::string&, const std::string&);
 void redirect(const int, const std::string&);
diff --git a/contrib/atf/atf-c++/utils_test.cpp b/contrib/atf/atf-c++/utils_test.cpp
index 34e0709f580a..93e16652bac1 100644
--- a/contrib/atf/atf-c++/utils_test.cpp
+++ b/contrib/atf/atf-c++/utils_test.cpp
@@ -335,6 +335,7 @@ fork_and_wait(const int exitstatus, const char* expout, const char* experr)
         std::cerr << "Some error\n";
         exit(123);
     }
+    atf::utils::reset_resultsfile();
     atf::utils::wait(pid, exitstatus, expout, experr);
     exit(EXIT_SUCCESS);
 }
diff --git a/contrib/atf/atf-c/.gitignore b/contrib/atf/atf-c/.gitignore
new file mode 100644
index 000000000000..e7f0fb647c32
--- /dev/null
+++ b/contrib/atf/atf-c/.gitignore
@@ -0,0 +1 @@
+defs.h
diff --git a/contrib/atf/atf-c/check.c b/contrib/atf/atf-c/check.c
index 38afdf3743a6..1aec01bcca6b 100644
--- a/contrib/atf/atf-c/check.c
+++ b/contrib/atf/atf-c/check.c
@@ -29,6 +29,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -106,7 +107,7 @@ static
 int
 const_execvp(const char *file, const char *const *argv)
 {
-#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
     return execvp(file, UNCONST(argv));
 #undef UNCONST
 }
diff --git a/contrib/atf/atf-c/detail/fs_test.c b/contrib/atf/atf-c/detail/fs_test.c
index 3dbc4d3ba7ef..7812be0334b8 100644
--- a/contrib/atf/atf-c/detail/fs_test.c
+++ b/contrib/atf/atf-c/detail/fs_test.c
@@ -779,7 +779,7 @@ ATF_TC_BODY(rmdir_enotempty, tc)
     atf_fs_path_fini(&p);
 }
 
-ATF_TC(rmdir_eperm);
+ATF_TC_WITH_CLEANUP(rmdir_eperm);
 ATF_TC_HEAD(rmdir_eperm, tc)
 {
     atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function");
@@ -808,6 +808,13 @@ ATF_TC_BODY(rmdir_eperm, tc)
 
     atf_fs_path_fini(&p);
 }
+ATF_TC_CLEANUP(rmdir_eperm, tc)
+{
+    if (chmod("test-dir", 0755) == -1) {
+        fprintf(stderr, "Failed to unprotect test-dir; test directory "
+                "cleanup will fail\n");
+    }
+}
 
 ATF_TC(mkdtemp_ok);
 ATF_TC_HEAD(mkdtemp_ok, tc)
diff --git a/contrib/atf/atf-c/detail/list.c b/contrib/atf/atf-c/detail/list.c
index d14216eb409f..7ac9f1fc948b 100644
--- a/contrib/atf/atf-c/detail/list.c
+++ b/contrib/atf/atf-c/detail/list.c
@@ -74,7 +74,7 @@ new_entry(void *object, bool managed)
         le->m_prev = le->m_next = NULL;
         le->m_object = object;
         le->m_managed = managed;
-    } else
+    } else if (managed)
         free(object);
 
     return le;
diff --git a/contrib/atf/atf-c/detail/process.c b/contrib/atf/atf-c/detail/process.c
index 8e08b6c57466..a6189bf78e20 100644
--- a/contrib/atf/atf-c/detail/process.c
+++ b/contrib/atf/atf-c/detail/process.c
@@ -30,6 +30,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -552,7 +553,7 @@ static
 int
 const_execvp(const char *file, const char *const *argv)
 {
-#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
     return execvp(file, UNCONST(argv));
 #undef UNCONST
 }
diff --git a/contrib/atf/atf-c/detail/test_helpers.h b/contrib/atf/atf-c/detail/test_helpers.h
index a601c293ffe4..90841f803c59 100644
--- a/contrib/atf/atf-c/detail/test_helpers.h
+++ b/contrib/atf/atf-c/detail/test_helpers.h
@@ -33,6 +33,7 @@
 
 #include <atf-c.h>
 
+#include <atf-c/detail/env.h>
 #include <atf-c/error_fwd.h>
 #include <atf-c/tc.h>
 
@@ -46,8 +47,11 @@ struct atf_fs_path;
     ATF_TC(name); \
     ATF_TC_HEAD(name, tc) \
     { \
+        const char *cc; \
         atf_tc_set_md_var(tc, "descr", "Tests that the " hdrname " file can " \
             "be included on its own, without any prerequisites"); \
+        cc = atf_env_get_with_default("ATF_BUILD_CC", ATF_BUILD_CC); \
+        atf_tc_set_md_var(tc, "require.progs", cc); \
     } \
     ATF_TC_BODY(name, tc) \
     { \
@@ -58,7 +62,10 @@ struct atf_fs_path;
     ATF_TC(name); \
     ATF_TC_HEAD(name, tc) \
     { \
+        const char *cc; \
         atf_tc_set_md_var(tc, "descr", descr); \
+        cc = atf_env_get_with_default("ATF_BUILD_CC", ATF_BUILD_CC); \
+        atf_tc_set_md_var(tc, "require.progs", cc); \
     } \
     ATF_TC_BODY(name, tc) \
     { \
diff --git a/contrib/atf/atf-c/tc.c b/contrib/atf/atf-c/tc.c
index 92c3e12c99b1..69b31123f3a3 100644
--- a/contrib/atf/atf-c/tc.c
+++ b/contrib/atf/atf-c/tc.c
@@ -33,6 +33,7 @@
 #include <fcntl.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -62,6 +63,7 @@ enum expect_type {
 struct context {
     const atf_tc_t *tc;
     const char *resfile;
+    int resfilefd;
     size_t fail_count;
 
     enum expect_type expect;
@@ -73,12 +75,14 @@ struct context {
 };
 
 static void context_init(struct context *, const atf_tc_t *, const char *);
+static void context_set_resfile(struct context *, const char *);
+static void context_close_resfile(struct context *);
 static void check_fatal_error(atf_error_t);
 static void report_fatal_error(const char *, ...)
     ATF_DEFS_ATTRIBUTE_NORETURN;
 static atf_error_t write_resfile(const int, const char *, const int,
                                  const atf_dynstr_t *);
-static void create_resfile(const char *, const char *, const int,
+static void create_resfile(struct context *, const char *, const int,
                            atf_dynstr_t *);
 static void error_in_expect(struct context *, const char *, ...)
     ATF_DEFS_ATTRIBUTE_NORETURN;
@@ -102,11 +106,16 @@ static void errno_test(struct context *, const char *, const size_t,
 static atf_error_t check_prog_in_dir(const char *, void *);
 static atf_error_t check_prog(struct context *, const char *);
 
+/* No prototype in header for this one, it's a little sketchy (internal). */
+void atf_tc_set_resultsfile(const char *);
+
 static void
 context_init(struct context *ctx, const atf_tc_t *tc, const char *resfile)
 {
+
     ctx->tc = tc;
-    ctx->resfile = resfile;
+    ctx->resfilefd = -1;
+    context_set_resfile(ctx, resfile);
     ctx->fail_count = 0;
     ctx->expect = EXPECT_PASS;
     check_fatal_error(atf_dynstr_init(&ctx->expect_reason));
@@ -116,6 +125,41 @@ context_init(struct context *ctx, const atf_tc_t *tc, const char *resfile)
     ctx->expect_signo = 0;
 }
 
+static void
+context_set_resfile(struct context *ctx, const char *resfile)
+{
+    atf_error_t err;
+
+    context_close_resfile(ctx);
+    ctx->resfile = resfile;
+    if (strcmp(resfile, "/dev/stdout") == 0)
+        ctx->resfilefd = STDOUT_FILENO;
+    else if (strcmp(resfile, "/dev/stderr") == 0)
+        ctx->resfilefd = STDERR_FILENO;
+    else
+        ctx->resfilefd = open(resfile, O_WRONLY | O_CREAT | O_TRUNC,
+            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    if (ctx->resfilefd == -1) {
+            err = atf_libc_error(errno,
+                "Cannot create results file '%s'", resfile);
+            check_fatal_error(err);
+    }
+
+    ctx->resfile = resfile;
+}
+
+static void
+context_close_resfile(struct context *ctx)
+{
+
+    if (ctx->resfilefd == -1)
+        return;
+    if (ctx->resfilefd != STDOUT_FILENO && ctx->resfilefd != STDERR_FILENO)
+        close(ctx->resfilefd);
+    ctx->resfilefd = -1;
+    ctx->resfile = NULL;
+}
+
 static void
 check_fatal_error(atf_error_t err)
 {
@@ -162,7 +206,7 @@ write_resfile(const int fd, const char *result, const int arg,
 
     INV(arg == -1 || reason != NULL);
 
-#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
     iov[count].iov_base = UNCONST(result);
     iov[count++].iov_len = strlen(result);
 
@@ -202,26 +246,23 @@ write_resfile(const int fd, const char *result, const int arg,
  * not return any error code.
  */
 static void
-create_resfile(const char *resfile, const char *result, const int arg,
+create_resfile(struct context *ctx, const char *result, const int arg,
                atf_dynstr_t *reason)
 {
     atf_error_t err;
 
-    if (strcmp("/dev/stdout", resfile) == 0) {
-        err = write_resfile(STDOUT_FILENO, result, arg, reason);
-    } else if (strcmp("/dev/stderr", resfile) == 0) {
-        err = write_resfile(STDERR_FILENO, result, arg, reason);
-    } else {
-        const int fd = open(resfile, O_WRONLY | O_CREAT | O_TRUNC,
-            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-        if (fd == -1) {
-            err = atf_libc_error(errno, "Cannot create results file '%s'",
-                                 resfile);
-        } else {
-            err = write_resfile(fd, result, arg, reason);
-            close(fd);
-        }
-    }
+    /*
+     * We'll attempt to truncate the results file, but only if it's not pointed
+     * at stdout/stderr.  We could just blindly ftruncate() here, but it may
+     * be that stdout/stderr have been redirected to a file that we want to
+     * validate expectations on, for example.  Kyua will want the truncation,
+     * but it will also redirect the results directly to some file and we'll
+     * have no issue here.
+     */
+    if (ctx->resfilefd != STDOUT_FILENO && ctx->resfilefd != STDERR_FILENO &&
+        ftruncate(ctx->resfilefd, 0) != -1)
+        lseek(ctx->resfilefd, 0, SEEK_SET);
+    err = write_resfile(ctx->resfilefd, result, arg, reason);
 
     if (reason != NULL)
         atf_dynstr_fini(reason);
@@ -280,7 +321,8 @@ expected_failure(struct context *ctx, atf_dynstr_t *reason)
 {
     check_fatal_error(atf_dynstr_prepend_fmt(reason, "%s: ",
         atf_dynstr_cstring(&ctx->expect_reason)));
-    create_resfile(ctx->resfile, "expected_failure", -1, reason);
+    create_resfile(ctx, "expected_failure", -1, reason);
+    context_close_resfile(ctx);
     exit(EXIT_SUCCESS);
 }
 
@@ -290,7 +332,8 @@ fail_requirement(struct context *ctx, atf_dynstr_t *reason)
     if (ctx->expect == EXPECT_FAIL) {
         expected_failure(ctx, reason);
     } else if (ctx->expect == EXPECT_PASS) {
-        create_resfile(ctx->resfile, "failed", -1, reason);
+        create_resfile(ctx, "failed", -1, reason);
+        context_close_resfile(ctx);
         exit(EXIT_FAILURE);
     } else {
         error_in_expect(ctx, "Test case raised a failure but was not "
@@ -325,7 +368,8 @@ pass(struct context *ctx)
         error_in_expect(ctx, "Test case was expecting a failure but got "
             "a pass instead");
     } else if (ctx->expect == EXPECT_PASS) {
-        create_resfile(ctx->resfile, "passed", -1, NULL);
+        create_resfile(ctx, "passed", -1, NULL);
+        context_close_resfile(ctx);
         exit(EXIT_SUCCESS);
     } else {
         error_in_expect(ctx, "Test case asked to explicitly pass but was "
@@ -338,7 +382,8 @@ static void
 skip(struct context *ctx, atf_dynstr_t *reason)
 {
     if (ctx->expect == EXPECT_PASS) {
-        create_resfile(ctx->resfile, "skipped", -1, reason);
+        create_resfile(ctx, "skipped", -1, reason);
+        context_close_resfile(ctx);
         exit(EXIT_SUCCESS);
     } else {
         error_in_expect(ctx, "Can only skip a test case when running in "
@@ -942,7 +987,7 @@ _atf_tc_expect_exit(struct context *ctx, const int exitcode, const char *reason,
     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
     va_end(ap2);
 
-    create_resfile(ctx->resfile, "expected_exit", exitcode, &formatted);
+    create_resfile(ctx, "expected_exit", exitcode, &formatted);
 }
 
 static void
@@ -959,7 +1004,7 @@ _atf_tc_expect_signal(struct context *ctx, const int signo, const char *reason,
     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
     va_end(ap2);
 
-    create_resfile(ctx->resfile, "expected_signal", signo, &formatted);
+    create_resfile(ctx, "expected_signal", signo, &formatted);
 }
 
 static void
@@ -975,7 +1020,7 @@ _atf_tc_expect_death(struct context *ctx, const char *reason, va_list ap)
     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
     va_end(ap2);
 
-    create_resfile(ctx->resfile, "expected_death", -1, &formatted);
+    create_resfile(ctx, "expected_death", -1, &formatted);
 }
 
 static void
@@ -991,7 +1036,14 @@ _atf_tc_expect_timeout(struct context *ctx, const char *reason, va_list ap)
     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
     va_end(ap2);
 
-    create_resfile(ctx->resfile, "expected_timeout", -1, &formatted);
+    create_resfile(ctx, "expected_timeout", -1, &formatted);
+}
+
+static void
+_atf_tc_set_resultsfile(struct context *ctx, const char *file)
+{
+
+    context_set_resfile(ctx, file);
 }
 
 /* ---------------------------------------------------------------------
@@ -1215,3 +1267,13 @@ atf_tc_expect_timeout(const char *reason, ...)
     _atf_tc_expect_timeout(&Current, reason, ap);
     va_end(ap);
 }
+
+/* Internal! */
+void
+atf_tc_set_resultsfile(const char *file)
+{
+
+    PRE(Current.tc != NULL);
+
+    _atf_tc_set_resultsfile(&Current, file);
+}
diff --git a/contrib/atf/atf-c/utils.c b/contrib/atf/atf-c/utils.c
index 1e2aac1ed3b6..d8355bc68936 100644
--- a/contrib/atf/atf-c/utils.c
+++ b/contrib/atf/atf-c/utils.c
@@ -41,6 +41,9 @@
 
 #include "atf-c/detail/dynstr.h"
 
+/* No prototype in header for this one, it's a little sketchy (internal). */
+void atf_tc_set_resultsfile(const char *);
+
 /** Allocate a filename to be used by atf_utils_{fork,wait}.
  *
  * In case of a failure, marks the calling test as failed when in_parent is
@@ -271,6 +274,13 @@ atf_utils_fork(void)
     return pid;
 }
 
+void
+atf_utils_reset_resultsfile(void)
+{
+
+    atf_tc_set_resultsfile("/dev/null");
+}
+
 /** Frees an dynamically-allocated "argv" array.
  *
  * \param argv A dynamically-allocated array of dynamically-allocated
diff --git a/contrib/atf/atf-c/utils.h b/contrib/atf/atf-c/utils.h
index e4162b215fe5..422186a31e76 100644
--- a/contrib/atf/atf-c/utils.h
+++ b/contrib/atf/atf-c/utils.h
@@ -46,5 +46,6 @@ bool atf_utils_grep_string(const char *, const char *, ...)
 char *atf_utils_readline(int);
 void atf_utils_redirect(const int, const char *);
 void atf_utils_wait(const pid_t, const int, const char *, const char *);
+void atf_utils_reset_resultsfile(void);
 
 #endif /* !defined(ATF_C_UTILS_H) */
diff --git a/contrib/atf/atf-c/utils_test.c b/contrib/atf/atf-c/utils_test.c
index fb81cd3a1d9e..9d8f69683b9a 100644
--- a/contrib/atf/atf-c/utils_test.c
+++ b/contrib/atf/atf-c/utils_test.c
@@ -395,6 +395,7 @@ fork_and_wait(const int exitstatus, const char* expout, const char* experr)
         fprintf(stderr, "Some error\n");
         exit(123);
     }
+    atf_utils_reset_resultsfile();
     atf_utils_wait(pid, exitstatus, expout, experr);
     exit(EXIT_SUCCESS);
 }
diff --git a/contrib/atf/atf-sh/.gitignore b/contrib/atf/atf-sh/.gitignore
new file mode 100644
index 000000000000..a29438f1a9b8
--- /dev/null
+++ b/contrib/atf/atf-sh/.gitignore
@@ -0,0 +1,2 @@
+atf-check
+atf-sh
diff --git a/contrib/atf/atf-sh/atf-check.1 b/contrib/atf/atf-sh/atf-check.1
index a423e3ac3b1c..b03058e8442c 100644
--- a/contrib/atf/atf-sh/atf-check.1
+++ b/contrib/atf/atf-sh/atf-check.1
@@ -22,7 +22,7 @@
 .\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.Dd March 6, 2017
+.Dd June 21, 2020
 .Dt ATF-CHECK 1
 .Os
 .Sh NAME
@@ -120,6 +120,14 @@ as a shell command line, executing it with the system shell defined by
 .Va ATF_SHELL .
 You should avoid using this flag if at all possible to prevent shell quoting
 issues.
+.It Fl r Ar timeout[:interval]
+Repeats failed checks until the
+.Ar timeout
+(in seconds) expires.
+If unspecified, the default
+.Ar interval
+(in milliseconds) is 50 ms.
+This can be used to wait for an expected update to the contents of a file.
 .El
 .Sh ENVIRONMENT
 .Bl -tag -width ATFXSHELLXX -compact
@@ -157,6 +165,11 @@ atf_check -s signal:sigsegv my_program
 
 # Combined checks
 atf_check -o match:foo -o not-match:bar echo foo baz
+
+# Wait 5 seconds for a line to show up in a file
+( sleep 2 ; echo "testing 123" > $test_path ) &
+atf-check -o ignore -e ignore -s exit:0 -r 5 \e
+    grep "testing 123" $test_path
 .Ed
 .Sh SEE ALSO
 .Xr atf-sh 1
diff --git a/contrib/atf/atf-sh/atf-check.cpp b/contrib/atf/atf-sh/atf-check.cpp
index 414b64ea91f0..38ab527aab54 100644
--- a/contrib/atf/atf-sh/atf-check.cpp
+++ b/contrib/atf/atf-sh/atf-check.cpp
@@ -29,6 +29,7 @@ extern "C" {
 
 #include <limits.h>
 #include <signal.h>
+#include <stdint.h>
 #include <unistd.h>
 }
 
@@ -53,6 +54,10 @@ extern "C" {
 #include "atf-c++/detail/sanity.hpp"
 #include "atf-c++/detail/text.hpp"
 
+static const useconds_t seconds_in_useconds = (1000 * 1000);
+static const useconds_t mseconds_in_useconds = 1000;
+static const useconds_t useconds_in_nseconds = 1000;
+
 // ------------------------------------------------------------------------
 // Auxiliary functions.
 // ------------------------------------------------------------------------
@@ -162,6 +167,33 @@ public:
 
 } // anonymous namespace
 
+static useconds_t
+get_monotonic_useconds(void)
+{
+    struct timespec ts;
+    useconds_t res;
+    int rc;
+
+    rc = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (rc != 0)
+        throw std::runtime_error("clock_gettime: " +
+            std::string(strerror(errno)));
+
+    res = ts.tv_sec * seconds_in_useconds;
+    res += ts.tv_nsec / useconds_in_nseconds;
+    return res;
+}
+
+static bool
+timo_expired(useconds_t timeout)
+{
+
+    if (get_monotonic_useconds() >= timeout)
+        return true;
+    return false;
+}
+
+
 static int
 parse_exit_code(const std::string& str)
 {
@@ -216,7 +248,7 @@ parse_signal(const std::string& str)
     if (signo == INT_MIN) {
         try {
             return atf::text::to_type< int >(str);
-        } catch (std::runtime_error) {
+        } catch (const std::runtime_error&) {
             throw atf::application::usage_error("Invalid signal name or number "
                 "in -s option");
         }
@@ -306,6 +338,62 @@ parse_output_check_arg(const std::string& arg)
     return output_check(type, negated, arg.substr(delimiter + 1));
 }
 
+static void
+parse_repeat_check_arg(const std::string& arg, useconds_t *m_timo,
+    useconds_t *m_interval)
+{
+    const std::string::size_type delimiter = arg.find(':');
+    const bool has_interval = (delimiter != std::string::npos);
+    const std::string timo_str = arg.substr(0, delimiter);
+
+    long l;
+    char *end;
+
+    // There is no reason this couldn't be a non-integer number of seconds,
+    // this was just easy to do for now.
+    errno = 0;
+    l = strtol(timo_str.c_str(), &end, 10);
+    if (errno == ERANGE)
+        throw atf::application::usage_error("Bogus timeout in seconds");
+    else if (errno != 0)
+        throw atf::application::usage_error("Timeout must be a number");
+
+    if (*end != 0)
+        throw atf::application::usage_error("Timeout must be a number");
+
+    *m_timo = get_monotonic_useconds() + (l * seconds_in_useconds);
+    // 50 milliseconds is chosen arbitrarily.  There is a tradeoff between
+    // longer and shorter poll times.  A shorter poll time makes for faster
+    // tests.  A longer poll time makes for lower CPU overhead for the polled
+    // operation.  50ms is chosen with these tradeoffs in mind: on
+    // microcontrollers, the hope is that we can still avoid meaningful CPU use
+    // with a small test every 50ms.  And on typical fast x86 hardware, our
+    // tests can be much more precise with time wasted than they typically are
+    // without this feature.
+    *m_interval = 50 * mseconds_in_useconds;
+
+    if (!has_interval)
+        return;
+
+    const std::string intv_str = arg.substr(delimiter + 1, std::string::npos);
+
+    // Same -- this could be non-integer milliseconds.
+    errno = 0;
+    l = strtol(intv_str.c_str(), &end, 10);
+    if (errno == ERANGE)
+        throw atf::application::usage_error(
+            "Bogus repeat interval in milliseconds");
+    else if (errno != 0)
+        throw atf::application::usage_error(
+            "Repeat interval must be a number");
+
+    if (*end != 0)
+        throw atf::application::usage_error(
+            "Repeat interval must be a number");
+
+    *m_interval = l * mseconds_in_useconds;
+}
+
 static
 std::string
 flatten_argv(char* const* argv)
@@ -694,8 +782,12 @@ run_output_checks(const std::vector< output_check >& checks,
 namespace {
 
 class atf_check : public atf::application::app {
+    bool m_rflag;
     bool m_xflag;
 
+    useconds_t m_timo;
+    useconds_t m_interval;
+
     std::vector< status_check > m_status_checks;
     std::vector< output_check > m_stdout_checks;
     std::vector< output_check > m_stderr_checks;
@@ -722,6 +814,7 @@ const char* atf_check::m_description =
 
 atf_check::atf_check(void) :
     app(m_description, "atf-check(1)"),
+    m_rflag(false),
     m_xflag(false)
 {
 }
@@ -765,6 +858,8 @@ atf_check::specific_options(void)
     opts.insert(option('e', "action:arg", "Handle stderr. Action must be "
                 "one of: empty ignore file:<path> inline:<val> match:regexp "
                 "save:<path>"));
+    opts.insert(option('r', "timeout[:interval]", "Repeat failed check until "
+                "the timeout expires."));
     opts.insert(option('x', "", "Execute command as a shell command"));
 
     return opts;
@@ -786,6 +881,11 @@ atf_check::process_option(int ch, const char* arg)
         m_stderr_checks.push_back(parse_output_check_arg(arg));
         break;
 
+    case 'r':
+        m_rflag = true;
+        parse_repeat_check_arg(arg, &m_timo, &m_interval);
+        break;
+
     case 'x':
         m_xflag = true;
         break;
@@ -803,9 +903,6 @@ atf_check::main(void)
 
     int status = EXIT_FAILURE;
 
-    std::auto_ptr< atf::check::check_result > r =
-        m_xflag ? execute_with_shell(m_argv) : execute(m_argv);
-
     if (m_status_checks.empty())
         m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS));
     else if (m_status_checks.size() > 1) {
@@ -818,12 +915,23 @@ atf_check::main(void)
     if (m_stderr_checks.empty())
         m_stderr_checks.push_back(output_check(oc_empty, false, ""));
 
-    if ((run_status_checks(m_status_checks, *r) == false) ||
-        (run_output_checks(*r, "stderr") == false) ||
-        (run_output_checks(*r, "stdout") == false))
-        status = EXIT_FAILURE;
-    else
-        status = EXIT_SUCCESS;
+    do {
+        std::auto_ptr< atf::check::check_result > r =
+            m_xflag ? execute_with_shell(m_argv) : execute(m_argv);
+
*** 271 LINES SKIPPED ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202103171405.12HE5HQN085994>