xref: /freebsd/lib/libc/tests/stdlib/strfrom_test.c (revision f68d7bfc1479042184e09431bd55771c50c47f68)
1 /*
2  * Copyright (c) 2026 Faraz Vahedi <kfv@kfv.io>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 /*
8  * Tests for strfromd(3), strfromf(3), and strfroml(3).
9  *
10  * C23 §7.24.1.3 states that these functions are equivalent to snprintf(3),
11  * albeit with some subtle exceptions, and therefore snprintf(3) is used as
12  * the correctness oracle.  Hardcoded string checks guard against both
13  * producing the same wrong answer.
14  */
15 
16 #include <errno.h>
17 #include <float.h>
18 #include <locale.h>
19 #include <math.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include <atf-c.h>
25 
26 /*
27  * Buffer large enough for any finite value in %f form, including LDBL_MAX on
28  * platforms with 80-bit or 128-bit long double.  Sizing for the full output
29  * keeps the snprintf(3) oracle a full-length comparison rather than letting
30  * both sides truncate alike.
31  */
32 #define	FBUF	8192
33 
34 static void
check_d(const char * fmt,double val)35 check_d(const char *fmt, double val)
36 {
37 	char got[FBUF], ref[FBUF];
38 	int rlen, glen;
39 
40 	rlen = snprintf(ref, sizeof(ref), fmt, val);
41 	glen = strfromd(got, sizeof(got), fmt, val);
42 	ATF_CHECK_MSG(glen == rlen,
43 	    "strfromd(\"%s\", %g): return %d, want %d", fmt, val, glen, rlen);
44 	ATF_CHECK_STREQ_MSG(ref, got, "strfromd(\"%s\", %g)", fmt, val);
45 }
46 
47 static void
check_f(const char * fmt,float val)48 check_f(const char *fmt, float val)
49 {
50 	char got[FBUF], ref[FBUF];
51 	int rlen, glen;
52 
53 	rlen = snprintf(ref, sizeof(ref), fmt, (double)val);
54 	glen = strfromf(got, sizeof(got), fmt, val);
55 	ATF_CHECK_MSG(glen == rlen,
56 	    "strfromf(\"%s\", %g): return %d, want %d",
57 	    fmt, (double)val, glen, rlen);
58 	ATF_CHECK_STREQ_MSG(ref, got, "strfromf(\"%s\", %g)", fmt, (double)val);
59 }
60 
61 static void
check_l(const char * fmt,long double val)62 check_l(const char *fmt, long double val)
63 {
64 	char got[FBUF], ref[FBUF], lfmt[32];
65 	int rlen, glen;
66 
67 	/*
68 	 * snprintf requires the 'L' length modifier for long double.
69 	 */
70 	snprintf(lfmt, sizeof(lfmt), "%.*sL%c",
71 	    (int)(strlen(fmt) - 1), fmt, fmt[strlen(fmt) - 1]);
72 	rlen = snprintf(ref, sizeof(ref), lfmt, val);
73 	glen = strfroml(got, sizeof(got), fmt, val);
74 	ATF_CHECK_MSG(glen == rlen,
75 	    "strfroml(\"%s\", %Lg): return %d, want %d", fmt, val, glen, rlen);
76 	ATF_CHECK_STREQ_MSG(ref, got, "strfroml(\"%s\", %Lg)", fmt, val);
77 }
78 
79 ATF_TC_WITHOUT_HEAD(strfromd_f);
ATF_TC_BODY(strfromd_f,tc)80 ATF_TC_BODY(strfromd_f, tc)
81 {
82 	char buf[FBUF];
83 
84 	check_d("%f", 0.0);
85 	check_d("%f", 1.0);
86 	check_d("%f", -1.0);
87 	check_d("%f", 3.14159265358979);
88 	check_d("%f", 1.0 / 3.0);
89 	check_d("%f", 1e10);
90 	check_d("%f", 1e-10);
91 	check_d("%f", DBL_MAX);
92 	check_d("%f", DBL_MIN);
93 	check_d("%.0f", 3.14);
94 	check_d("%.0f", 3.5);
95 	check_d("%.0f", 4.5);
96 	check_d("%.2f", 3.14159);
97 	check_d("%.10f", 1.0 / 7.0);
98 	check_d("%.20f", 1.0);
99 
100 	strfromd(buf, sizeof(buf), "%.6f", 0.0);
101 	ATF_CHECK_STREQ("0.000000", buf);
102 	strfromd(buf, sizeof(buf), "%.6f", -1.5);
103 	ATF_CHECK_STREQ("-1.500000", buf);
104 }
105 
106 ATF_TC_WITHOUT_HEAD(strfromd_e);
ATF_TC_BODY(strfromd_e,tc)107 ATF_TC_BODY(strfromd_e, tc)
108 {
109 	char buf[FBUF];
110 
111 	check_d("%e", 0.0);
112 	check_d("%e", 1.0);
113 	check_d("%e", -1.0);
114 	check_d("%e", 3.14159265358979);
115 	check_d("%e", 1e100);
116 	check_d("%e", 1e-100);
117 	check_d("%E", 2.718281828);
118 	check_d("%.0e", 3.14);
119 	check_d("%.2e", 12345.6789);
120 	check_d("%.15e", 1.0 / 3.0);
121 
122 	strfromd(buf, sizeof(buf), "%e", 1.0);
123 	ATF_CHECK_STREQ("1.000000e+00", buf);
124 	strfromd(buf, sizeof(buf), "%E", 1.0);
125 	ATF_CHECK_STREQ("1.000000E+00", buf);
126 }
127 
128 ATF_TC_WITHOUT_HEAD(strfromd_g);
ATF_TC_BODY(strfromd_g,tc)129 ATF_TC_BODY(strfromd_g, tc)
130 {
131 	char buf[FBUF];
132 
133 	check_d("%g", 0.0);
134 	check_d("%g", 1.0);
135 	check_d("%g", 100000.0);
136 	check_d("%g", 1000000.0);        /* exponent >= prec:   hence %e */
137 	check_d("%g", 0.0001);           /* exponent == -4:     hence %f */
138 	check_d("%g", 0.00001);          /* exponent < -4:      hence %e */
139 	check_d("%g", 3.14159265358979);
140 	check_d("%G", 1.23e10);
141 	check_d("%.2g", 3.14159);
142 	check_d("%.0g", 3.14159);
143 	check_d("%.10g", 1.0 / 7.0);
144 	check_d("%g", DBL_MIN);
145 	check_d("%g", DBL_MAX);
146 
147 	strfromd(buf, sizeof(buf), "%g", 1.0);
148 	ATF_CHECK_STREQ("1", buf);
149 	strfromd(buf, sizeof(buf), "%g", 100.0);
150 	ATF_CHECK_STREQ("100", buf);
151 }
152 
153 ATF_TC_WITHOUT_HEAD(strfromd_a);
ATF_TC_BODY(strfromd_a,tc)154 ATF_TC_BODY(strfromd_a, tc)
155 {
156 	char buf[FBUF];
157 
158 	check_d("%a", 0.0);
159 	check_d("%a", 1.0);
160 	check_d("%a", -1.0);
161 	check_d("%a", 0.5);
162 	check_d("%a", 1.5);
163 	check_d("%a", DBL_MIN);
164 	check_d("%a", DBL_MAX);
165 	check_d("%A", 1.0);
166 	check_d("%.4a", 1.0 / 3.0);
167 	check_d("%.0a", 1.5);
168 
169 	strfromd(buf, sizeof(buf), "%a", 1.0);
170 	ATF_CHECK_STREQ("0x1p+0", buf);
171 	strfromd(buf, sizeof(buf), "%a", 0.5);
172 	ATF_CHECK_STREQ("0x1p-1", buf);
173 	strfromd(buf, sizeof(buf), "%A", 1.0);
174 	ATF_CHECK_STREQ("0X1P+0", buf);
175 }
176 
177 ATF_TC_WITHOUT_HEAD(strfromd_special);
ATF_TC_BODY(strfromd_special,tc)178 ATF_TC_BODY(strfromd_special, tc)
179 {
180 	char buf[FBUF];
181 
182 	check_d("%f", INFINITY);
183 	check_d("%f", -INFINITY);
184 	check_d("%f", NAN);
185 	check_d("%e", INFINITY);
186 	check_d("%e", -INFINITY);
187 	check_d("%e", NAN);
188 	check_d("%g", INFINITY);
189 	check_d("%g", -INFINITY);
190 	check_d("%g", NAN);
191 	check_d("%a", INFINITY);
192 	check_d("%a", -INFINITY);
193 	check_d("%a", NAN);
194 	check_d("%F", INFINITY);
195 	check_d("%E", INFINITY);
196 	check_d("%G", INFINITY);
197 	check_d("%A", INFINITY);
198 	check_d("%f", -0.0);
199 	check_d("%g", -0.0);
200 	check_d("%e", -0.0);
201 	check_d("%a", -0.0);
202 
203 	strfromd(buf, sizeof(buf), "%f", INFINITY);
204 	ATF_CHECK_STREQ("inf", buf);
205 	strfromd(buf, sizeof(buf), "%F", INFINITY);
206 	ATF_CHECK_STREQ("INF", buf);
207 	strfromd(buf, sizeof(buf), "%f", -INFINITY);
208 	ATF_CHECK_STREQ("-inf", buf);
209 	strfromd(buf, sizeof(buf), "%f", NAN);
210 	ATF_CHECK_STREQ("nan", buf);
211 	strfromd(buf, sizeof(buf), "%F", NAN);
212 	ATF_CHECK_STREQ("NAN", buf);
213 }
214 
215 ATF_TC_WITHOUT_HEAD(strfromd_truncation);
ATF_TC_BODY(strfromd_truncation,tc)216 ATF_TC_BODY(strfromd_truncation, tc)
217 {
218 	char buf[FBUF];
219 	int ret;
220 
221 	/*
222 	 * When n == 0 the buffer must not be touched and the return value
223 	 * must still reflect the full output length.
224 	 */
225 	buf[0] = 'X';
226 	ret = strfromd(buf, 0, "%f", 1.0);
227 	ATF_CHECK(ret > 0);
228 	ATF_CHECK_MSG(buf[0] == 'X', "buffer modified with n==0");
229 
230 	/*
231 	 * With a short buffer, a null terminated prefix is written and the
232 	 * return value is the length that would have been needed.
233 	 */
234 	ret = strfromd(buf, 5, "%.6f", 1.0);
235 	ATF_CHECK_MSG(ret == 8, "return %d, want 8", ret);
236 	ATF_CHECK_MSG(buf[4] == '\0', "not null terminated at buf[4]");
237 	ATF_CHECK_MSG(strncmp(buf, "1.00", 4) == 0,
238 	    "prefix mismatch: got \"%.*s\"", 4, buf);
239 }
240 
241 ATF_TC_WITHOUT_HEAD(strfromf_f);
ATF_TC_BODY(strfromf_f,tc)242 ATF_TC_BODY(strfromf_f, tc)
243 {
244 	check_f("%f", 0.0f);
245 	check_f("%f", 1.0f);
246 	check_f("%f", -1.0f);
247 	check_f("%f", 3.14159f);
248 	check_f("%.0f", 3.5f);
249 	check_f("%.2f", 3.14159f);
250 	check_f("%.9f", 1.0f / 3.0f);
251 	check_f("%f", FLT_MAX);
252 	check_f("%f", FLT_MIN);
253 }
254 
255 ATF_TC_WITHOUT_HEAD(strfromf_e);
ATF_TC_BODY(strfromf_e,tc)256 ATF_TC_BODY(strfromf_e, tc)
257 {
258 	char buf[FBUF];
259 
260 	check_f("%e", 0.0f);
261 	check_f("%e", 1.0f);
262 	check_f("%e", -1.0f);
263 	check_f("%e", 3.14159f);
264 	check_f("%E", 2.71828f);
265 	check_f("%.2e", 12345.6f);
266 	check_f("%.0e", 3.14f);
267 
268 	strfromf(buf, sizeof(buf), "%e", 1.0f);
269 	ATF_CHECK_STREQ("1.000000e+00", buf);
270 }
271 
272 ATF_TC_WITHOUT_HEAD(strfromf_g);
ATF_TC_BODY(strfromf_g,tc)273 ATF_TC_BODY(strfromf_g, tc)
274 {
275 	char buf[FBUF];
276 
277 	check_f("%g", 0.0f);
278 	check_f("%g", 1.0f);
279 	check_f("%g", 100000.0f);
280 	check_f("%g", 1000000.0f);
281 	check_f("%g", 0.0001f);
282 	check_f("%g", 0.00001f);
283 	check_f("%G", 1.23e10f);
284 	check_f("%.2g", 3.14159f);
285 
286 	strfromf(buf, sizeof(buf), "%g", 1.0f);
287 	ATF_CHECK_STREQ("1", buf);
288 }
289 
290 ATF_TC_WITHOUT_HEAD(strfromf_a);
ATF_TC_BODY(strfromf_a,tc)291 ATF_TC_BODY(strfromf_a, tc)
292 {
293 	char buf[FBUF];
294 
295 	check_f("%a", 0.0f);
296 	check_f("%a", 1.0f);
297 	check_f("%a", 0.5f);
298 	check_f("%a", -1.0f);
299 	check_f("%A", 1.0f);
300 	check_f("%.2a", 1.0f / 3.0f);
301 
302 	strfromf(buf, sizeof(buf), "%a", 1.0f);
303 	ATF_CHECK_STREQ("0x1p+0", buf);
304 }
305 
306 ATF_TC_WITHOUT_HEAD(strfromf_special);
ATF_TC_BODY(strfromf_special,tc)307 ATF_TC_BODY(strfromf_special, tc)
308 {
309 	check_f("%f", INFINITY);
310 	check_f("%f", -INFINITY);
311 	check_f("%f", NAN);
312 	check_f("%e", INFINITY);
313 	check_f("%g", INFINITY);
314 	check_f("%a", INFINITY);
315 	check_f("%F", INFINITY);
316 	check_f("%f", -0.0f);
317 	check_f("%g", -0.0f);
318 }
319 
320 ATF_TC_WITHOUT_HEAD(strfromf_truncation);
ATF_TC_BODY(strfromf_truncation,tc)321 ATF_TC_BODY(strfromf_truncation, tc)
322 {
323 	char buf[FBUF];
324 	int ret;
325 
326 	buf[0] = 'X';
327 	ret = strfromf(buf, 0, "%f", 1.0f);
328 	ATF_CHECK(ret > 0);
329 	ATF_CHECK_MSG(buf[0] == 'X', "buffer modified with n==0");
330 
331 	ret = strfromf(buf, 5, "%.6f", 1.0f);
332 	ATF_CHECK_MSG(ret == 8, "return %d, want 8", ret);
333 	ATF_CHECK_MSG(buf[4] == '\0', "not null terminated at buf[4]");
334 }
335 
336 ATF_TC_WITHOUT_HEAD(strfroml_f);
ATF_TC_BODY(strfroml_f,tc)337 ATF_TC_BODY(strfroml_f, tc)
338 {
339 	check_l("%f", 0.0L);
340 	check_l("%f", 1.0L);
341 	check_l("%f", -1.0L);
342 	check_l("%f", 3.14159265358979323846L);
343 	check_l("%.0f", 3.5L);
344 	check_l("%.2f", 3.14159L);
345 	check_l("%.15f", 1.0L / 3.0L);
346 	check_l("%f", LDBL_MAX);
347 	check_l("%f", LDBL_MIN);
348 }
349 
350 ATF_TC_WITHOUT_HEAD(strfroml_e);
ATF_TC_BODY(strfroml_e,tc)351 ATF_TC_BODY(strfroml_e, tc)
352 {
353 	char buf[FBUF];
354 
355 	check_l("%e", 0.0L);
356 	check_l("%e", 1.0L);
357 	check_l("%e", -1.0L);
358 	check_l("%e", 3.14159265358979323846L);
359 	check_l("%E", 2.71828182845904523536L);
360 	check_l("%.2e", 12345.6789L);
361 	check_l("%.0e", 3.14L);
362 	check_l("%e", LDBL_MAX);
363 	check_l("%e", LDBL_MIN);
364 
365 	strfroml(buf, sizeof(buf), "%e", 1.0L);
366 	ATF_CHECK_STREQ("1.000000e+00", buf);
367 }
368 
369 ATF_TC_WITHOUT_HEAD(strfroml_g);
ATF_TC_BODY(strfroml_g,tc)370 ATF_TC_BODY(strfroml_g, tc)
371 {
372 	char buf[FBUF];
373 
374 	check_l("%g", 0.0L);
375 	check_l("%g", 1.0L);
376 	check_l("%g", 100000.0L);
377 	check_l("%g", 1000000.0L);
378 	check_l("%g", 0.0001L);
379 	check_l("%g", 0.00001L);
380 	check_l("%G", 1.23e10L);
381 	check_l("%.2g", 3.14159L);
382 	check_l("%.0g", 3.14159L);
383 
384 	strfroml(buf, sizeof(buf), "%g", 1.0L);
385 	ATF_CHECK_STREQ("1", buf);
386 }
387 
388 ATF_TC_WITHOUT_HEAD(strfroml_a);
ATF_TC_BODY(strfroml_a,tc)389 ATF_TC_BODY(strfroml_a, tc)
390 {
391 	char buf[FBUF];
392 
393 	check_l("%a", 0.0L);
394 	check_l("%a", 1.0L);
395 	check_l("%a", 0.5L);
396 	check_l("%a", -1.0L);
397 	check_l("%A", 1.0L);
398 	check_l("%.4a", 1.0L / 3.0L);
399 	check_l("%.0a", 1.5L);
400 	check_l("%a", LDBL_MAX);
401 	check_l("%a", LDBL_MIN);
402 
403 	strfroml(buf, sizeof(buf), "%a", 1.0L);
404 	ATF_CHECK_STREQ("0x1p+0", buf);
405 }
406 
407 ATF_TC_WITHOUT_HEAD(strfroml_special);
ATF_TC_BODY(strfroml_special,tc)408 ATF_TC_BODY(strfroml_special, tc)
409 {
410 	check_l("%f", INFINITY);
411 	check_l("%f", -INFINITY);
412 	check_l("%f", NAN);
413 	check_l("%e", INFINITY);
414 	check_l("%g", INFINITY);
415 	check_l("%a", INFINITY);
416 	check_l("%F", INFINITY);
417 	check_l("%f", -0.0L);
418 	check_l("%g", -0.0L);
419 #ifdef __HAVE_LONG_DOUBLE
420 	check_l("%.20f", 1.0L / 3.0L);
421 	check_l("%.20e", 1.0L / 7.0L);
422 #endif
423 }
424 
425 ATF_TC_WITHOUT_HEAD(strfroml_truncation);
ATF_TC_BODY(strfroml_truncation,tc)426 ATF_TC_BODY(strfroml_truncation, tc)
427 {
428 	char buf[FBUF];
429 	int ret;
430 
431 	buf[0] = 'X';
432 	ret = strfroml(buf, 0, "%f", 1.0L);
433 	ATF_CHECK(ret > 0);
434 	ATF_CHECK_MSG(buf[0] == 'X', "buffer modified with n==0");
435 
436 	ret = strfroml(buf, 5, "%.6f", 1.0L);
437 	ATF_CHECK_MSG(ret == 8, "return %d, want 8", ret);
438 	ATF_CHECK_MSG(buf[4] == '\0', "not null terminated at buf[4]");
439 }
440 
441 ATF_TC_WITHOUT_HEAD(strfrom_locale);
ATF_TC_BODY(strfrom_locale,tc)442 ATF_TC_BODY(strfrom_locale, tc)
443 {
444 	char buf[FBUF];
445 
446 	/*
447 	 * strfrom* is equivalent to snprintf(3) and so honours LC_NUMERIC:
448 	 * In a locale whose radix is not '.', the output must use that radix.
449 	 * The check_* helpers already compare against snprintf in this locale;
450 	 * the literal checks confirm the radix actually changed for both the
451 	 * decimal and hexadecimal paths.  Skip if the locale is unavailable.
452 	 */
453 	if (setlocale(LC_NUMERIC, "de_DE.UTF-8") == NULL)
454 		atf_tc_skip("de_DE.UTF-8 locale not available");
455 
456 	check_d("%f", 1.5);
457 	check_d("%e", 1.5);
458 	check_d("%g", 1.5);
459 	check_d("%a", 1.5);
460 	check_f("%f", 1.5f);
461 	check_l("%f", 1.5L);
462 
463 	strfromd(buf, sizeof(buf), "%.1f", 1.5);
464 	ATF_CHECK_STREQ("1,5", buf);
465 	strfromd(buf, sizeof(buf), "%a", 1.5);
466 	ATF_CHECK_STREQ("0x1,8p+0", buf);
467 }
468 
469 /*
470  * Per C23 §7.24.1.3p2, format string outside the restricted grammar is
471  * undefined behaviour.  FreeBSD reports it as the programming error it
472  * is; the conversion yields "EDOOFUS" and sets errno accordingly.
473  */
474 static void
check_edoofus(const char * fmt,int ret,const char * buf)475 check_edoofus(const char *fmt, int ret, const char *buf)
476 {
477 	ATF_CHECK_MSG(ret == 7, "%s: return %d, want 7", fmt, ret);
478 	ATF_CHECK_STREQ_MSG("EDOOFUS", buf, "%s: got \"%s\"", fmt, buf);
479 	ATF_CHECK_MSG(errno == EDOOFUS, "%s: errno %d, want %d", fmt, errno,
480 	    EDOOFUS);
481 }
482 
483 ATF_TC_WITHOUT_HEAD(strfrom_empty_format);
ATF_TC_BODY(strfrom_empty_format,tc)484 ATF_TC_BODY(strfrom_empty_format, tc)
485 {
486 	char buf[FBUF];
487 	int ret;
488 
489 	errno = 0;
490 	ret = strfromd(buf, sizeof(buf), "", 1.0);
491 	check_edoofus("", ret, buf);
492 }
493 
494 ATF_TC_WITHOUT_HEAD(strfrom_bad_specifier);
ATF_TC_BODY(strfrom_bad_specifier,tc)495 ATF_TC_BODY(strfrom_bad_specifier, tc)
496 {
497 	char buf[FBUF];
498 	int ret;
499 
500 	errno = 0;
501 	ret = strfromd(buf, sizeof(buf), "%q", 1.0);
502 	check_edoofus("%q", ret, buf);
503 
504 	errno = 0;
505 	ret = strfromf(buf, sizeof(buf), "%.2q", 1.0f);
506 	check_edoofus("%.2q", ret, buf);
507 
508 	errno = 0;
509 	ret = strfroml(buf, sizeof(buf), "%", 1.0L);
510 	check_edoofus("%", ret, buf);
511 
512 	errno = 0;
513 	ret = strfromd(buf, sizeof(buf), "f", 1.0);
514 	check_edoofus("f", ret, buf);
515 
516 	errno = 0;
517 	ret = strfromd(buf, sizeof(buf), "%*f", 1.0);
518 	check_edoofus("%*f", ret, buf);
519 
520 	errno = 0;
521 	ret = strfromd(buf, sizeof(buf), "%f ", 1.0);
522 	check_edoofus("%f ", ret, buf);
523 }
524 
525 ATF_TC_WITHOUT_HEAD(strfrom_bad_format_truncation);
ATF_TC_BODY(strfrom_bad_format_truncation,tc)526 ATF_TC_BODY(strfrom_bad_format_truncation, tc)
527 {
528 	char buf[FBUF];
529 	int ret;
530 
531 	/* The error string obeys the snprintf(3) truncation rules. */
532 	buf[0] = 'X';
533 	errno = 0;
534 	ret = strfromd(buf, 0, "%q", 1.0);
535 	ATF_CHECK_MSG(ret == 7, "return %d, want 7", ret);
536 	ATF_CHECK_MSG(buf[0] == 'X', "buffer modified with n==0");
537 	ATF_CHECK_EQ(EDOOFUS, errno);
538 
539 	errno = 0;
540 	ret = strfromd(buf, 4, "%q", 1.0);
541 	ATF_CHECK_MSG(ret == 7, "return %d, want 7", ret);
542 	ATF_CHECK_STREQ("EDO", buf);
543 	ATF_CHECK_EQ(EDOOFUS, errno);
544 }
545 
546 ATF_TC_WITHOUT_HEAD(strfrom_errno_preserved);
ATF_TC_BODY(strfrom_errno_preserved,tc)547 ATF_TC_BODY(strfrom_errno_preserved, tc)
548 {
549 	char buf[FBUF];
550 
551 	errno = 0;
552 	strfromd(buf, sizeof(buf), "%f", 1.0);
553 	ATF_CHECK_EQ(0, errno);
554 	strfromf(buf, sizeof(buf), "%a", 1.0f);
555 	ATF_CHECK_EQ(0, errno);
556 	strfroml(buf, sizeof(buf), "%.3g", 1.0L);
557 	ATF_CHECK_EQ(0, errno);
558 }
559 
ATF_TP_ADD_TCS(tp)560 ATF_TP_ADD_TCS(tp)
561 {
562 	ATF_TP_ADD_TC(tp, strfromd_f);
563 	ATF_TP_ADD_TC(tp, strfromd_e);
564 	ATF_TP_ADD_TC(tp, strfromd_g);
565 	ATF_TP_ADD_TC(tp, strfromd_a);
566 	ATF_TP_ADD_TC(tp, strfromd_special);
567 	ATF_TP_ADD_TC(tp, strfromd_truncation);
568 
569 	ATF_TP_ADD_TC(tp, strfromf_f);
570 	ATF_TP_ADD_TC(tp, strfromf_e);
571 	ATF_TP_ADD_TC(tp, strfromf_g);
572 	ATF_TP_ADD_TC(tp, strfromf_a);
573 	ATF_TP_ADD_TC(tp, strfromf_special);
574 	ATF_TP_ADD_TC(tp, strfromf_truncation);
575 
576 	ATF_TP_ADD_TC(tp, strfroml_f);
577 	ATF_TP_ADD_TC(tp, strfroml_e);
578 	ATF_TP_ADD_TC(tp, strfroml_g);
579 	ATF_TP_ADD_TC(tp, strfroml_a);
580 	ATF_TP_ADD_TC(tp, strfroml_special);
581 	ATF_TP_ADD_TC(tp, strfroml_truncation);
582 
583 	ATF_TP_ADD_TC(tp, strfrom_locale);
584 	ATF_TP_ADD_TC(tp, strfrom_empty_format);
585 	ATF_TP_ADD_TC(tp, strfrom_bad_specifier);
586 	ATF_TP_ADD_TC(tp, strfrom_bad_format_truncation);
587 	ATF_TP_ADD_TC(tp, strfrom_errno_preserved);
588 
589 	return (atf_no_error());
590 }
591