usleep tests: Avoid failure due to known Cygwin 3.5.3 bug.
[gnulib.git] / tests / test-userspec.c
blobe1f375513a76c1af9aed225858d878317318cc03
1 /* Test userspec.c
2 Copyright (C) 2009-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Jim Meyering. */
19 #include <config.h>
21 #include "userspec.h"
23 #include <stdio.h>
24 #include <assert.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <pwd.h>
29 #include <grp.h>
31 #include "xalloc.h"
33 #define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
35 struct test
37 char in[100];
38 uid_t uid;
39 gid_t gid;
40 const char *user_name;
41 const char *group_name;
42 const char *result;
45 static struct test T[] =
47 { "", -1, -1, "", "", NULL},
48 { ":", -1, -1, "", "", NULL},
49 { "+0:+0", 0, 0, "", "", NULL},
50 { "+0:+0", 0, 0, "", "", NULL},
51 { ":+1", -1, 1, "", "", NULL},
52 { "+1", 1, -1, "", "", NULL},
53 { ":+0", -1, 0, "", "", NULL},
54 { "+22:+42", 22, 42, "", "", NULL},
55 /* (uint32_t)-1 should be invalid everywhere */
56 { "+4294967295:+4294967295", 0, 0, NULL, NULL, "invalid user"},
57 /* likewise, but with only the group being invalid */
58 { "+0:+4294967295", 0, 0, NULL, NULL, "invalid group"},
59 { ":+4294967295", 0, 0, NULL, NULL, "invalid group"},
60 /* and only the user being invalid */
61 { "+4294967295:+0", 0, 0, NULL, NULL, "invalid user"},
62 /* and using 2^32 */
63 { "+4294967296:+4294967296", 0, 0, NULL, NULL, "invalid user"},
64 { "+0:+4294967296", 0, 0, NULL, NULL, "invalid group"},
65 { ":+4294967296", 0, 0, NULL, NULL, "invalid group"},
66 { "+4294967296:+0", 0, 0, NULL, NULL, "invalid user"},
67 /* numeric user and no group is invalid */
68 { "+4294967295:", 0, 0, NULL, NULL, "invalid spec"},
69 { "+4294967296:", 0, 0, NULL, NULL, "invalid spec"},
70 { "+1:", 0, 0, NULL, NULL, "invalid spec"},
71 { "+0:", 0, 0, NULL, NULL, "invalid spec"},
73 /* "username:" must expand to UID:GID where GID is username's login group */
74 /* Add an entry like the following to the table, if possible.
75 { "U_NAME:", UID,GID, U_NAME, G_NAME, NULL}, */
76 { "" /* placeholder */, -1, -1, "", "", NULL},
79 #define STREQ(a, b) (strcmp (a, b) == 0)
81 static char const *
82 maybe_null (char const *s)
84 return s ? s : "NULL";
87 static bool
88 same_diag (char const *s, char const *t)
90 if (s == NULL && t == NULL)
91 return true;
92 if (s == NULL || t == NULL)
93 return false;
94 return STREQ (s, t);
97 int
98 main (void)
100 unsigned int i;
101 int fail = 0;
103 /* Find a UID that has both a user name and login group name,
104 but skip UID 0. */
106 uid_t uid;
107 for (uid = 1200; 0 < uid; uid--)
109 struct group *gr;
110 struct passwd *pw = getpwuid (uid);
111 unsigned int j;
112 size_t len;
113 if (!pw || !pw->pw_name || !(gr = getgrgid (pw->pw_gid)) || !gr->gr_name)
114 continue;
115 j = ARRAY_CARDINALITY (T) - 1;
116 len = strlen (pw->pw_name);
117 if (sizeof T[j].in - 2 < len)
118 continue;
120 /* Store "username:" in T[j].in. */
121 memcpy(T[j].in, pw->pw_name, len);
122 strcpy(T[j].in + len, ":");
124 T[j].uid = uid;
125 T[j].gid = gr->gr_gid;
126 T[j].user_name = xstrdup (pw->pw_name);
127 T[j].group_name = xstrdup (gr->gr_name);
128 T[j].result = NULL;
129 break;
133 char *user_name = NULL;
134 char *group_name = NULL;
136 for (i = 0; i < ARRAY_CARDINALITY (T); i++)
138 uid_t uid = (uid_t) -1;
139 gid_t gid = (gid_t) -1;
140 free (user_name);
141 free (group_name);
142 char const *diag = parse_user_spec (T[i].in, &uid, &gid,
143 &user_name, &group_name);
144 if (!same_diag (diag, T[i].result))
146 printf ("%s return value mismatch: got %s, expected %s\n",
147 T[i].in, maybe_null (diag), maybe_null (T[i].result));
148 fail = 1;
149 continue;
152 if (diag)
153 continue;
155 if (uid != T[i].uid || gid != T[i].gid)
157 printf ("%s mismatch (-: expected uid,gid; +:actual)\n"
158 "-%3lu,%3lu\n+%3lu,%3lu\n",
159 T[i].in,
160 (unsigned long int) T[i].uid,
161 (unsigned long int) T[i].gid,
162 (unsigned long int) uid,
163 (unsigned long int) gid);
164 fail = 1;
167 if (T[i].result)
169 /* Expected a non-NULL result diagnostic, yet got NULL. */
170 diag = "NULL";
171 printf ("%s diagnostic mismatch (-: expected diagnostic; +:actual)\n"
172 "-%s\n+%s\n", T[i].in, T[i].result, diag);
173 fail = 1;
175 else
177 /* Should get the same result, but with a warning, if replacing
178 ':' with '.'. */
179 char *colon = strchr (T[i].in, ':');
180 if (colon)
182 *colon = '.';
183 uid_t uid2 = -1;
184 gid_t gid2 = -1;
185 char *user_name2 = NULL;
186 char *group_name2 = NULL;
187 bool warn;
188 if (! (parse_user_spec_warn (T[i].in, &uid2, &gid2,
189 &user_name2, &group_name2, &warn)
190 && warn))
191 printf ("%s did not warn\n", T[i].in);
192 else if (! (uid == uid2 && gid == gid2
193 && !!user_name == !!user_name2
194 && (!user_name || STREQ (user_name, user_name2))
195 && !!group_name == !!group_name2
196 && (!group_name || STREQ (group_name, group_name2))))
197 printf ("%s treated differently than with colon\n", T[i].in);
199 free (user_name2);
200 free (group_name2);
205 /* Ensure NULL parameters are ignored. */
207 uid_t uid = (uid_t) -1;
208 char const *diag = parse_user_spec ("", &uid, NULL, NULL, NULL);
209 if (diag)
211 printf ("unexpected error: %s\n", diag);
212 fail = 1;
216 return fail;
220 Local Variables:
221 indent-tabs-mode: nil
222 End: