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

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/ea99c4dda830c700f2fd81a89b90ea50d2591ee4
commit ea99c4dda830c700f2fd81a89b90ea50d2591ee4
Author: Greg Hudson <[email protected]>
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();
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.