1*592efe25SPierre Pronchery /*
2*592efe25SPierre Pronchery * test-fileio.c
3*592efe25SPierre Pronchery * Tests for the public libpkgconf file i/o API.
4*592efe25SPierre Pronchery *
5*592efe25SPierre Pronchery * SPDX-License-Identifier: pkgconf
6*592efe25SPierre Pronchery *
7*592efe25SPierre Pronchery * Copyright (c) 2026 pkgconf authors (see AUTHORS).
8*592efe25SPierre Pronchery *
9*592efe25SPierre Pronchery * Permission to use, copy, modify, and/or distribute this software for any
10*592efe25SPierre Pronchery * purpose with or without fee is hereby granted, provided that the above
11*592efe25SPierre Pronchery * copyright notice and this permission notice appear in all copies.
12*592efe25SPierre Pronchery *
13*592efe25SPierre Pronchery * This software is provided 'as is' and without any warranty, express or
14*592efe25SPierre Pronchery * implied. In no event shall the authors be liable for any damages arising
15*592efe25SPierre Pronchery * from the use of this software.
16*592efe25SPierre Pronchery */
17*592efe25SPierre Pronchery
18*592efe25SPierre Pronchery #include "test-api.h"
19*592efe25SPierre Pronchery
20*592efe25SPierre Pronchery static FILE *
fmemstream(const char * contents)21*592efe25SPierre Pronchery fmemstream(const char *contents)
22*592efe25SPierre Pronchery {
23*592efe25SPierre Pronchery FILE *f = tmpfile();
24*592efe25SPierre Pronchery
25*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(f);
26*592efe25SPierre Pronchery
27*592efe25SPierre Pronchery fwrite(contents, 1, strlen(contents), f);
28*592efe25SPierre Pronchery rewind(f);
29*592efe25SPierre Pronchery
30*592efe25SPierre Pronchery return f;
31*592efe25SPierre Pronchery }
32*592efe25SPierre Pronchery
33*592efe25SPierre Pronchery static void
test_fgetline_no_trailing_newline(void)34*592efe25SPierre Pronchery test_fgetline_no_trailing_newline(void)
35*592efe25SPierre Pronchery {
36*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
37*592efe25SPierre Pronchery FILE *f = fmemstream("hello");
38*592efe25SPierre Pronchery
39*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
40*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "hello");
41*592efe25SPierre Pronchery
42*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
43*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
44*592efe25SPierre Pronchery
45*592efe25SPierre Pronchery fclose(f);
46*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
47*592efe25SPierre Pronchery }
48*592efe25SPierre Pronchery
49*592efe25SPierre Pronchery static void
test_fgetline_empty_stream(void)50*592efe25SPierre Pronchery test_fgetline_empty_stream(void)
51*592efe25SPierre Pronchery {
52*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
53*592efe25SPierre Pronchery FILE *f = fmemstream("");
54*592efe25SPierre Pronchery
55*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
56*592efe25SPierre Pronchery
57*592efe25SPierre Pronchery fclose(f);
58*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
59*592efe25SPierre Pronchery }
60*592efe25SPierre Pronchery
61*592efe25SPierre Pronchery static void
test_fgetline_lf(void)62*592efe25SPierre Pronchery test_fgetline_lf(void)
63*592efe25SPierre Pronchery {
64*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
65*592efe25SPierre Pronchery FILE *f = fmemstream("hello\nworld\n");
66*592efe25SPierre Pronchery
67*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
68*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "hello");
69*592efe25SPierre Pronchery
70*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
71*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
72*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "world");
73*592efe25SPierre Pronchery
74*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
75*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
76*592efe25SPierre Pronchery
77*592efe25SPierre Pronchery fclose(f);
78*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
79*592efe25SPierre Pronchery }
80*592efe25SPierre Pronchery
81*592efe25SPierre Pronchery static void
test_fgetline_crlf(void)82*592efe25SPierre Pronchery test_fgetline_crlf(void)
83*592efe25SPierre Pronchery {
84*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
85*592efe25SPierre Pronchery FILE *f = fmemstream("hello\r\nworld\r\n");
86*592efe25SPierre Pronchery
87*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
88*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "hello");
89*592efe25SPierre Pronchery
90*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
91*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
92*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "world");
93*592efe25SPierre Pronchery
94*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
95*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
96*592efe25SPierre Pronchery
97*592efe25SPierre Pronchery fclose(f);
98*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
99*592efe25SPierre Pronchery }
100*592efe25SPierre Pronchery
101*592efe25SPierre Pronchery static void
test_fgetline_lone_cr(void)102*592efe25SPierre Pronchery test_fgetline_lone_cr(void)
103*592efe25SPierre Pronchery {
104*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
105*592efe25SPierre Pronchery FILE *f = fmemstream("hello\rworld\r");
106*592efe25SPierre Pronchery
107*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
108*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "hello");
109*592efe25SPierre Pronchery
110*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
111*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
112*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "world");
113*592efe25SPierre Pronchery
114*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
115*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
116*592efe25SPierre Pronchery
117*592efe25SPierre Pronchery fclose(f);
118*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
119*592efe25SPierre Pronchery }
120*592efe25SPierre Pronchery
121*592efe25SPierre Pronchery static void
test_fgetline_backslash_continuation_lf(void)122*592efe25SPierre Pronchery test_fgetline_backslash_continuation_lf(void)
123*592efe25SPierre Pronchery {
124*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
125*592efe25SPierre Pronchery FILE *f = fmemstream("foo\\\nbar\n");
126*592efe25SPierre Pronchery
127*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
128*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "foobar");
129*592efe25SPierre Pronchery
130*592efe25SPierre Pronchery fclose(f);
131*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
132*592efe25SPierre Pronchery }
133*592efe25SPierre Pronchery
134*592efe25SPierre Pronchery static void
test_fgetline_backslash_continuation_crlf(void)135*592efe25SPierre Pronchery test_fgetline_backslash_continuation_crlf(void)
136*592efe25SPierre Pronchery {
137*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
138*592efe25SPierre Pronchery FILE *f = fmemstream("foo\\\r\nbar\r\n");
139*592efe25SPierre Pronchery
140*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
141*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "foobar");
142*592efe25SPierre Pronchery
143*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
144*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
145*592efe25SPierre Pronchery
146*592efe25SPierre Pronchery fclose(f);
147*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
148*592efe25SPierre Pronchery }
149*592efe25SPierre Pronchery
150*592efe25SPierre Pronchery static void
test_fgetline_backslash_continuation_lone_cr(void)151*592efe25SPierre Pronchery test_fgetline_backslash_continuation_lone_cr(void)
152*592efe25SPierre Pronchery {
153*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
154*592efe25SPierre Pronchery FILE *f = fmemstream("foo\\\rbar\r");
155*592efe25SPierre Pronchery
156*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
157*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "foobar");
158*592efe25SPierre Pronchery
159*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
160*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
161*592efe25SPierre Pronchery
162*592efe25SPierre Pronchery fclose(f);
163*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
164*592efe25SPierre Pronchery }
165*592efe25SPierre Pronchery
166*592efe25SPierre Pronchery // A backslash not immediately followed by a newline is not a continuation,
167*592efe25SPierre Pronchery // so it must be preserved literally in the output.
168*592efe25SPierre Pronchery static void
test_fgetline_backslash_not_continuation(void)169*592efe25SPierre Pronchery test_fgetline_backslash_not_continuation(void)
170*592efe25SPierre Pronchery {
171*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
172*592efe25SPierre Pronchery FILE *f = fmemstream("foo\\bar\n");
173*592efe25SPierre Pronchery
174*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
175*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "foo\\bar");
176*592efe25SPierre Pronchery
177*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
178*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
179*592efe25SPierre Pronchery
180*592efe25SPierre Pronchery fclose(f);
181*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
182*592efe25SPierre Pronchery }
183*592efe25SPierre Pronchery
184*592efe25SPierre Pronchery // fgets() only stops on '\n', a full read buffer, or EOF. NOT on on a lone '\r'.
185*592efe25SPierre Pronchery // If a '\r' happens to be the very last byte fgets() manages to read
186*592efe25SPierre Pronchery // before the buffer fills up, the matching '\n' is still unread in the
187*592efe25SPierre Pronchery // stream and isn't visible to pkgconf_fgetline()'s lookahead, so the CRLF
188*592efe25SPierre Pronchery // pair gets split across two fgets() calls and is misparsed as two lines.
189*592efe25SPierre Pronchery static void
test_fgetline_crlf_split_across_fgets_buffer(void)190*592efe25SPierre Pronchery test_fgetline_crlf_split_across_fgets_buffer(void)
191*592efe25SPierre Pronchery {
192*592efe25SPierre Pronchery pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
193*592efe25SPierre Pronchery size_t prefix_len = PKGCONF_ITEM_SIZE - 2;
194*592efe25SPierre Pronchery char *content = malloc(prefix_len + strlen("\r\nworld\n") + 1);
195*592efe25SPierre Pronchery FILE *f;
196*592efe25SPierre Pronchery
197*592efe25SPierre Pronchery TEST_ASSERT_NONNULL(content);
198*592efe25SPierre Pronchery memset(content, 'a', prefix_len);
199*592efe25SPierre Pronchery memcpy(content + prefix_len, "\r\nworld\n", strlen("\r\nworld\n") + 1);
200*592efe25SPierre Pronchery
201*592efe25SPierre Pronchery f = fmemstream(content);
202*592efe25SPierre Pronchery free(content);
203*592efe25SPierre Pronchery
204*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
205*592efe25SPierre Pronchery TEST_ASSERT_EQ(strlen(pkgconf_buffer_str(&buf)), prefix_len);
206*592efe25SPierre Pronchery
207*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
208*592efe25SPierre Pronchery TEST_ASSERT_TRUE(pkgconf_fgetline(&buf, f));
209*592efe25SPierre Pronchery TEST_ASSERT_STRCMP_EQ(pkgconf_buffer_str(&buf), "world");
210*592efe25SPierre Pronchery
211*592efe25SPierre Pronchery pkgconf_buffer_reset(&buf);
212*592efe25SPierre Pronchery TEST_ASSERT_FALSE(pkgconf_fgetline(&buf, f));
213*592efe25SPierre Pronchery
214*592efe25SPierre Pronchery fclose(f);
215*592efe25SPierre Pronchery pkgconf_buffer_finalize(&buf);
216*592efe25SPierre Pronchery }
217*592efe25SPierre Pronchery
218*592efe25SPierre Pronchery int
main(int argc,const char ** argv)219*592efe25SPierre Pronchery main(int argc, const char **argv)
220*592efe25SPierre Pronchery {
221*592efe25SPierre Pronchery (void) argc;
222*592efe25SPierre Pronchery const char *basename = pkgconf_path_find_basename(argv[0]);
223*592efe25SPierre Pronchery
224*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_no_trailing_newline);
225*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_empty_stream);
226*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_lf);
227*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_crlf);
228*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_lone_cr);
229*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_backslash_continuation_lf);
230*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_backslash_continuation_crlf);
231*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_backslash_continuation_lone_cr);
232*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_backslash_not_continuation);
233*592efe25SPierre Pronchery TEST_RUN(basename, test_fgetline_crlf_split_across_fgets_buffer);
234*592efe25SPierre Pronchery
235*592efe25SPierre Pronchery return EXIT_SUCCESS;
236*592efe25SPierre Pronchery }
237