2 * (c) 2007, Quest Software, Inc. All rights reserved.
4 * This file is part of XScreenSaver,
5 * Copyright (c) 1993-2004 Jamie Zawinski <jwz@jwz.org>
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation. No representations are made about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
20 #include "mlstring.c" /* hokey, but whatever */
22 #define WRAP_WIDTH_PX 100
35 #define FAIL(msg, ...) \
38 fprintf(stderr, "[FAIL] "); \
39 fprintf(stderr, msg, __VA_ARGS__); \
44 #define SUCCEED(testname) \
46 fprintf(stderr, "[SUCCESS] %s\n", (testname)); \
49 #define SKIP(testname) \
51 fprintf(stderr, "[SKIPPED] %s\n", (testname)); \
54 extern mlstring* mlstring_allocate(const char *msg);
55 extern void mlstring_wrap(mlstring *mstr, XFontStruct *font, Dimension width);
57 static int failcount = 0;
59 static char *mlstring_to_cstr(const mlstring *mlstr) {
61 size_t cstrlen = 0, alloclen = 1024;
62 const struct mlstr_line *line;
64 cstr = malloc(alloclen);
69 for (line = mlstr->lines; line; line = line->next_line) {
70 /* Extend the buffer if necessary. */
71 if (cstrlen + strlen(line->line) + 1 > alloclen) {
72 cstr = realloc(cstr, alloclen *= 2);
77 /* If this is not the first line */
78 if (line != mlstr->lines) {
79 /* Append a newline character */
85 strcat(cstr, line->line);
86 cstrlen += strlen(line->line);
91 /* Pass -1 for expect_min or expect_exact to not check that value.
92 * expect_empty_p means an empty line is expected at some point in the string.
93 * Also ensures that the string was not too wide after wrapping. */
94 static int mlstring_expect_lines(const mlstring *mlstr, int expect_min, int expect_exact, Bool expect_empty_p)
97 Bool got_empty_line = False;
98 const struct mlstr_line *line = mlstr->lines;
100 for (count = 0; line; line = line->next_line) {
101 if (line->line[0] == '\0') {
103 FAIL("Not expecting empty lines, but got one on line %d of [%s]", count + 1, mlstring_to_cstr(mlstr));
104 got_empty_line = True;
109 if (expect_empty_p && !got_empty_line)
110 FAIL("Expecting an empty line, but none found in [%s]", mlstring_to_cstr(mlstr));
112 if (expect_exact != -1 && expect_exact != count)
113 FAIL("Expected %d lines, got %d", expect_exact, count);
115 if (expect_min != -1 && count < expect_min)
116 FAIL("Expected at least %d lines, got %d", expect_min, count);
121 static int mlstring_expect(const char *msg, int expect_lines, const mlstring *mlstr, Bool expect_empty_p)
124 const struct mlstr_line *cur;
127 /* Duplicate msg so we can chop it up */
128 str_top = strdup(msg);
132 /* Replace all newlines with NUL */
134 while ((str = strchr(str, '\n')))
137 /* str is now used to point to the expected string */
140 for (cur = mlstr->lines; cur; cur = cur->next_line)
143 if (strcmp(cur->line, str))
144 FAIL("lines didn't match; expected [%s], got [%s]", str, cur->line);
146 str += strlen(str) + 1; /* Point to the next expected string */
151 return mlstring_expect_lines(mlstr, -1, expect_lines, expect_empty_p);
154 /* Ensures that the width has been set properly after wrapping */
155 static int check_width(const char *msg, const mlstring *mlstr) {
156 if (mlstr->overall_width == 0)
157 FAIL("Overall width was zero for string [%s]", msg);
159 if (mlstr->overall_width > WRAP_WIDTH_PX)
160 FAIL("Overall width was %hu but the maximum wrap width was %d", mlstr->overall_width, WRAP_WIDTH_PX);
165 /* FAIL() actually returns the wrong return codes in main, but it
166 * prints a message which is what we want. */
168 #define TRY_NEW(str, numl, expect_empty) \
170 mlstr = mlstring_allocate((str)); \
173 if (SUCCESS == mlstring_expect((str), (numl), mlstr, (expect_empty))) \
178 /* Expects an XFontStruct* font, and tries to wrap to 100px */
179 #define TRY_WRAP(str, minl, expect_empty) \
181 mltest = mlstring_allocate((str)); \
185 mlstring_wrap(mltest, font, WRAP_WIDTH_PX); \
186 check_width((str), mltest); \
187 if (SUCCESS == mlstring_expect_lines(mltest, (minl), -1, (expect_empty))) \
195 /* Ideally this function would use stub functions rather than real Xlib.
196 * Then it would be possible to test for exact line counts, which would be
198 * It also doesn't handle Xlib errors.
200 * Don't print anything based on the return value of this function, it only
201 * returns a value so that I can use the FAIL() macro without warning.
203 * Anyone who understands this function wins a cookie ;)
205 static int test_wrapping(void)
208 XFontStruct *font = NULL;
209 mlstring *mltest = NULL;
211 int chars_per_line, chars_first_word, i;
213 const char *test_short = "a";
214 const char *test_hardwrap = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
215 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
216 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
217 const char *test_withnewlines = "a\nb";
218 char *test_softwrap = NULL;
220 dpy = XOpenDisplay(NULL);
224 font = XLoadQueryFont(dpy, "fixed");
228 TRY_WRAP(test_short, 1, False);
229 TRY_WRAP(test_hardwrap, 2, False);
230 TRY_WRAP(test_withnewlines, 2, False);
232 /* See if wrapping splits on word boundaries like it should */
233 chars_per_line = WRAP_WIDTH_PX / font->max_bounds.width;
234 if (chars_per_line < 3)
237 /* Allocate for 2 lines + \0 */
238 test_softwrap = malloc(chars_per_line * 2 + 1);
242 /* 2 = strlen(' a'); that is, the minimum space required to start a new word
243 * on the same line. */
244 chars_first_word = chars_per_line - 2;
246 for (i = 0; i < chars_first_word; ++i) {
247 test_softwrap[i] = 'a'; /* first word */
248 test_softwrap[i + chars_per_line] = 'b'; /* second word */
250 /* space between first & second words */
251 test_softwrap[chars_first_word] = ' ';
252 /* first char of second word (last char of first line) */
253 test_softwrap[chars_first_word + 1] = 'b';
254 /* after second word */
255 test_softwrap[chars_per_line * 2] = '\0';
257 mltest = mlstring_allocate(test_softwrap);
258 mlstring_wrap(mltest, font, WRAP_WIDTH_PX);
260 /* reusing 'i' for a moment here to make freeing mltest easier */
261 i = strlen(mltest->lines->line);
264 if (i != chars_first_word)
265 FAIL("Soft wrap failed, expected the first line to be %d chars, but it was %d.", chars_first_word, i);
266 SUCCEED("Soft wrap");
275 XFreeFont(dpy, font);
283 return ok ? SUCCESS : SKIPPED; /* Unused, actually */
287 int main(int argc, char *argv[])
289 const char *oneline = "1Foo";
290 const char *twolines = "2Foo\nBar";
291 const char *threelines = "3Foo\nBar\nWhippet";
292 const char *trailnewline = "4Foo\n";
293 const char *trailnewlines = "5Foo\n\n";
294 const char *embeddednewlines = "6Foo\n\nBar";
297 TRY_NEW(oneline, 1, False);
298 TRY_NEW(twolines, 2, False);
299 TRY_NEW(threelines, 3, False);
300 TRY_NEW(trailnewline, 2, True);
301 TRY_NEW(trailnewlines, 3, True);
302 TRY_NEW(embeddednewlines, 3, True);
304 (void) test_wrapping();
306 fprintf(stdout, "%d test failures.\n", failcount);
311 /* vim:ts=8:sw=2:noet