krb5 commit: Add C profile test program to replace Tcl tests

Greg Hudson ghudson at mit.edu
Thu Jun 3 15:10:56 EDT 2021


https://github.com/krb5/krb5/commit/ea99c4dda830c700f2fd81a89b90ea50d2591ee4
commit ea99c4dda830c700f2fd81a89b90ea50d2591ee4
Author: Greg Hudson <ghudson at mit.edu>
Date:   Wed May 26 01:30:14 2021 -0400

    Add C profile test program to replace Tcl tests

 src/util/profile/Makefile.in |   15 ++-
 src/util/profile/t_profile.c |  389 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 400 insertions(+), 4 deletions(-)

diff --git a/src/util/profile/Makefile.in b/src/util/profile/Makefile.in
index 6ee264a..30f1695 100644
--- a/src/util/profile/Makefile.in
+++ b/src/util/profile/Makefile.in
@@ -38,7 +38,7 @@ SRCS = $(srcdir)/prof_tree.c \
 
 EXTRADEPSRCS=$(srcdir)/test_load.c $(srcdir)/test_parse.c \
 	$(srcdir)/test_profile.c $(srcdir)/test_vtable.c \
-	$(srcdir)/profile_tcl.c
+	$(srcdir)/profile_tcl.c $(srcdir)/t_profile.c
 
 DEPLIBS = $(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB)
 MLIBS = $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS)
@@ -65,7 +65,7 @@ $(PROFILE_HDR): profile.h
 includes: $(PROFILE_HDR)
 
 clean-unix::
-	$(RM) $(BUILDTOP)/include/profile.h test2.ini test3.ini test2.ini.bak
+	$(RM) $(BUILDTOP)/include/profile.h
 
 ##DOS##LIBOBJS = $(OBJS)
 
@@ -89,6 +89,9 @@ test_vtable: test_vtable.$(OBJEXT) $(OBJS) $(DEPLIBS)
 test_load: test_load.$(OBJEXT) $(OBJS) $(DEPLIBS)
 	$(CC_LINK) -o test_load test_load.$(OBJEXT) $(OBJS) $(MLIBS)
 
+t_profile: t_profile.$(OBJEXT) $(OBJS) $(DEPLIBS)
+	$(CC_LINK) -o $@ t_profile.$(OBJEXT) $(OBJS) $(MLIBS)
+
 modtest.conf:
 	echo "module `pwd`/testmod/proftest$(DYNOBJEXT):teststring" > $@
 
@@ -129,15 +132,19 @@ profile_tcl: profile_tcl.o $(PROF_DEPLIB) $(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB)
 clean-unix:: clean-libs clean-libobjs
 	$(RM) $(PROGS) *.o *~ core prof_err.h profile.h prof_err.c
 	$(RM) test_load test_parse test_profile test_vtable profile_tcl
-	$(RM) modtest.conf testinc.ini testinc2.ini final.out
+	$(RM) t_profile modtest.conf testinc.ini testinc2.ini final.out
+	$(RM) test2* test3*
 	$(RM) -r test_include_dir
 
 clean-windows::
 	$(RM) $(PROFILE_HDR)
 
-check-unix: test_parse test_profile test_vtable test_load modtest.conf
+check-unix: test_parse test_profile modtest.conf
+check-unix: test_vtable test_load t_profile
 	$(RUN_TEST) ./test_vtable
 	$(RUN_TEST) ./test_load
+	cp $(srcdir)/test.ini test2.ini
+	$(RUN_TEST) ./t_profile
 
 DO_TCL=@DO_TCL@
 check-unix: check-unix-final check-unix-tcl-$(DO_TCL)
