github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/cmd/libsnap-confine-private/infofile-test.c (about)

     1  /*
     2   * Copyright (C) 2019 Canonical Ltd
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License version 3 as
     6   * published by the Free Software Foundation.
     7   *
     8   * This program is distributed in the hope that it will be useful,
     9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11   * GNU General Public License for more details.
    12   *
    13   * You should have received a copy of the GNU General Public License
    14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15   *
    16   */
    17  
    18  #include "infofile.h"
    19  
    20  #include <glib.h>
    21  #include <unistd.h>
    22  
    23  #include "infofile.c"
    24  
    25  static void test_infofile_get_key(void) {
    26      int rc;
    27      sc_error *err;
    28  
    29      char text[] =
    30          "key=value\n"
    31          "other-key=other-value\n"
    32          "dup-key=value-one\n"
    33          "dup-key=value-two\n";
    34      FILE *stream = fmemopen(text, sizeof text - 1, "r");
    35      g_assert_nonnull(stream);
    36  
    37      char *value;
    38  
    39      /* Caller must provide the stream to scan. */
    40      rc = sc_infofile_get_key(NULL, "key", &value, &err);
    41      g_assert_cmpint(rc, ==, -1);
    42      g_assert_nonnull(err);
    43      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
    44      g_assert_cmpint(sc_error_code(err), ==, SC_API_MISUSE);
    45      g_assert_cmpstr(sc_error_msg(err), ==, "stream cannot be NULL");
    46      sc_error_free(err);
    47  
    48      /* Caller must provide the key to look for. */
    49      rc = sc_infofile_get_key(stream, NULL, &value, &err);
    50      g_assert_cmpint(rc, ==, -1);
    51      g_assert_nonnull(err);
    52      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
    53      g_assert_cmpint(sc_error_code(err), ==, SC_API_MISUSE);
    54      g_assert_cmpstr(sc_error_msg(err), ==, "key cannot be NULL");
    55      sc_error_free(err);
    56  
    57      /* Caller must provide storage for the value. */
    58      rc = sc_infofile_get_key(stream, "key", NULL, &err);
    59      g_assert_cmpint(rc, ==, -1);
    60      g_assert_nonnull(err);
    61      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
    62      g_assert_cmpint(sc_error_code(err), ==, SC_API_MISUSE);
    63      g_assert_cmpstr(sc_error_msg(err), ==, "value cannot be NULL");
    64      sc_error_free(err);
    65  
    66      /* Keys that are not found get NULL values. */
    67      value = (void *)0xfefefefe;
    68      rewind(stream);
    69      rc = sc_infofile_get_key(stream, "missing-key", &value, &err);
    70      g_assert_cmpint(rc, ==, 0);
    71      g_assert_null(err);
    72      g_assert_null(value);
    73  
    74      /* Keys that are found get strdup-duplicated values. */
    75      value = NULL;
    76      rewind(stream);
    77      rc = sc_infofile_get_key(stream, "key", &value, &err);
    78      g_assert_cmpint(rc, ==, 0);
    79      g_assert_null(err);
    80      g_assert_nonnull(value);
    81      g_assert_cmpstr(value, ==, "value");
    82      free(value);
    83  
    84      /* When duplicate keys are present the first value is extracted. */
    85      char *dup_value;
    86      rewind(stream);
    87      rc = sc_infofile_get_key(stream, "dup-key", &dup_value, &err);
    88      g_assert_cmpint(rc, ==, 0);
    89      g_assert_null(err);
    90      g_assert_nonnull(dup_value);
    91      g_assert_cmpstr(dup_value, ==, "value-one");
    92      free(dup_value);
    93  
    94      fclose(stream);
    95  
    96      /* Key without a value. */
    97      char *tricky_value;
    98      char tricky1[] = "key\n";
    99      stream = fmemopen(tricky1, sizeof tricky1 - 1, "r");
   100      g_assert_nonnull(stream);
   101      rc = sc_infofile_get_key(stream, "key", &tricky_value, &err);
   102      g_assert_cmpint(rc, ==, -1);
   103      g_assert_nonnull(err);
   104      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
   105      g_assert_cmpint(sc_error_code(err), ==, 0);
   106      g_assert_cmpstr(sc_error_msg(err), ==, "line 1 is not a key=value assignment");
   107      g_assert_null(tricky_value);
   108      sc_error_free(err);
   109      fclose(stream);
   110  
   111      /* Key-value pair with embedded NUL byte. */
   112      char tricky2[] = "key=value\0garbage\n";
   113      stream = fmemopen(tricky2, sizeof tricky2 - 1, "r");
   114      g_assert_nonnull(stream);
   115      rc = sc_infofile_get_key(stream, "key", &tricky_value, &err);
   116      g_assert_cmpint(rc, ==, -1);
   117      g_assert_nonnull(err);
   118      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
   119      g_assert_cmpint(sc_error_code(err), ==, 0);
   120      g_assert_cmpstr(sc_error_msg(err), ==, "line 1 contains NUL byte");
   121      g_assert_null(tricky_value);
   122      sc_error_free(err);
   123      fclose(stream);
   124  
   125      /* Key with empty value but without trailing newline. */
   126      char tricky3[] = "key=";
   127      stream = fmemopen(tricky3, sizeof tricky3 - 1, "r");
   128      g_assert_nonnull(stream);
   129      rc = sc_infofile_get_key(stream, "key", &tricky_value, &err);
   130      g_assert_cmpint(rc, ==, -1);
   131      g_assert_nonnull(err);
   132      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
   133      g_assert_cmpint(sc_error_code(err), ==, 0);
   134      g_assert_cmpstr(sc_error_msg(err), ==, "line 1 does not end with a newline");
   135      g_assert_null(tricky_value);
   136      sc_error_free(err);
   137      fclose(stream);
   138  
   139      /* Key with empty value with a trailing newline (which is also valid). */
   140      char tricky4[] = "key=\n";
   141      stream = fmemopen(tricky4, sizeof tricky4 - 1, "r");
   142      g_assert_nonnull(stream);
   143      rc = sc_infofile_get_key(stream, "key", &tricky_value, &err);
   144      g_assert_cmpint(rc, ==, 0);
   145      g_assert_null(err);
   146      g_assert_cmpstr(tricky_value, ==, "");
   147      sc_error_free(err);
   148      fclose(stream);
   149      free(tricky_value);
   150  
   151      /* The equals character alone (key is empty) */
   152      char tricky5[] = "=\n";
   153      stream = fmemopen(tricky5, sizeof tricky5 - 1, "r");
   154      g_assert_nonnull(stream);
   155      rc = sc_infofile_get_key(stream, "key", &tricky_value, &err);
   156      g_assert_cmpint(rc, ==, -1);
   157      g_assert_nonnull(err);
   158      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
   159      g_assert_cmpint(sc_error_code(err), ==, 0);
   160      g_assert_cmpstr(sc_error_msg(err), ==, "line 1 contains empty key");
   161      g_assert_null(tricky_value);
   162      sc_error_free(err);
   163      fclose(stream);
   164  
   165      /* Unexpected section */
   166      char tricky6[] = "[section]\n";
   167      stream = fmemopen(tricky6, sizeof tricky6 - 1, "r");
   168      g_assert_nonnull(stream);
   169      rc = sc_infofile_get_key(stream, "key", &tricky_value, &err);
   170      g_assert_cmpint(rc, ==, -1);
   171      g_assert_nonnull(err);
   172      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
   173      g_assert_cmpint(sc_error_code(err), ==, 0);
   174      g_assert_cmpstr(sc_error_msg(err), ==, "line 1 contains unexpected section");
   175      g_assert_null(tricky_value);
   176      sc_error_free(err);
   177      fclose(stream);
   178  }
   179  
   180  static void test_infofile_get_ini_key(void) {
   181      int rc;
   182      sc_error *err;
   183  
   184      char text[] =
   185          "[section1]\n"
   186          "key=value\n"
   187          "[section2]\n"
   188          "key2=value-two\n"
   189          "other-key2=other-value-two\n"
   190          "key=value-one-two\n";
   191      FILE *stream = fmemopen(text, sizeof text - 1, "r");
   192      g_assert_nonnull(stream);
   193  
   194      char *value;
   195  
   196      /* Key in matching in the first section */
   197      value = NULL;
   198      rewind(stream);
   199      rc = sc_infofile_get_ini_section_key(stream, "section1", "key", &value, &err);
   200      g_assert_cmpint(rc, ==, 0);
   201      g_assert_null(err);
   202      g_assert_nonnull(value);
   203      g_assert_cmpstr(value, ==, "value");
   204      free(value);
   205  
   206      /* Key matching in the second section */
   207      value = NULL;
   208      rewind(stream);
   209      rc = sc_infofile_get_ini_section_key(stream, "section2", "key2", &value, &err);
   210      g_assert_cmpint(rc, ==, 0);
   211      g_assert_null(err);
   212      g_assert_nonnull(value);
   213      g_assert_cmpstr(value, ==, "value-two");
   214      free(value);
   215  
   216      /* Key matching in the second section (identical to the key from 1st section) */
   217      value = NULL;
   218      rewind(stream);
   219      rc = sc_infofile_get_ini_section_key(stream, "section2", "key", &value, &err);
   220      g_assert_cmpint(rc, ==, 0);
   221      g_assert_null(err);
   222      g_assert_nonnull(value);
   223      g_assert_cmpstr(value, ==, "value-one-two");
   224      free(value);
   225  
   226      /* No matching section */
   227      value = NULL;
   228      rewind(stream);
   229      rc = sc_infofile_get_ini_section_key(stream, "section-x", "key", &value, &err);
   230      g_assert_cmpint(rc, ==, 0);
   231      g_assert_null(err);
   232      g_assert_null(value);
   233  
   234      /* Invalid empty section name */
   235      value = NULL;
   236      rewind(stream);
   237      rc = sc_infofile_get_ini_section_key(stream, "", "key", &value, &err);
   238      g_assert_cmpint(rc, ==, -1);
   239      g_assert_nonnull(err);
   240      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
   241      g_assert_cmpint(sc_error_code(err), ==, SC_API_MISUSE);
   242      g_assert_cmpstr(sc_error_msg(err), ==, "section name cannot be empty");
   243      g_assert_null(value);
   244      sc_error_free(err);
   245  
   246      /* Malformed section */
   247      value = NULL;
   248      char malformed[] = "[section\n";
   249      stream = fmemopen(malformed, sizeof malformed - 1, "r");
   250      g_assert_nonnull(stream);
   251      rc = sc_infofile_get_ini_section_key(stream, "section", "key", &value, &err);
   252      g_assert_cmpint(rc, ==, -1);
   253      g_assert_nonnull(err);
   254      g_assert_cmpstr(sc_error_domain(err), ==, SC_LIBSNAP_DOMAIN);
   255      g_assert_cmpint(sc_error_code(err), ==, 0);
   256      g_assert_cmpstr(sc_error_msg(err), ==, "line 1 is not a valid ini section");
   257      g_assert_null(value);
   258      sc_error_free(err);
   259      fclose(stream);
   260  }
   261  
   262  static void __attribute__((constructor)) init(void) {
   263      g_test_add_func("/infofile/get_key", test_infofile_get_key);
   264      g_test_add_func("/infofile/get_ini_key", test_infofile_get_ini_key);
   265  }