2 * INF file parsing tests
4 * Copyright 2002, 2005 Alexandre Julliard for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/test.h"
33 static const char tmpfile
[] = ".\\tmp.inf";
35 /* some large strings */
36 #define A255 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
37 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
38 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
39 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
41 #define A400 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
42 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
43 "aaaaaaaaaaaaaaaa" A256
44 #define A511 A255 A256
45 #define A4097 "a" A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256
47 #define STD_HEADER "[Version]\r\nSignature=\"$CHICAGO$\"\r\n"
49 #define STR_SECTION "[Strings]\nfoo=aaa\nbar=bbb\nloop=%loop2%\nloop2=%loop%\n" \
50 "per%%cent=abcd\nper=1\ncent=2\n22=foo\n" \
52 "verybig=" A400 A400 A400 "\n"
54 /* create a new file with specified contents and open it */
55 static HINF
test_file_contents( const char *data
, UINT
*err_line
)
58 HANDLE handle
= CreateFileA( tmpfile
, GENERIC_READ
|GENERIC_WRITE
,
59 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, 0, 0 );
60 if (handle
== INVALID_HANDLE_VALUE
) return 0;
61 if (!WriteFile( handle
, data
, strlen(data
), &res
, NULL
)) trace( "write error\n" );
62 CloseHandle( handle
);
63 return SetupOpenInfFileA( tmpfile
, 0, INF_STYLE_WIN4
, err_line
);
66 static const char *get_string_field( INFCONTEXT
*context
, DWORD index
)
68 static char buffer
[MAX_INF_STRING_LENGTH
+32];
69 if (SetupGetStringFieldA( context
, index
, buffer
, sizeof(buffer
), NULL
)) return buffer
;
73 static const char *get_line_text( INFCONTEXT
*context
)
75 static char buffer
[MAX_INF_STRING_LENGTH
+32];
76 if (SetupGetLineTextA( context
, 0, 0, 0, buffer
, sizeof(buffer
), NULL
)) return buffer
;
81 /* Test various valid/invalid file formats */
91 /* file contents expected error (or 0) errline todo */
92 { "\r\n", ERROR_WRONG_INF_STYLE
, 0, 0 },
93 { "abcd\r\n", ERROR_WRONG_INF_STYLE
, 0, 1 },
94 { "[Version]\r\n", ERROR_WRONG_INF_STYLE
, 0, 0 },
95 { "[Version]\nSignature=", ERROR_WRONG_INF_STYLE
, 0, 0 },
96 { "[Version]\nSignature=foo", ERROR_WRONG_INF_STYLE
, 0, 0 },
97 { "[version]\nsignature=$chicago$", 0, 0, 0 },
98 { "[VERSION]\nSIGNATURE=$CHICAGO$", 0, 0, 0 },
99 { "[Version]\nSignature=$chicago$,abcd", 0, 0, 0 },
100 { "[Version]\nabc=def\nSignature=$chicago$", 0, 0, 0 },
101 { "[Version]\nabc=def\n[Version]\nSignature=$chicago$", 0, 0, 0 },
102 { STD_HEADER
, 0, 0, 0 },
103 { STD_HEADER
"[]\r\n", 0, 0, 0 },
104 { STD_HEADER
"]\r\n", 0, 0, 0 },
105 { STD_HEADER
"[" A255
"]\r\n", 0, 0, 0 },
106 { STD_HEADER
"[ab\r\n", ERROR_BAD_SECTION_NAME_LINE
, 3, 0 },
107 { STD_HEADER
"\n\n[ab\x1a]\n", ERROR_BAD_SECTION_NAME_LINE
, 5, 0 },
108 { STD_HEADER
"[" A256
"]\r\n", ERROR_SECTION_NAME_TOO_LONG
, 3, 0 },
109 { "[abc]\n" STD_HEADER
, 0, 0, 0 },
110 { "abc\r\n" STD_HEADER
, ERROR_EXPECTED_SECTION_NAME
, 1, 0 },
111 { ";\n;\nabc\r\n" STD_HEADER
, ERROR_EXPECTED_SECTION_NAME
, 3, 0 },
112 { ";\n;\nab\nab\n" STD_HEADER
, ERROR_EXPECTED_SECTION_NAME
, 3, 0 },
113 { ";aa\n;bb\n" STD_HEADER
, 0, 0, 0 },
114 { STD_HEADER
" [TestSection\x00] \n", ERROR_BAD_SECTION_NAME_LINE
, 3, 0 },
115 { STD_HEADER
" [Test\x00Section] \n", ERROR_BAD_SECTION_NAME_LINE
, 3, 0 },
116 { STD_HEADER
" [TestSection\x00] \n", ERROR_BAD_SECTION_NAME_LINE
, 3, 0 },
117 { STD_HEADER
" [Test\x00Section] \n", ERROR_BAD_SECTION_NAME_LINE
, 3, 0 },
120 static void test_invalid_files(void)
127 for (i
= 0; i
< sizeof(invalid_files
)/sizeof(invalid_files
[0]); i
++)
129 SetLastError( 0xdeadbeef );
130 err_line
= 0xdeadbeef;
131 hinf
= test_file_contents( invalid_files
[i
].data
, &err_line
);
132 err
= GetLastError();
133 trace( "hinf=%p err=%lx line=%d\n", hinf
, err
, err_line
);
134 if (invalid_files
[i
].error
) /* should fail */
136 ok( hinf
== INVALID_HANDLE_VALUE
, "file %u: Open succeeded\n", i
);
137 if (invalid_files
[i
].todo
) todo_wine
139 ok( err
== invalid_files
[i
].error
, "file %u: Bad error %lx/%lx\n",
140 i
, err
, invalid_files
[i
].error
);
141 ok( err_line
== invalid_files
[i
].err_line
, "file %u: Bad error line %d/%d\n",
142 i
, err_line
, invalid_files
[i
].err_line
);
146 ok( err
== invalid_files
[i
].error
, "file %u: Bad error %lx/%lx\n",
147 i
, err
, invalid_files
[i
].error
);
148 ok( err_line
== invalid_files
[i
].err_line
, "file %u: Bad error line %d/%d\n",
149 i
, err_line
, invalid_files
[i
].err_line
);
152 else /* should succeed */
154 ok( hinf
!= INVALID_HANDLE_VALUE
, "file %u: Open failed\n", i
);
155 ok( err
== 0, "file %u: Error code set to %lx\n", i
, err
);
157 if (hinf
!= INVALID_HANDLE_VALUE
) SetupCloseInfFile( hinf
);
162 /* Test various section names */
171 /* file contents section name error code */
172 { STD_HEADER
"[TestSection]", "TestSection", 0 },
173 { STD_HEADER
"[TestSection]\n", "TestSection", 0 },
174 { STD_HEADER
"[TESTSECTION]\r\n", "TestSection", 0 },
175 { STD_HEADER
"[TestSection]\n[abc]", "testsection", 0 },
176 { STD_HEADER
";[TestSection]\n", "TestSection", ERROR_SECTION_NOT_FOUND
},
177 { STD_HEADER
"[TestSection]\n", "Bad name", ERROR_SECTION_NOT_FOUND
},
179 { STD_HEADER
"[TestSection] \r\n", "TestSection", 0 },
180 { STD_HEADER
" [TestSection]\r\n", "TestSection", 0 },
181 { STD_HEADER
" [TestSection] dummy\r\n", "TestSection", 0 },
182 { STD_HEADER
" [TestSection] [foo]\r\n", "TestSection", 0 },
183 { STD_HEADER
" [ Test Section ] dummy\r\n", " Test Section ", 0 },
184 { STD_HEADER
"[TestSection] \032\ndummy", "TestSection", 0 },
185 { STD_HEADER
"[TestSection] \n\032dummy", "TestSection", 0 },
186 /* special chars in section name */
187 { STD_HEADER
"[Test[Section]\r\n", "Test[Section", 0 },
188 { STD_HEADER
"[Test[S]ection]\r\n", "Test[S", 0 },
189 { STD_HEADER
"[Test[[[Section]\r\n", "Test[[[Section", 0 },
190 { STD_HEADER
"[]\r\n", "", 0 },
191 { STD_HEADER
"[[[]\n", "[[", 0 },
192 { STD_HEADER
"[Test\"Section]\r\n", "Test\"Section", 0 },
193 { STD_HEADER
"[Test\\Section]\r\n", "Test\\Section", 0 },
194 { STD_HEADER
"[Test\\ Section]\r\n", "Test\\ Section", 0 },
195 { STD_HEADER
"[Test;Section]\r\n", "Test;Section", 0 },
196 /* various control chars */
197 { STD_HEADER
" [Test\r\b\tSection] \n", "Test\r\b\tSection", 0 },
201 static void test_section_names(void)
209 for (i
= 0; i
< sizeof(section_names
)/sizeof(section_names
[0]); i
++)
211 SetLastError( 0xdeadbeef );
212 hinf
= test_file_contents( section_names
[i
].data
, &err_line
);
213 ok( hinf
!= INVALID_HANDLE_VALUE
, "line %u: open failed err %lx\n", i
, GetLastError() );
214 if (hinf
== INVALID_HANDLE_VALUE
) continue;
216 ret
= SetupGetLineCountA( hinf
, section_names
[i
].section
);
217 err
= GetLastError();
218 trace( "hinf=%p ret=%ld err=%lx\n", hinf
, ret
, err
);
221 ok( !section_names
[i
].error
, "line %u: section name %s found\n",
222 i
, section_names
[i
].section
);
223 ok( !err
, "line %u: bad error code %lx\n", i
, err
);
227 ok( section_names
[i
].error
, "line %u: section name %s not found\n",
228 i
, section_names
[i
].section
);
229 ok( err
== section_names
[i
].error
, "line %u: bad error %lx/%lx\n",
230 i
, err
, section_names
[i
].error
);
232 SetupCloseInfFile( hinf
);
237 /* Test various key and value names */
243 const char *fields
[10];
246 /* file contents expected key expected fields */
247 { "ab=cd", "ab", { "cd" } },
248 { "ab=cd,ef,gh,ij", "ab", { "cd", "ef", "gh", "ij" } },
249 { "ab", "ab", { "ab" } },
250 { "ab,cd", NULL
, { "ab", "cd" } },
251 { "ab,cd=ef", NULL
, { "ab", "cd=ef" } },
252 { "=abcd,ef", "", { "abcd", "ef" } },
254 { "ba\\\ncd=ef", "bacd", { "ef" } },
255 { "ab \\ \ncd=ef", "abcd", { "ef" } },
256 { "ab\\\ncd,ef", NULL
, { "abcd", "ef" } },
257 { "ab \\ ;cc\ncd=ef", "abcd", { "ef" } },
258 { "ab \\ \\ \ncd=ef", "abcd", { "ef" } },
259 { "ba \\ dc=xx", "ba \\ dc", { "xx" } },
260 { "ba \\\\ \nc=d", "bac", { "d" } },
261 { "a=b\\\\c", "a", { "b\\\\c" } },
262 { "ab=cd \\ ", "ab", { "cd" } },
263 { "ba=c \\ \n \\ \n a", "ba", { "ca" } },
264 { "ba=c \\ \n \\ a", "ba", { "c\\ a" } },
265 { " \\ a= \\ b", "\\ a", { "\\ b" } },
267 { "Ab\"Cd\"=Ef", "AbCd", { "Ef" } },
268 { "Ab\"Cd=Ef\"", "AbCd=Ef", { "AbCd=Ef" } },
269 { "ab\"\"\"cd,ef=gh\"", "ab\"cd,ef=gh", { "ab\"cd,ef=gh" } },
270 { "ab\"\"cd=ef", "abcd", { "ef" } },
271 { "ab\"\"cd=ef,gh", "abcd", { "ef", "gh" } },
272 { "ab=cd\"\"ef", "ab", { "cdef" } },
273 { "ab=cd\",\"ef", "ab", { "cd,ef" } },
274 { "ab=cd\",ef", "ab", { "cd,ef" } },
275 { "ab=cd\",ef\\\nab", "ab", { "cd,ef\\" } },
277 { " a b = c , d \n", "a b", { "c", "d" } },
278 { " a b = c ,\" d\" \n", "a b", { "c", " d" } },
279 { " a b\r = c\r\n", "a b", { "c" } },
281 { "a=b,,,c,,,d", "a", { "b", "", "", "c", "", "", "d" } },
282 { "a=b,\"\",c,\" \",d", "a", { "b", "", "c", " ", "d" } },
283 { "=,,b", "", { "", "", "b" } },
284 { ",=,,b", NULL
, { "", "=", "", "b" } },
285 { "a=\n", "a", { "" } },
288 { "ab=c\032d", "ab", { "c" } },
289 { "ab\032=cd", "ab", { "ab" } },
291 { "abcd=ef\x0gh", "abcd", { "ef" } },
292 /* multiple sections with same name */
293 { "[Test2]\nab\n[Test]\nee=ff\n", "ee", { "ff" } },
294 /* string substitution */
295 { "%foo%=%bar%\n" STR_SECTION
, "aaa", { "bbb" } },
296 { "%foo%xx=%bar%yy\n" STR_SECTION
, "aaaxx", { "bbbyy" } },
297 { "%% %foo%=%bar%\n" STR_SECTION
, "% aaa", { "bbb" } },
298 { "%f\"o\"o%=ccc\n" STR_SECTION
, "aaa", { "ccc" } },
299 { "abc=%bar;bla%\n" STR_SECTION
, "abc", { "%bar" } },
300 { "loop=%loop%\n" STR_SECTION
, "loop", { "%loop2%" } },
301 { "%per%%cent%=100\n" STR_SECTION
, "12", { "100" } },
302 { "a=%big%\n" STR_SECTION
, "a", { A400
} },
303 { "a=%verybig%\n" STR_SECTION
, "a", { A511
} }, /* truncated to 511 */
304 { "a=%big%%big%%big%%big%\n" STR_SECTION
, "a", { A400 A400 A400 A400
} },
305 { "a=%big%%big%%big%%big%%big%%big%%big%%big%%big%\n" STR_SECTION
, "a", { A400 A400 A400 A400 A400 A400 A400 A400 A400
} },
306 { "a=%big%%big%%big%%big%%big%%big%%big%%big%%big%%big%%big%\n" STR_SECTION
, "a", { A4097
/*MAX_INF_STRING_LENGTH+1*/ } },
309 /* check the key of a certain line */
310 static const char *check_key( INFCONTEXT
*context
, const char *wanted
)
312 const char *key
= get_string_field( context
, 0 );
313 DWORD err
= GetLastError();
317 ok( !wanted
, "missing key %s\n", wanted
);
318 ok( err
== 0 || err
== ERROR_INVALID_PARAMETER
, "last error set to %lx\n", err
);
322 ok( !strcmp( key
, wanted
), "bad key %s/%s\n", key
, wanted
);
323 ok( err
== 0, "last error set to %lx\n", err
);
328 static void test_key_names(void)
330 char buffer
[MAX_INF_STRING_LENGTH
+32];
331 const char *key
, *line
;
332 unsigned int i
, index
, count
;
339 for (i
= 0; i
< sizeof(key_names
)/sizeof(key_names
[0]); i
++)
341 strcpy( buffer
, STD_HEADER
"[Test]\n" );
342 strcat( buffer
, key_names
[i
].data
);
343 SetLastError( 0xdeadbeef );
344 hinf
= test_file_contents( buffer
, &err_line
);
345 ok( hinf
!= INVALID_HANDLE_VALUE
, "line %u: open failed err %lx\n", i
, GetLastError() );
346 if (hinf
== INVALID_HANDLE_VALUE
) continue;
348 ret
= SetupFindFirstLineA( hinf
, "Test", 0, &context
);
351 key
= check_key( &context
, key_names
[i
].key
);
353 buffer
[0] = buffer
[1] = 0; /* build the full line */
354 for (index
= 0; ; index
++)
356 const char *field
= get_string_field( &context
, index
+ 1 );
357 err
= GetLastError();
360 ok( err
== 0, "line %u: bad error %lx\n", i
, GetLastError() );
361 if (key_names
[i
].fields
[index
])
362 ok( !strcmp( field
, key_names
[i
].fields
[index
] ), "line %u: bad field %s/%s\n",
363 i
, field
, key_names
[i
].fields
[index
] );
365 ok( 0, "line %u: got extra field %s\n", i
, field
);
366 strcat( buffer
, "," );
367 strcat( buffer
, field
);
371 ok( err
== 0 || err
== ERROR_INVALID_PARAMETER
,
372 "line %u: bad error %lx\n", i
, GetLastError() );
373 if (key_names
[i
].fields
[index
])
374 ok( 0, "line %u: missing field %s\n", i
, key_names
[i
].fields
[index
] );
376 if (!key_names
[i
].fields
[index
]) break;
378 count
= SetupGetFieldCount( &context
);
379 ok( count
== index
, "line %u: bad count %d/%d\n", i
, index
, count
);
381 line
= get_line_text( &context
);
382 ok( line
!= NULL
, "line %u: SetupGetLineText failed\n", i
);
383 if (line
) ok( !strcmp( line
, buffer
+1 ), "line %u: bad text %s/%s\n", i
, line
, buffer
+1 );
385 SetupCloseInfFile( hinf
);
390 static void test_SetupCloseInfFile(void)
392 /* try to close with invalid handles */
393 SetupCloseInfFile( NULL
);
394 SetupCloseInfFile( INVALID_HANDLE_VALUE
);
399 test_invalid_files();
400 test_section_names();
402 test_SetupCloseInfFile();
403 DeleteFileA( tmpfile
);