diff --git a/src/util/profile/t_profile.c b/src/util/profile/t_profile.c
new file mode 100644
index 0000000..b0e715b
--- /dev/null
+++ b/src/util/profile/t_profile.c
@@ -0,0 +1,389 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/profile/t_profile.c - profile library regression tests */
+/*
+ * Copyright (C) 2021 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 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.
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include "profile.h"
+
+static void
+check(long code)
+{
+    assert(code == 0);
+}
+
+static void
+check_fail(long code, long expected)
+{
+    assert(code == expected);
+}
+
+static void
+write_file(const char *name, int nlines, ...)
+{
+    FILE *f;
+    va_list ap;
+    int i;
+
+    (void)unlink(name);
+    f = fopen(name, "w");
+    assert(f != NULL);
+    va_start(ap, nlines);
+    for (i = 0; i < nlines; i++)
+        fprintf(f, "%s\n", va_arg(ap, char *));
+    va_end(ap);
+    fclose(f);
+}
+
+/* Regression test for #2685 (profile iterator breaks when modifications
+ * made) */
+static void
+test_iterate()
+{
+    profile_t p;
+    void *iter;
+    const char *names[] = { "test section 1", "child_section", "child", NULL };
+    const char *values[] = { "slick", "harry", "john", NULL };
+    char *name, *value;
+    int i;
+
+    check(profile_init_path("test2.ini", &p));
+
+    /* Iterate and check for the expected values. */
+    check(profile_iterator_create(p, names, 0, &iter));
+    for (i = 0;; i++) {
+        check(profile_iterator(&iter, &name, &value));
+        if (name == NULL && value == NULL)
+            break;
+        assert(strcmp(name, names[2]) == 0);
+        assert(values[i] != NULL);
+        assert(strcmp(value, values[i]) == 0);
+        profile_release_string(name);
+        profile_release_string(value);
+    }
+    assert(values[i] == NULL);
+    profile_iterator_free(&iter);
+
+    /* Iterate again, deleting each value as we go.  Flush the result to a
+     * separate file. */
+    check(profile_iterator_create(p, names, 0, &iter));
+    for (;;) {
+        check(profile_iterator(&iter, NULL, &value));
+        if (value == NULL)
+            break;
+        check(profile_update_relation(p, names, value, NULL));
+        profile_release_string(value);
+    }
+    profile_iterator_free(&iter);
+    (void)unlink("test3.ini");
+    profile_flush_to_file(p, "test3.ini");
+
+    profile_abandon(p);
+
+    /* Check that no values for the section are found in the resulting file. */
+    check(profile_init_path("test3.ini", &p));
+    check(profile_iterator_create(p, names, 0, &iter));
+    check(profile_iterator(&iter, &name, &value));
+    assert(name == NULL && value == NULL);
+    profile_iterator_free(&iter);
+    profile_abandon(p);
+}
+
+/*
+ * Regression test for a 1.4-era bug where updating the underlying file data of
+ * a profile object lost track of the flag indicating that it was part of the
+ * global shared profiles list.
+ */
+static void
+test_shared()
+{
+    profile_t a, b;
+    struct utimbuf times;
+
+    system("cp test2.ini test3.ini");
+
+    /* Create an entry in the shared table. */
+    check(profile_init_path("test3.ini", &a));
+
+    /*
+     * Force an update of the underlying data.  With the bug present, the
+     * shared flag is erroneously cleared.  The easiest way to force an update
+     * is to reopen the file (since we don't enforce the one-stat-per-second
+     * limit during open) after changing the timestamp.
+     */
+    times.actime = time(NULL) + 2;
+    times.modtime = times.actime;
+    utime("test3.ini", &times);
+    check(profile_init_path("test3.ini", &b));
+    profile_release(b);
+
+    /* Release the profile.  With the bug present, a dangling reference is left
+     * behind in the shared table. */
+    profile_release(a);
+
+    /* Open the profile again to dereference the dangling pointer if one was
+     * created. */
+    check(profile_init_path("test3.ini", &a));
+    profile_release(a);
+}
+
+/* Regression test for #2950 (profile_clear_relation not reflected within
+ * handle where deletion is performed) */
+static void
+test_clear()
+{
+    profile_t p;
+    const char *names[] = { "test section 1", "quux", NULL };
+    char **values, **dummy;
+
+    check(profile_init_path("test2.ini", &p));
+    check(profile_get_values(p, names, &values));
+    check(profile_clear_relation(p, names));
+    check_fail(profile_get_values(p, names, &dummy), PROF_NO_RELATION);
+    check(profile_add_relation(p, names, values[0]));
+    profile_free_list(values);
+    check(profile_get_values(p, names, &values));
+    assert(values[0] != NULL && values[1] == NULL);
+    profile_free_list(values);
+    profile_abandon(p);
+}
+
+static void
+test_include()
+{
+    profile_t p;
+    const char *names[] = { "test section 1", "bar", NULL };
+    char **values;
+
+    /* Test expected error code when including nonexistent file. */
+    write_file("testinc.ini", 1, "include does-not-exist");
+    check_fail(profile_init_path("testinc.ini", &p), PROF_FAIL_INCLUDE_FILE);
+
+    /* Test expected error code when including nonexistent directory. */
+    write_file("testinc.ini", 1, "includedir does-not-exist");
+    check_fail(profile_init_path("testinc.ini", &p), PROF_FAIL_INCLUDE_DIR);
+
+    /* Test including a file. */
+    write_file("testinc.ini", 1, "include test2.ini");
+    check(profile_init_path("testinc.ini", &p));
+    check(profile_get_values(p, names, &values));
+    assert(strcmp(values[0], "foo") == 0 && values[1] == NULL);
+    profile_free_list(values);
+    profile_release(p);
+
+    /*
+     * Test including a directory.  Put four copies of test2.ini inside the
+     * directory, two with invalid names.  Check that we get two values for one
+     * of the variables.
+     */
+    system("rm -rf test_include_dir");
+    system("mkdir test_include_dir");
+    system("cp test2.ini test_include_dir/a");
+    system("cp test2.ini test_include_dir/a~");
+    system("cp test2.ini test_include_dir/b.conf");
+    system("cp test2.ini test_include_dir/b.conf.rpmsave");
+    write_file("testinc.ini", 1, "includedir test_include_dir");
+    check(profile_init_path("testinc.ini", &p));
+    check(profile_get_values(p, names, &values));
+    assert(strcmp(values[0], "foo") == 0);
+    assert(strcmp(values[1], "foo") == 0);
+    assert(values[2] == NULL);
+    profile_free_list(values);
+    profile_release(p);
+
+    /* Directly list the directory in the profile path and try again. */
+    check(profile_init_path("test_include_dir", &p));
+    check(profile_get_values(p, names, &values));
+    assert(strcmp(values[0], "foo") == 0);
+    assert(strcmp(values[1], "foo") == 0);
+    assert(values[2] == NULL);
+    profile_free_list(values);
+    profile_release(p);
+}
+
+/* Test syntactic independence of included profile files. */
+static void
+test_independence()
+{
+    profile_t p;
+    const char *names1[] = { "sec1", "var", "a", NULL };
+    const char *names2[] = { "sec2", "b", NULL };
+    const char *names3[] = { "sec1", "var", "c", NULL };
+    char **values;
+
+    write_file("testinc.ini", 6, "[sec1]", "var = {", "a = 1",
+               "include testinc2.ini", "c = 3", "}");
+    write_file("testinc2.ini", 2, "[sec2]", "b = 2");
+
+    check(profile_init_path("testinc.ini", &p));
+    check(profile_get_values(p, names1, &values));
+    assert(strcmp(values[0], "1") == 0 && values[1] == NULL);
+    profile_free_list(values);
+    check(profile_get_values(p, names2, &values));
+    assert(strcmp(values[0], "2") == 0 && values[1] == NULL);
+    profile_free_list(values);
+    check(profile_get_values(p, names3, &values));
+    assert(strcmp(values[0], "3") == 0 && values[1] == NULL);
+    profile_free_list(values);
+    profile_release(p);
+}
+
+/* Regression test for #7971 (deleted sections should not be iterable) */
+static void
+test_delete_section()
+{
+    profile_t p;
+    const char *sect[] = { "test section 1", NULL };
+    const char *newrel[] = { "test section 1", "testkey", NULL };
+    const char *oldrel[] = { "test section 1", "child", NULL };
+    char **values;
+
+    check(profile_init_path("test2.ini", &p));
+
+    /* Remove and replace a section. */
+    check(profile_rename_section(p, sect, NULL));
+    check(profile_add_relation(p, sect, NULL));
+    check(profile_add_relation(p, newrel, "6"));
+
+    /* Check that we can read the new relation but not the old one. */
+    check(profile_get_values(p, newrel, &values));
+    assert(strcmp(values[0], "6") == 0 && values[1] == NULL);
+    profile_free_list(values);
+    check_fail(profile_get_values(p, oldrel, &values), PROF_NO_RELATION);
+    profile_abandon(p);
+}
+
+/* Regression test for #7971 (profile_clear_relation() error with deleted node
+ * at end of value set) */
+static void
+test_delete_clear_relation()
+{
+    profile_t p;
+    const char *names[] = { "test section 1", "testkey", NULL };
+
+    check(profile_init_path("test2.ini", &p));
+    check(profile_add_relation(p, names, "1"));
+    check(profile_add_relation(p, names, "2"));
+    check(profile_update_relation(p, names, "2", NULL));
+    check(profile_clear_relation(p, names));
+    profile_abandon(p);
+}
+
+/* Test that order of relations is preserved if some relations are deleted. */
+static void
+test_delete_ordering()
+{
+    profile_t p;
+    const char *names[] = { "test section 1", "testkey", NULL };
+    char **values;
+
+    check(profile_init_path("test2.ini", &p));
+    check(profile_add_relation(p, names, "1"));
+    check(profile_add_relation(p, names, "2"));
+    check(profile_add_relation(p, names, "3"));
+    check(profile_update_relation(p, names, "2", NULL));
+    check(profile_add_relation(p, names, "4"));
+    check(profile_get_values(p, names, &values));
+    assert(strcmp(values[0], "1") == 0);
+    assert(strcmp(values[1], "3") == 0);
+    assert(strcmp(values[2], "4") == 0);
+    assert(values[3] == NULL);
+    profile_free_list(values);
+    profile_abandon(p);
+}
+
+/* Regression test for #8431 (profile_flush_to_file erroneously changes flag
+ * state on source object) */
+static void
+test_flush_to_file()
+{
+    profile_t p;
+
+    /* Flush a profile object to a file without making any changes, so that the
+     * source object is still within g_shared_trees. */
+    check(profile_init_path("test2.ini", &p));
+    unlink("test3.ini");
+    check(profile_flush_to_file(p, "test3.ini"));
+    profile_release(p);
+
+    /* Check for a dangling reference in g_shared_trees by creating another
+     * profile object. */
+    profile_init_path("test2.ini", &p);
+    profile_release(p);
+}
+
+/* Regression test for #7863 (multiply-specified subsections should
+ * be merged) */
+static void
+test_merge_subsections()
+{
+    profile_t p;
+    const char *n1[] = { "test section 2", "child_section2", "child", NULL };
+    const char *n2[] = { "test section 2", "child_section2", "chores", NULL };
+    char **values;
+
+    check(profile_init_path("test2.ini", &p));
+
+    check(profile_get_values(p, n1, &values));
+    assert(strcmp(values[0], "slick") == 0);
+    assert(strcmp(values[1], "harry") == 0);
+    assert(strcmp(values[2], "john\tb ") == 0);
+    assert(strcmp(values[3], "ron") == 0);
+    assert(values[4] == NULL);
+    profile_free_list(values);
+
+    check(profile_get_values(p, n2, &values));
+    assert(strcmp(values[0], "cleaning") == 0 && values[1] == NULL);
+    profile_free_list(values);
+
+    profile_release(p);
+}
+
+int
+main()
+{
+    test_iterate();
+    test_shared();
+    test_clear();
+    test_include();
+    test_independence();
+    test_delete_section();
+    test_delete_clear_relation();
+    test_delete_ordering();
+    test_flush_to_file();
+    test_merge_subsections();
+}


More information about the cvs-krb5 mailing list