1 /*
2  * Copyright (C) 2005 Mike McCormack for CodeWeavers
3  *
4  * A test program for MSI records
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 #include "precomp.h"
22 
23 static const char *msifile = "winetest-record.msi";
24 static const WCHAR msifileW[] =
25     {'w','i','n','e','t','e','s','t','-','r','e','c','o','r','d','.','m','s','i',0};
26 
27 static BOOL create_temp_file(char *name)
28 {
29     UINT r;
30     unsigned char buffer[26], i;
31     DWORD sz;
32     HANDLE handle;
33 
34     r = GetTempFileNameA(".", "msitest",0,name);
35     if(!r)
36         return r;
37     handle = CreateFileA(name, GENERIC_READ|GENERIC_WRITE,
38         0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
39     if(handle==INVALID_HANDLE_VALUE)
40         return FALSE;
41     for(i=0; i<26; i++)
42         buffer[i]=i+'a';
43     r = WriteFile(handle,buffer,sizeof buffer,&sz,NULL);
44     CloseHandle(handle);
45     return r;
46 }
47 
48 static void test_msirecord(void)
49 {
50     DWORD r, sz;
51     INT i;
52     MSIHANDLE h;
53     char buf[10];
54     WCHAR bufW[10];
55     const char str[] = "hello";
56     const WCHAR strW[] = { 'h','e','l','l','o',0};
57     char filename[MAX_PATH];
58 
59     /* check behaviour with an invalid record */
60     r = MsiRecordGetFieldCount(0);
61     ok(r==-1, "field count for invalid record not -1\n");
62     SetLastError(0);
63     r = MsiRecordIsNull(0, 0);
64     ok(r==0, "invalid handle not considered to be non-null...\n");
65     ok(GetLastError()==0, "MsiRecordIsNull set LastError\n");
66     r = MsiRecordGetInteger(0,0);
67     ok(r == MSI_NULL_INTEGER, "got integer from invalid record\n");
68     r = MsiRecordSetInteger(0,0,0);
69     ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
70     r = MsiRecordSetInteger(0,-1,0);
71     ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
72     SetLastError(0);
73     h = MsiCreateRecord(-1);
74     ok(h==0, "created record with -1 elements\n");
75     h = MsiCreateRecord(0x10000);
76     ok(h==0, "created record with 0x10000 elements\n");
77     /* doesn't set LastError */
78     ok(GetLastError()==0, "MsiCreateRecord set last error\n");
79     r = MsiRecordClearData(0);
80     ok(r == ERROR_INVALID_HANDLE, "MsiRecordClearData returned wrong error\n");
81     r = MsiRecordDataSize(0,0);
82     ok(r == 0, "MsiRecordDataSize returned wrong error\n");
83 
84 
85     /* check behaviour of a record with 0 elements */
86     h = MsiCreateRecord(0);
87     ok(h!=0, "couldn't create record with zero elements\n");
88     r = MsiRecordGetFieldCount(h);
89     ok(r==0, "field count should be zero\n");
90     r = MsiRecordIsNull(h,0);
91     ok(r, "new record wasn't null\n");
92     r = MsiRecordIsNull(h,1);
93     ok(r, "out of range record wasn't null\n");
94     r = MsiRecordIsNull(h,-1);
95     ok(r, "out of range record wasn't null\n");
96     r = MsiRecordDataSize(h,0);
97     ok(r==0, "size of null record is 0\n");
98     sz = sizeof buf;
99     strcpy(buf,"x");
100     r = MsiRecordGetStringA(h, 0, buf, &sz);
101     ok(r==ERROR_SUCCESS, "failed to get null string\n");
102     ok(sz==0, "null string too long\n");
103     ok(buf[0]==0, "null string not set\n");
104 
105     /* same record, but add an integer to it */
106     r = MsiRecordSetInteger(h, 0, 0);
107     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 0\n");
108     r = MsiRecordIsNull(h,0);
109     ok(r==0, "new record is null after setting an integer\n");
110     r = MsiRecordDataSize(h,0);
111     ok(r==sizeof(DWORD), "size of integer record is 4\n");
112     r = MsiRecordSetInteger(h, 0, 1);
113     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 1\n");
114     r = MsiRecordSetInteger(h, 1, 1);
115     ok(r == ERROR_INVALID_PARAMETER, "set integer at 1\n");
116     r = MsiRecordSetInteger(h, -1, 0);
117     ok(r == ERROR_INVALID_PARAMETER, "set integer at -1\n");
118     r = MsiRecordIsNull(h,0);
119     ok(r==0, "new record is null after setting an integer\n");
120     r = MsiRecordGetInteger(h, 0);
121     ok(r == 1, "failed to get integer\n");
122 
123     /* same record, but add a null or empty string to it */
124     r = MsiRecordSetStringA(h, 0, NULL);
125     ok(r == ERROR_SUCCESS, "Failed to set null string at 0\n");
126     r = MsiRecordIsNull(h, 0);
127     ok(r == TRUE, "null string not null field\n");
128     r = MsiRecordDataSize(h, 0);
129     ok(r == 0, "size of string record is strlen\n");
130     buf[0] = 0;
131     sz = sizeof buf;
132     r = MsiRecordGetStringA(h, 0, buf, &sz);
133     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
134     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
135     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
136     bufW[0] = 0;
137     sz = sizeof bufW / sizeof bufW[0];
138     r = MsiRecordGetStringW(h, 0, bufW, &sz);
139     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
140     ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n");
141     ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n");
142     r = MsiRecordSetStringA(h, 0, "");
143     ok(r == ERROR_SUCCESS, "Failed to set empty string at 0\n");
144     r = MsiRecordIsNull(h, 0);
145     ok(r == TRUE, "null string not null field\n");
146     r = MsiRecordDataSize(h, 0);
147     ok(r == 0, "size of string record is strlen\n");
148     buf[0] = 0;
149     sz = sizeof buf;
150     r = MsiRecordGetStringA(h, 0, buf, &sz);
151     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
152     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
153     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
154     bufW[0] = 0;
155     sz = sizeof bufW / sizeof bufW[0];
156     r = MsiRecordGetStringW(h, 0, bufW, &sz);
157     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
158     ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n");
159     ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n");
160 
161     /* same record, but add a null integer to it */
162     r = MsiRecordSetInteger(h, 0, 1);
163     ok(r == ERROR_SUCCESS, "Failed to set integer at 0\n");
164     r = MsiRecordIsNull(h, 0);
165     ok(r == FALSE, "expected field to be non-null\n");
166     r = MsiRecordSetInteger(h, 0, MSI_NULL_INTEGER);
167     ok(r == ERROR_SUCCESS, "Failed to set integer at 0\n");
168     r = MsiRecordIsNull(h, 0);
169     ok(r == TRUE, "expected field to be null\n");
170     sz = sizeof buf;
171     r = MsiRecordGetStringA(h, 0, buf, &sz);
172     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
173     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
174     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
175 
176     /* same record, but add a string to it */
177     r = MsiRecordSetStringA(h,0,str);
178     ok(r == ERROR_SUCCESS, "Failed to set string at 0\n");
179     r = MsiRecordGetInteger(h, 0);
180     ok(r == MSI_NULL_INTEGER, "should get invalid integer\n");
181     r = MsiRecordDataSize(h,0);
182     ok(r==sizeof str-1, "size of string record is strlen\n");
183     buf[0]=0;
184     sz = sizeof buf;
185     r = MsiRecordGetStringA(h,0,buf,&sz);
186     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
187     ok(0==strcmp(buf,str), "MsiRecordGetStringA returned the wrong string\n");
188     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
189     buf[0]=0;
190     sz = sizeof str - 2;
191     r = MsiRecordGetStringA(h,0,buf,&sz);
192     ok(r == ERROR_MORE_DATA, "small buffer should yield ERROR_MORE_DATA\n");
193     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
194     ok(0==strncmp(buf,str,sizeof str-3), "MsiRecordGetStringA returned the wrong string\n");
195     ok(buf[sizeof str - 3]==0, "string wasn't nul terminated\n");
196 
197     buf[0]=0;
198     sz = sizeof str;
199     r = MsiRecordGetStringA(h,0,buf,&sz);
200     ok(r == ERROR_SUCCESS, "wrong error\n");
201     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
202     ok(0==strcmp(buf,str), "MsiRecordGetStringA returned the wrong string\n");
203 
204 
205     memset(bufW, 0, sizeof bufW);
206     sz = 5;
207     r = MsiRecordGetStringW(h,0,bufW,&sz);
208     ok(r == ERROR_MORE_DATA, "wrong error\n");
209     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
210     ok(0==memcmp(bufW,strW,8), "MsiRecordGetStringA returned the wrong string\n");
211 
212     sz = 0;
213     bufW[0] = 'x';
214     r = MsiRecordGetStringW(h,0,bufW,&sz);
215     ok(r == ERROR_MORE_DATA, "wrong error\n");
216     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
217     ok('x'==bufW[0], "MsiRecordGetStringA returned the wrong string\n");
218 
219     memset(buf, 0, sizeof buf);
220     sz = 5;
221     r = MsiRecordGetStringA(h,0,buf,&sz);
222     ok(r == ERROR_MORE_DATA, "wrong error\n");
223     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
224     ok(0==memcmp(buf,str,4), "MsiRecordGetStringA returned the wrong string\n");
225 
226     sz = 0;
227     buf[0] = 'x';
228     r = MsiRecordGetStringA(h,0,buf,&sz);
229     ok(r == ERROR_MORE_DATA, "wrong error\n");
230     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
231     ok('x'==buf[0], "MsiRecordGetStringA returned the wrong string\n");
232 
233     /* same record, check we can wipe all the data */
234     r = MsiRecordClearData(h);
235     ok(r == ERROR_SUCCESS, "Failed to clear record\n");
236     r = MsiRecordClearData(h);
237     ok(r == ERROR_SUCCESS, "Failed to clear record again\n");
238     r = MsiRecordIsNull(h,0);
239     ok(r, "cleared record wasn't null\n");
240 
241     /* same record, try converting strings to integers */
242     i = MsiRecordSetStringA(h,0,"42");
243     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
244     i = MsiRecordGetInteger(h, 0);
245     ok(i == 42, "should get invalid integer\n");
246     i = MsiRecordSetStringA(h,0,"-42");
247     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
248     i = MsiRecordGetInteger(h, 0);
249     ok(i == -42, "should get invalid integer\n");
250     i = MsiRecordSetStringA(h,0," 42");
251     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
252     i = MsiRecordGetInteger(h, 0);
253     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
254     i = MsiRecordSetStringA(h,0,"42 ");
255     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
256     i = MsiRecordGetInteger(h, 0);
257     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
258     i = MsiRecordSetStringA(h,0,"42.0");
259     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
260     i = MsiRecordGetInteger(h, 0);
261     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
262     i = MsiRecordSetStringA(h,0,"0x42");
263     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
264     i = MsiRecordGetInteger(h, 0);
265     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
266     i = MsiRecordSetStringA(h,0,"1000000000000000");
267     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
268     i = MsiRecordGetInteger(h, 0);
269     ok(i == -1530494976, "should get truncated integer\n");
270     i = MsiRecordSetStringA(h,0,"2147483647");
271     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
272     i = MsiRecordGetInteger(h, 0);
273     ok(i == 2147483647, "should get maxint\n");
274     i = MsiRecordSetStringA(h,0,"-2147483647");
275     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
276     i = MsiRecordGetInteger(h, 0);
277     ok(i == -2147483647, "should get -maxint-1\n");
278     i = MsiRecordSetStringA(h,0,"4294967297");
279     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
280     i = MsiRecordGetInteger(h, 0);
281     ok(i == 1, "should get one\n");
282     i = MsiRecordSetStringA(h,0,"foo");
283     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
284     i = MsiRecordGetInteger(h, 0);
285     ok(i == MSI_NULL_INTEGER, "should get zero\n");
286     i = MsiRecordSetStringA(h,0,"");
287     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
288     i = MsiRecordGetInteger(h, 0);
289     ok(i == MSI_NULL_INTEGER, "should get zero\n");
290     i = MsiRecordSetStringA(h,0,"+1");
291     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
292     i = MsiRecordGetInteger(h, 0);
293     ok(i == MSI_NULL_INTEGER, "should get zero\n");
294 
295     /* same record, try converting integers to strings */
296     r = MsiRecordSetInteger(h, 0, 32);
297     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
298     sz = 1;
299     r = MsiRecordGetStringA(h, 0, NULL, &sz);
300     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
301     ok(sz == 2, "length wrong\n");
302     buf[0]=0;
303     sz = sizeof buf;
304     r = MsiRecordGetStringA(h, 0, buf, &sz);
305     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
306     ok(0==strcmp(buf,"32"), "failed to get string from integer\n");
307     r = MsiRecordSetInteger(h, 0, -32);
308     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
309     buf[0]=0;
310     sz = 1;
311     r = MsiRecordGetStringA(h, 0, NULL, &sz);
312     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
313     ok(sz == 3, "length wrong\n");
314     sz = sizeof buf;
315     r = MsiRecordGetStringA(h, 0, buf, &sz);
316     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
317     ok(0==strcmp(buf,"-32"), "failed to get string from integer\n");
318     buf[0]=0;
319 
320     /* same record, now try streams */
321     r = MsiRecordSetStreamA(h, 0, NULL);
322     ok(r == ERROR_INVALID_PARAMETER, "set NULL stream\n");
323     sz = sizeof buf;
324     r = MsiRecordReadStream(h, 0, buf, &sz);
325     ok(r == ERROR_INVALID_DATATYPE, "read non-stream type\n");
326     ok(sz == sizeof buf, "set sz\n");
327     r = MsiRecordDataSize( h, -1);
328     ok(r == 0,"MsiRecordDataSize returned wrong size\n");
329     r = MsiRecordDataSize( h, 0);
330     ok(r == 4,"MsiRecordDataSize returned wrong size\n");
331 
332     /* same record, now close it */
333     r = MsiCloseHandle(h);
334     ok(r == ERROR_SUCCESS, "Failed to close handle\n");
335 
336     /* now try streams in a new record - need to create a file to play with */
337     r = create_temp_file(filename);
338     if(!r)
339         return;
340 
341     /* streams can't be inserted in field 0 for some reason */
342     h = MsiCreateRecord(2);
343     ok(h, "couldn't create a two field record\n");
344     r = MsiRecordSetStreamA(h, 0, filename);
345     ok(r == ERROR_INVALID_PARAMETER, "added stream to field 0\n");
346     r = MsiRecordSetStreamA(h, 1, filename);
347     ok(r == ERROR_SUCCESS, "failed to add stream to record\n");
348     r = MsiRecordReadStream(h, 1, buf, NULL);
349     ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
350     DeleteFileA(filename); /* Windows 98 doesn't like this at all, so don't check return. */
351     r = MsiRecordReadStream(h, 1, NULL, NULL);
352     ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
353     sz = sizeof buf;
354     r = MsiRecordReadStream(h, 1, NULL, &sz);
355     ok(r == ERROR_SUCCESS, "failed to read stream\n");
356     ok(sz==26,"couldn't get size of stream\n");
357     sz = 0;
358     r = MsiRecordReadStream(h, 1, buf, &sz);
359     ok(r == ERROR_SUCCESS, "failed to read stream\n");
360     ok(sz==0,"short read\n");
361     sz = sizeof buf;
362     r = MsiRecordReadStream(h, 1, buf, &sz);
363     ok(r == ERROR_SUCCESS, "failed to read stream\n");
364     ok(sz==sizeof buf,"short read\n");
365     ok(!strncmp(buf,"abcdefghij",10), "read the wrong thing\n");
366     sz = sizeof buf;
367     r = MsiRecordReadStream(h, 1, buf, &sz);
368     ok(r == ERROR_SUCCESS, "failed to read stream\n");
369     ok(sz==sizeof buf,"short read\n");
370     ok(!strncmp(buf,"klmnopqrst",10), "read the wrong thing\n");
371     memset(buf,0,sizeof buf);
372     sz = sizeof buf;
373     r = MsiRecordReadStream(h, 1, buf, &sz);
374     ok(r == ERROR_SUCCESS, "failed to read stream\n");
375     ok(sz==6,"short read\n");
376     ok(!strcmp(buf,"uvwxyz"), "read the wrong thing\n");
377     memset(buf,0,sizeof buf);
378     sz = sizeof buf;
379     r = MsiRecordReadStream(h, 1, buf, &sz);
380     ok(r == ERROR_SUCCESS, "failed to read stream\n");
381     ok(sz==0,"size non-zero at end of stream\n");
382     ok(buf[0]==0, "read something at end of the stream\n");
383     r = MsiRecordSetStreamA(h, 1, NULL);
384     ok(r == ERROR_SUCCESS, "failed to reset stream\n");
385     sz = 0;
386     r = MsiRecordReadStream(h, 1, NULL, &sz);
387     ok(r == ERROR_SUCCESS, "bytes left wrong after reset\n");
388     ok(sz==26,"couldn't get size of stream\n");
389     r = MsiRecordDataSize(h,1);
390     ok(r == 26,"MsiRecordDataSize returned wrong size\n");
391 
392     /* now close the stream record */
393     r = MsiCloseHandle(h);
394     ok(r == ERROR_SUCCESS, "Failed to close handle\n");
395     DeleteFileA(filename); /* Delete it for sure, when everything else is closed. */
396 }
397 
398 static void test_MsiRecordGetString(void)
399 {
400     MSIHANDLE rec;
401     CHAR buf[MAX_PATH];
402     DWORD sz;
403     UINT r;
404 
405     rec = MsiCreateRecord(2);
406     ok(rec != 0, "Expected a valid handle\n");
407 
408     sz = MAX_PATH;
409     r = MsiRecordGetStringA(rec, 1, NULL, &sz);
410     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r);
411     ok(sz == 0, "Expected 0, got %d\n",sz);
412 
413     sz = MAX_PATH;
414     lstrcpyA(buf, "apple");
415     r = MsiRecordGetStringA(rec, 1, buf, &sz);
416     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
417     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
418     ok(sz == 0, "Expected 0, got %d\n", sz);
419 
420     sz = MAX_PATH;
421     lstrcpyA(buf, "apple");
422     r = MsiRecordGetStringA(rec, 10, buf, &sz);
423     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
424     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
425     ok(sz == 0, "Expected 0, got %d\n", sz);
426 
427     MsiCloseHandle(rec);
428 
429     rec = MsiCreateRecord(1);
430     ok(rec != 0, "Expected a valid handle\n");
431 
432     r = MsiRecordSetInteger(rec, 1, 5);
433     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
434 
435     sz = MAX_PATH;
436     r = MsiRecordGetStringA(rec, 1, NULL, &sz);
437     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r);
438     ok(sz == 1, "Expected 1, got %d\n",sz);
439 
440     sz = MAX_PATH;
441     lstrcpyA(buf, "apple");
442     r = MsiRecordGetStringA(rec, 1, buf, &sz);
443     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
444     ok(!lstrcmpA(buf, "5"), "Expected \"5\", got \"%s\"\n", buf);
445     ok(sz == 1, "Expected 1, got %d\n", sz);
446 
447     r = MsiRecordSetInteger(rec, 1, -5);
448     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
449 
450     sz = MAX_PATH;
451     lstrcpyA(buf, "apple");
452     r = MsiRecordGetStringA(rec, 1, buf, &sz);
453     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
454     ok(!lstrcmpA(buf, "-5"), "Expected \"-5\", got \"%s\"\n", buf);
455     ok(sz == 2, "Expected 2, got %d\n", sz);
456 
457     MsiCloseHandle(rec);
458 }
459 
460 static void test_MsiRecordGetInteger(void)
461 {
462     MSIHANDLE rec;
463     INT val;
464     UINT r;
465 
466     rec = MsiCreateRecord(1);
467     ok(rec != 0, "Expected a valid handle\n");
468 
469     r = MsiRecordSetStringA(rec, 1, "5");
470     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
471 
472     val = MsiRecordGetInteger(rec, 1);
473     ok(val == 5, "Expected 5, got %d\n", val);
474 
475     r = MsiRecordSetStringA(rec, 1, "-5");
476     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
477 
478     val = MsiRecordGetInteger(rec, 1);
479     ok(val == -5, "Expected -5, got %d\n", val);
480 
481     r = MsiRecordSetStringA(rec, 1, "5apple");
482     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
483 
484     val = MsiRecordGetInteger(rec, 1);
485     ok(val == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", val);
486 
487     MsiCloseHandle(rec);
488 }
489 
490 static void test_fieldzero(void)
491 {
492     MSIHANDLE hdb, hview, rec;
493     CHAR buf[MAX_PATH];
494     LPCSTR query;
495     DWORD sz;
496     UINT r;
497 
498     rec = MsiCreateRecord(1);
499     ok(rec != 0, "Expected a valid handle\n");
500 
501     r = MsiRecordGetInteger(rec, 0);
502     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
503 
504     sz = MAX_PATH;
505     lstrcpyA(buf, "apple");
506     r = MsiRecordGetStringA(rec, 0, buf, &sz);
507     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
508     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
509     ok(sz == 0, "Expected 0, got %d\n", sz);
510 
511     r = MsiRecordIsNull(rec, 0);
512     ok(r == TRUE, "Expected TRUE, got %d\n", r);
513 
514     r = MsiRecordGetInteger(rec, 1);
515     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
516 
517     r = MsiRecordSetInteger(rec, 1, 42);
518     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
519 
520     r = MsiRecordGetInteger(rec, 0);
521     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
522 
523     sz = MAX_PATH;
524     lstrcpyA(buf, "apple");
525     r = MsiRecordGetStringA(rec, 0, buf, &sz);
526     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
527     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
528     ok(sz == 0, "Expected 0, got %d\n", sz);
529 
530     r = MsiRecordIsNull(rec, 0);
531     ok(r == TRUE, "Expected TRUE, got %d\n", r);
532 
533     r = MsiRecordGetInteger(rec, 1);
534     ok(r == 42, "Expected 42, got %d\n", r);
535 
536     r = MsiRecordSetStringA(rec, 1, "bologna");
537     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
538 
539     r = MsiRecordGetInteger(rec, 0);
540     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
541 
542     sz = MAX_PATH;
543     lstrcpyA(buf, "apple");
544     r = MsiRecordGetStringA(rec, 0, buf, &sz);
545     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
546     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
547     ok(sz == 0, "Expected 0, got %d\n", sz);
548 
549     r = MsiRecordIsNull(rec, 0);
550     ok(r == TRUE, "Expected TRUE, got %d\n", r);
551 
552     sz = MAX_PATH;
553     lstrcpyA(buf, "apple");
554     r = MsiRecordGetStringA(rec, 1, buf, &sz);
555     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
556     ok(!lstrcmpA(buf, "bologna"), "Expected \"bologna\", got \"%s\"\n", buf);
557     ok(sz == 7, "Expected 7, got %d\n", sz);
558 
559     MsiCloseHandle(rec);
560 
561     r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_CREATE, &hdb);
562     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
563 
564     query = "CREATE TABLE `drone` ( "
565            "`id` INT, `name` CHAR(32), `number` CHAR(32) "
566            "PRIMARY KEY `id`)";
567     r = MsiDatabaseOpenViewA(hdb, query, &hview);
568     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
569     r = MsiViewExecute(hview, 0);
570     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
571     r = MsiViewClose(hview);
572     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
573     r = MsiCloseHandle(hview);
574     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
575 
576     query = "INSERT INTO `drone` ( `id`, `name`, `number` )"
577            "VALUES('1', 'Abe', '8675309')";
578     r = MsiDatabaseOpenViewA(hdb, query, &hview);
579     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
580     r = MsiViewExecute(hview, 0);
581     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
582     r = MsiViewClose(hview);
583     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
584     r = MsiCloseHandle(hview);
585     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
586 
587     r = MsiDatabaseGetPrimaryKeysA(hdb, "drone", &rec);
588     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
589 
590     r = MsiRecordGetInteger(rec, 0);
591     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
592 
593     sz = MAX_PATH;
594     lstrcpyA(buf, "apple");
595     r = MsiRecordGetStringA(rec, 0, buf, &sz);
596     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
597     ok(!lstrcmpA(buf, "drone"), "Expected \"drone\", got \"%s\"\n", buf);
598     ok(sz == 5, "Expected 5, got %d\n", sz);
599 
600     r = MsiRecordIsNull(rec, 0);
601     ok(r == FALSE, "Expected FALSE, got %d\n", r);
602 
603     MsiCloseHandle(rec);
604 
605     r = MsiDatabaseGetPrimaryKeysA(hdb, "nosuchtable", &rec);
606     ok(r == ERROR_INVALID_TABLE, "Expected ERROR_INVALID_TABLE, got %d\n", r);
607 
608     query = "SELECT * FROM `drone` WHERE `id` = 1";
609     r = MsiDatabaseOpenViewA(hdb, query, &hview);
610     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
611     r = MsiViewExecute(hview, 0);
612     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
613     r = MsiViewFetch(hview, &rec);
614     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
615 
616     r = MsiRecordGetInteger(rec, 0);
617     ok(r != MSI_NULL_INTEGER && r != 0, "Expected non-NULL value, got %d\n", r);
618 
619     r = MsiRecordIsNull(rec, 0);
620     ok(r == FALSE, "Expected FALSE, got %d\n", r);
621 
622     r = MsiCloseHandle(hview);
623     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
624     MsiCloseHandle(rec);
625     MsiCloseHandle(hdb);
626     DeleteFileA(msifile);
627 }
628 
629 START_TEST(record)
630 {
631     test_msirecord();
632     test_MsiRecordGetString();
633     test_MsiRecordGetInteger();
634     test_fieldzero();
635 }
636