<directory name="lz32">
<xi:include href="lz32/lz32.rbuild" />
</directory>
+<directory name="msi">
+ <xi:include href="msi/msi.rbuild" />
+</directory>
<directory name="msvcrt">
<xi:include href="msvcrt/msvcrt.rbuild" />
</directory>
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ *
+ * A test program for MSI database files.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#include <stdio.h>
+
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static const char *msifile = "winetest.msi";
+
+static void test_msidatabase(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+
+ DeleteFile(msifile);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ res = MsiCloseHandle( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to close database\n" );
+
+ res = DeleteFile( msifile );
+ ok( res == TRUE, "Failed to delete database\n" );
+}
+
+static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec)
+{
+ MSIHANDLE hview = 0;
+ UINT r, ret;
+
+ /* open a select query */
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if (r != ERROR_SUCCESS)
+ return r;
+ r = MsiViewExecute(hview, 0);
+ if (r != ERROR_SUCCESS)
+ return r;
+ ret = MsiViewFetch(hview, phrec);
+ r = MsiViewClose(hview);
+ if (r != ERROR_SUCCESS)
+ return r;
+ r = MsiCloseHandle(hview);
+ if (r != ERROR_SUCCESS)
+ return r;
+ return ret;
+}
+
+static void test_msiinsert(void)
+{
+ MSIHANDLE hdb = 0, hview = 0, hrec = 0;
+ UINT r;
+ const char *query;
+ char buf[80];
+ DWORD sz;
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ /* create a table */
+ query = "CREATE TABLE `phone` ( "
+ "`id` INT, `name` CHAR(32), `number` CHAR(32) "
+ "PRIMARY KEY `id`)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* insert a value into it */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES('1', 'Abe', '8675309')";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` = 1";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
+
+ /* check the record contains what we put in it */
+ r = MsiRecordGetFieldCount(hrec);
+ ok(r == 3, "record count wrong\n");
+
+ todo_wine {
+ r = MsiRecordIsNull(hrec, 0);
+ ok(r == FALSE, "field 0 not null\n");
+ }
+
+ r = MsiRecordGetInteger(hrec, 1);
+ ok(r == 1, "field 1 contents wrong\n");
+ sz = sizeof buf;
+ r = MsiRecordGetString(hrec, 2, buf, &sz);
+ ok(r == ERROR_SUCCESS, "field 2 content fetch failed\n");
+ ok(!strcmp(buf,"Abe"), "field 2 content incorrect\n");
+ sz = sizeof buf;
+ r = MsiRecordGetString(hrec, 3, buf, &sz);
+ ok(r == ERROR_SUCCESS, "field 3 content fetch failed\n");
+ ok(!strcmp(buf,"8675309"), "field 3 content incorrect\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* open a select query */
+ hrec = 100;
+ query = "SELECT * FROM `phone` WHERE `id` >= 10";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+ ok(hrec == 0, "hrec should be null\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` < 0";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` <= 0";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` <> 1";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` > 10";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ todo_wine {
+ /* now try a few bad INSERT xqueries */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES(?, ?)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n");
+
+ if (r == ERROR_SUCCESS)
+ r = MsiCloseHandle(hview);
+ }
+
+ /* construct a record to insert */
+ hrec = MsiCreateRecord(4);
+ r = MsiRecordSetInteger(hrec, 1, 2);
+ ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n");
+ r = MsiRecordSetString(hrec, 2, "Adam");
+ ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
+ r = MsiRecordSetString(hrec, 3, "96905305");
+ ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
+
+ /* insert another value, using a record and wildcards */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES(?, ?, ?)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ if (r == ERROR_SUCCESS)
+ {
+ r = MsiViewExecute(hview, hrec);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+ }
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = MsiViewFetch(0, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiViewFetch failed\n");
+
+ r = MsiDatabaseCommit(hdb);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
+
+ r = MsiCloseHandle(hdb);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = DeleteFile(msifile);
+ ok(r == TRUE, "file didn't exist after commit\n");
+}
+
+typedef UINT (WINAPI *fnMsiDecomposeDescriptorA)(LPCSTR, LPCSTR, LPSTR, LPSTR, DWORD *);
+static fnMsiDecomposeDescriptorA pMsiDecomposeDescriptorA;
+
+static void test_msidecomposedesc(void)
+{
+ char prod[MAX_FEATURE_CHARS+1], comp[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1];
+ const char *desc;
+ UINT r;
+ DWORD len;
+ HMODULE hmod;
+
+ hmod = GetModuleHandle("msi.dll");
+ if (!hmod)
+ return;
+ pMsiDecomposeDescriptorA = (fnMsiDecomposeDescriptorA)
+ GetProcAddress(hmod, "MsiDecomposeDescriptorA");
+ if (!pMsiDecomposeDescriptorA)
+ return;
+
+ /* test a valid feature descriptor */
+ desc = "']gAVn-}f(ZXfeAR6.jiFollowTheWhiteRabbit>3w2x^IGfe?CxI5heAvk.";
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
+ ok(r == ERROR_SUCCESS, "returned an error\n");
+ ok(len == strlen(desc), "length was wrong\n");
+ ok(strcmp(prod,"{90110409-6000-11D3-8CFE-0150048383C9}")==0, "product wrong\n");
+ ok(strcmp(feature,"FollowTheWhiteRabbit")==0, "feature wrong\n");
+ ok(strcmp(comp,"{A7CD68DB-EF74-49C8-FBB2-A7C463B2AC24}")==0,"component wrong\n");
+
+ /* test an invalid feature descriptor with too many characters */
+ desc = "']gAVn-}f(ZXfeAR6.ji"
+ "ThisWillFailIfTheresMoreThanAGuidsChars>"
+ "3w2x^IGfe?CxI5heAvk.";
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
+ ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
+
+ /*
+ * Test a valid feature descriptor with the
+ * maximum number of characters and some trailing characters.
+ */
+ desc = "']gAVn-}f(ZXfeAR6.ji"
+ "ThisWillWorkIfTheresLTEThanAGuidsChars>"
+ "3w2x^IGfe?CxI5heAvk."
+ "extra";
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, NULL, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, NULL, NULL, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, NULL, NULL, NULL, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(NULL, NULL, NULL, NULL, &len);
+ ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
+ ok(len == 0, "length wrong\n");
+}
+
+static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
+{
+ MSIHANDLE htab = 0;
+ UINT res;
+
+ res = MsiDatabaseOpenView( hdb, szQuery, &htab );
+ if(res == ERROR_SUCCESS )
+ {
+ UINT r;
+
+ r = MsiViewExecute( htab, hrec );
+ if(r != ERROR_SUCCESS )
+ res = r;
+
+ r = MsiViewClose( htab );
+ if(r != ERROR_SUCCESS )
+ res = r;
+
+ r = MsiCloseHandle( htab );
+ if(r != ERROR_SUCCESS )
+ res = r;
+ }
+ return res;
+}
+
+static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
+{
+ return try_query_param( hdb, szQuery, 0 );
+}
+
+static UINT try_insert_query( MSIHANDLE hdb, LPCSTR szQuery )
+{
+ MSIHANDLE hrec = 0;
+ UINT r;
+
+ hrec = MsiCreateRecord( 1 );
+ MsiRecordSetString( hrec, 1, "Hello");
+
+ r = try_query_param( hdb, szQuery, hrec );
+
+ MsiCloseHandle( hrec );
+ return r;
+}
+
+static void test_msibadqueries(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT r;
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ r = MsiDatabaseCommit( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to commit database\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to close database\n");
+
+ /* open it readonly */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb );
+ ok(r == ERROR_SUCCESS , "Failed to open database r/o\n");
+
+ /* add a table to it */
+ r = try_query( hdb, "select * from _Tables");
+ ok(r == ERROR_SUCCESS , "query 1 failed\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to close database r/o\n");
+
+ /* open it read/write */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb );
+ ok(r == ERROR_SUCCESS , "Failed to open database r/w\n");
+
+ /* a bunch of test queries that fail with the native MSI */
+
+ r = try_query( hdb, "CREATE TABLE");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2a return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a`");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2b return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` ()");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2c return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2d return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) )");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2e return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2f return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2g return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2h return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2i return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY 'b')");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2j return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2k return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2l return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHA(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2m return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(-1) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2n return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(720) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2o return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`` CHAR(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_SUCCESS , "valid query 2z failed\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "created same table again\n");
+
+ r = try_query( hdb, "CREATE TABLE `aa` (`b` CHAR(72) NOT NULL, `c` "
+ "CHAR(72), `d` CHAR(255) NOT NULL LOCALIZABLE PRIMARY KEY `b`)");
+ ok(r == ERROR_SUCCESS , "query 4 failed\n");
+
+ r = MsiDatabaseCommit( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
+
+ r = try_query( hdb, "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL "
+ "PRIMARY KEY `foo`)");
+ ok(r == ERROR_SUCCESS , "query 4 failed\n");
+
+ r = try_insert_query( hdb, "insert into a ( `b` ) VALUES ( ? )");
+ ok(r == ERROR_SUCCESS , "failed to insert record in db\n");
+
+ r = MsiDatabaseCommit( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
+
+ r = try_query( hdb, "CREATE TABLE `boo` (`foo` CHAR(72) NOT NULL "
+ "PRIMARY KEY `ba`)");
+ ok(r != ERROR_SUCCESS , "query 5 succeeded\n");
+
+ r = try_query( hdb,"CREATE TABLE `bee` (`foo` CHAR(72) NOT NULL )");
+ ok(r != ERROR_SUCCESS , "query 6 succeeded\n");
+
+ r = try_query( hdb, "CREATE TABLE `temp` (`t` CHAR(72) NOT NULL "
+ "PRIMARY KEY `t`)");
+ ok(r == ERROR_SUCCESS , "query 7 failed\n");
+
+ r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_SUCCESS , "query 8 failed\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to close database transact\n");
+
+ r = DeleteFile( msifile );
+ ok(r == TRUE, "file didn't exist after commit\n");
+}
+
+static UINT run_query( MSIHANDLE hdb, const char *query )
+{
+ MSIHANDLE hview = 0;
+ UINT r;
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, 0);
+ if( r == ERROR_SUCCESS )
+ r = MsiViewClose(hview);
+ MsiCloseHandle(hview);
+ return r;
+}
+
+static void test_viewmodify(void)
+{
+ MSIHANDLE hdb = 0, hview = 0, hrec = 0;
+ UINT r;
+ const char *query;
+ char buffer[0x100];
+ DWORD sz;
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ query = "CREATE TABLE `phone` ( "
+ "`id` INT, `name` CHAR(32), `number` CHAR(32) "
+ "PRIMARY KEY `id`)";
+ r = run_query( hdb, query );
+ ok(r == ERROR_SUCCESS, "query failed\n");
+
+ /* check what the error function reports without doing anything */
+ sz = 0;
+ /* passing NULL as the 3rd param make function to crash on older platforms */
+ r = MsiViewGetError( 0, NULL, &sz );
+ ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
+
+ /* open a view */
+ query = "SELECT * FROM `phone`";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ /* see what happens with a good hview and bad args */
+ r = MsiViewGetError( hview, NULL, NULL );
+ ok(r == MSIDBERROR_INVALIDARG || r == MSIDBERROR_NOERROR,
+ "MsiViewGetError returns %u (expected -3)\n", r);
+ r = MsiViewGetError( hview, buffer, NULL );
+ ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
+
+ /* see what happens with a zero length buffer */
+ sz = 0;
+ buffer[0] = 'x';
+ r = MsiViewGetError( hview, buffer, &sz );
+ ok(r == MSIDBERROR_MOREDATA, "MsiViewGetError return\n");
+ ok(buffer[0] == 'x', "buffer cleared\n");
+ ok(sz == 0, "size not zero\n");
+
+ /* ok this one is strange */
+ sz = 0;
+ r = MsiViewGetError( hview, NULL, &sz );
+ ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
+ ok(sz == 0, "size not zero\n");
+
+ /* see if it really has an error */
+ sz = sizeof buffer;
+ buffer[0] = 'x';
+ r = MsiViewGetError( hview, buffer, &sz );
+ ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
+ ok(buffer[0] == 0, "buffer not cleared\n");
+ ok(sz == 0, "size not zero\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+
+ /* try some invalid records */
+ r = MsiViewModify(hview, MSIMODIFY_INSERT, 0 );
+ ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
+ r = MsiViewModify(hview, -1, 0 );
+ ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
+
+ /* try an small record */
+ hrec = MsiCreateRecord(1);
+ r = MsiViewModify(hview, -1, hrec );
+ ok(r == ERROR_INVALID_DATA, "MsiViewModify failed\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "failed to close record\n");
+
+ /* insert a valid record */
+ hrec = MsiCreateRecord(3);
+
+ r = MsiRecordSetInteger(hrec, 2, 1);
+ ok(r == ERROR_SUCCESS, "failed to set integer\n");
+ r = MsiRecordSetString(hrec, 2, "bob");
+ ok(r == ERROR_SUCCESS, "failed to set integer\n");
+ r = MsiRecordSetString(hrec, 3, "7654321");
+ ok(r == ERROR_SUCCESS, "failed to set integer\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
+ ok(r == ERROR_SUCCESS, "MsiViewModify failed\n");
+
+ /* insert the same thing again */
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+
+ /* should fail ... */
+ todo_wine {
+ r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
+ ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n");
+ }
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "failed to close record\n");
+
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
+}
+
+static MSIHANDLE create_db(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+
+ DeleteFile(msifile);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+ if( res != ERROR_SUCCESS )
+ return hdb;
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ return hdb;
+}
+
+static void test_getcolinfo(void)
+{
+ MSIHANDLE hdb, hview = 0, rec = 0;
+ UINT r;
+ DWORD sz;
+ char buffer[0x20];
+
+ /* create an empty db */
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ /* tables should be present */
+ r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview);
+ ok( r == ERROR_SUCCESS, "failed to open query\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok( r == ERROR_SUCCESS, "failed to execute query\n");
+
+ /* check that NAMES works */
+ rec = 0;
+ r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec );
+ ok( r == ERROR_SUCCESS, "failed to get names\n");
+ sz = sizeof buffer;
+ r = MsiRecordGetString(rec, 1, buffer, &sz );
+ ok( r == ERROR_SUCCESS, "failed to get string\n");
+ ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n");
+ r = MsiCloseHandle( rec );
+ ok( r == ERROR_SUCCESS, "failed to close record handle\n");
+
+ /* check that TYPES works */
+ rec = 0;
+ r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec );
+ ok( r == ERROR_SUCCESS, "failed to get names\n");
+ sz = sizeof buffer;
+ r = MsiRecordGetString(rec, 1, buffer, &sz );
+ ok( r == ERROR_SUCCESS, "failed to get string\n");
+ ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n");
+ r = MsiCloseHandle( rec );
+ ok( r == ERROR_SUCCESS, "failed to close record handle\n");
+
+ /* check that invalid values fail */
+ rec = 0;
+ r = MsiViewGetColumnInfo( hview, 100, &rec );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
+ ok( rec == 0, "returned a record\n");
+
+ r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
+
+ r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec );
+ ok( r == ERROR_INVALID_HANDLE, "wrong error code\n");
+
+ r = MsiViewClose(hview);
+ ok( r == ERROR_SUCCESS, "failed to close view\n");
+ r = MsiCloseHandle(hview);
+ ok( r == ERROR_SUCCESS, "failed to close view handle\n");
+ r = MsiCloseHandle(hdb);
+ ok( r == ERROR_SUCCESS, "failed to close database\n");
+}
+
+static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type)
+{
+ MSIHANDLE hview = 0, rec = 0;
+ UINT r;
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, 0);
+ if( r == ERROR_SUCCESS )
+ {
+ MsiViewGetColumnInfo( hview, type, &rec );
+ MsiViewClose(hview);
+ }
+ MsiCloseHandle(hview);
+ return rec;
+}
+
+static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field)
+{
+ MSIHANDLE hview = 0, rec = 0;
+ UINT r, type = 0;
+ char query[0x100];
+
+ sprintf(query, "select * from `_Columns` where `Table` = '%s'", table );
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, 0);
+ if( r == ERROR_SUCCESS )
+ {
+ while (1)
+ {
+ r = MsiViewFetch( hview, &rec );
+ if( r != ERROR_SUCCESS)
+ break;
+ r = MsiRecordGetInteger( rec, 2 );
+ if (r == field)
+ type = MsiRecordGetInteger( rec, 4 );
+ MsiCloseHandle( rec );
+ }
+
+ MsiViewClose(hview);
+ }
+ MsiCloseHandle(hview);
+ return type;
+}
+
+static BOOL check_record( MSIHANDLE rec, UINT field, LPSTR val )
+{
+ CHAR buffer[0x20];
+ UINT r;
+ DWORD sz;
+
+ sz = sizeof buffer;
+ r = MsiRecordGetString( rec, field, buffer, &sz );
+ return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
+}
+
+static void test_viewgetcolumninfo(void)
+{
+ MSIHANDLE hdb = 0, rec;
+ UINT r;
+
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ r = run_query( hdb,
+ "CREATE TABLE `Properties` "
+ "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ /* check the column types */
+ rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "S255"), "wrong record type\n");
+ ok( check_record( rec, 2, "S1"), "wrong record type\n");
+
+ MsiCloseHandle( rec );
+
+ /* check the type in _Columns */
+ ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n");
+ ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n");
+
+ /* now try the names */
+ rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "Property"), "wrong record type\n");
+ ok( check_record( rec, 2, "Value"), "wrong record type\n");
+
+ MsiCloseHandle( rec );
+
+ r = run_query( hdb,
+ "CREATE TABLE `Binary` "
+ "( `Name` CHAR(255), `Data` OBJECT PRIMARY KEY `Name`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ /* check the column types */
+ rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "S255"), "wrong record type\n");
+ ok( check_record( rec, 2, "V0"), "wrong record type\n");
+
+ MsiCloseHandle( rec );
+
+ /* check the type in _Columns */
+ ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n");
+ ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n");
+
+ /* now try the names */
+ rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "Name"), "wrong record type\n");
+ ok( check_record( rec, 2, "Data"), "wrong record type\n");
+ MsiCloseHandle( rec );
+
+ r = run_query( hdb,
+ "CREATE TABLE `UIText` "
+ "( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n");
+ ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n");
+
+ rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+ ok( check_record( rec, 1, "Key"), "wrong record type\n");
+ ok( check_record( rec, 2, "Text"), "wrong record type\n");
+ MsiCloseHandle( rec );
+
+ rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+ ok( check_record( rec, 1, "s72"), "wrong record type\n");
+ ok( check_record( rec, 2, "L255"), "wrong record type\n");
+ MsiCloseHandle( rec );
+
+ MsiCloseHandle( hdb );
+}
+
+static void test_msiexport(void)
+{
+ MSIHANDLE hdb = 0, hview = 0;
+ UINT r;
+ const char *query;
+ char path[MAX_PATH];
+ const char file[] = "phone.txt";
+ HANDLE handle;
+ char buffer[0x100];
+ DWORD length;
+ const char expected[] =
+ "id\tname\tnumber\r\n"
+ "I2\tS32\tS32\r\n"
+ "phone\tid\r\n"
+ "1\tAbe\t8675309\r\n";
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ /* create a table */
+ query = "CREATE TABLE `phone` ( "
+ "`id` INT, `name` CHAR(32), `number` CHAR(32) "
+ "PRIMARY KEY `id`)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* insert a value into it */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES('1', 'Abe', '8675309')";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ GetCurrentDirectory(MAX_PATH, path);
+
+ todo_wine {
+ r = MsiDatabaseExport(hdb, "phone", path, file);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n");
+
+ MsiCloseHandle(hdb);
+
+ lstrcat(path, "\\");
+ lstrcat(path, file);
+
+ /* check the data that was written */
+ length = 0;
+ memset(buffer, 0, sizeof buffer);
+ handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ ReadFile(handle, buffer, sizeof buffer, &length, NULL);
+ CloseHandle(handle);
+ DeleteFile(path);
+ }
+ else
+ ok(0, "failed to open file %s\n", path);
+
+ ok( length == strlen(expected), "length of data wrong\n");
+ ok( !lstrcmp(buffer, expected), "data doesn't match\n");
+ }
+ DeleteFile(msifile);
+}
+
+static void test_longstrings(void)
+{
+ const char insert_query[] =
+ "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')";
+ char *str;
+ MSIHANDLE hdb = 0, hview = 0, hrec = 0;
+ DWORD len;
+ UINT r;
+ const DWORD STRING_LENGTH = 0x10005;
+
+ DeleteFile(msifile);
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ /* create a table */
+ r = try_query( hdb,
+ "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)");
+ ok(r == ERROR_SUCCESS, "query failed\n");
+
+ /* try a insert a very long string */
+ str = HeapAlloc(GetProcessHeap(), 0, STRING_LENGTH+sizeof insert_query);
+ len = strchr(insert_query, 'Z') - insert_query;
+ strcpy(str, insert_query);
+ memset(str+len, 'Z', STRING_LENGTH);
+ strcpy(str+len+STRING_LENGTH, insert_query+len+1);
+ r = try_query( hdb, str );
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ HeapFree(GetProcessHeap(), 0, str);
+
+ MsiDatabaseCommit(hdb);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
+ MsiCloseHandle(hdb);
+
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ r = MsiDatabaseOpenView(hdb, "select * from `strings` where `id` = 1", &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+
+ r = MsiViewFetch(hview, &hrec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
+
+ MsiCloseHandle(hview);
+
+ r = MsiRecordGetString(hrec, 2, NULL, &len);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
+ todo_wine {
+ ok(len == STRING_LENGTH, "string length wrong\n");
+ }
+
+ MsiCloseHandle(hrec);
+ MsiCloseHandle(hdb);
+ DeleteFile(msifile);
+}
+
+static void test_streamtable(void)
+{
+ MSIHANDLE hdb = 0, rec;
+ UINT r;
+
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ r = run_query( hdb,
+ "CREATE TABLE `Properties` "
+ "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ /* check the column types */
+ rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+
+ todo_wine {
+ ok( check_record( rec, 1, "s62"), "wrong record type\n");
+ ok( check_record( rec, 2, "V0"), "wrong record type\n");
+ }
+
+ MsiCloseHandle( rec );
+
+ /* now try the names */
+ rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+
+ todo_wine {
+ ok( check_record( rec, 1, "Name"), "wrong record type\n");
+ ok( check_record( rec, 2, "Data"), "wrong record type\n");
+ }
+
+ MsiCloseHandle( rec );
+ MsiCloseHandle( hdb );
+ DeleteFile(msifile);
+}
+
+static void test_where(void)
+{
+ MSIHANDLE hdb = 0, rec;
+ LPCSTR query;
+ UINT r;
+
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ r = run_query( hdb,
+ "CREATE TABLE `Media` ("
+ "`DiskId` SHORT NOT NULL, "
+ "`LastSequence` LONG, "
+ "`DiskPrompt` CHAR(64) LOCALIZABLE, "
+ "`Cabinet` CHAR(255), "
+ "`VolumeLabel` CHAR(32), "
+ "`Source` CHAR(72) "
+ "PRIMARY KEY `DiskId`)" );
+ ok( r == S_OK, "cannot create Media table: %d\n", r );
+
+ r = run_query( hdb, "INSERT INTO `Media` "
+ "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
+ "VALUES ( 1, 0, '', 'zero.cab', '', '' )" );
+ ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
+
+ r = run_query( hdb, "INSERT INTO `Media` "
+ "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
+ "VALUES ( 2, 1, '', 'one.cab', '', '' )" );
+ ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
+
+ r = run_query( hdb, "INSERT INTO `Media` "
+ "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
+ "VALUES ( 3, 2, '', 'two.cab', '', '' )" );
+ ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
+
+ query = "SELECT * FROM `Media`";
+ r = do_query(hdb, query, &rec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
+ ok( check_record( rec, 4, "zero.cab"), "wrong cabinet\n");
+ MsiCloseHandle( rec );
+
+ query = "SELECT * FROM `Media` WHERE `LastSequence` >= 1";
+ r = do_query(hdb, query, &rec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
+ ok( check_record( rec, 4, "one.cab"), "wrong cabinet\n");
+
+ r = MsiRecordGetInteger(rec, 1);
+ ok( 2 == r, "field wrong\n");
+ r = MsiRecordGetInteger(rec, 2);
+ ok( 1 == r, "field wrong\n");
+
+ MsiCloseHandle( rec );
+
+ MsiCloseHandle( hdb );
+ DeleteFile(msifile);
+}
+
+START_TEST(db)
+{
+ test_msidatabase();
+ test_msiinsert();
+ test_msidecomposedesc();
+ test_msibadqueries();
+ test_viewmodify();
+ test_viewgetcolumninfo();
+ test_getcolinfo();
+ test_msiexport();
+ test_longstrings();
+ test_streamtable();
+ test_where();
+}
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ * Copyright (C) 2005 Aric Stewart for CodeWeavers
+ *
+ * A test program for MSI record formatting
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static MSIHANDLE helper_createpackage( const char *szName )
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+ CHAR szPackage[10];
+ MSIHANDLE hPackage;
+ MSIHANDLE suminfo;
+
+ DeleteFile(szName);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(szName, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ /* build summmary info */
+ res = MsiGetSummaryInformation(hdb, NULL, 7, &suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to open summaryinfo\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,2, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,3, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,4, VT_LPSTR, 0,NULL,
+ "Wine Hackers");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,7, VT_LPSTR, 0,NULL,
+ ";1033");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,9, VT_LPSTR, 0,NULL,
+ "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 14, VT_I4, 100, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 15, VT_I4, 0, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoPersist(suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to make summary info persist\n" );
+
+ res = MsiCloseHandle( suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to close suminfo\n" );
+
+ sprintf(szPackage,"#%li",(DWORD)hdb);
+ res = MsiOpenPackage(szPackage,&hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to open package\n" );
+
+ res = MsiCloseHandle( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to close database\n" );
+
+ return hPackage;
+}
+
+static void test_createpackage(void)
+{
+ static const CHAR filename[] = "winetest.msi";
+ MSIHANDLE hPackage = 0;
+ UINT res;
+
+ hPackage = helper_createpackage( filename );
+ ok ( hPackage != 0, " Failed to create package\n");
+
+ res = MsiCloseHandle( hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to close package\n" );
+
+ DeleteFile( filename );
+}
+
+static void test_formatrecord(void)
+{
+ char buffer[100];
+ MSIHANDLE hrec;
+ UINT r;
+ DWORD sz;
+
+ r = MsiFormatRecord(0, 0, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong error\n");
+
+ hrec = MsiCreateRecord(4);
+ ok( hrec, "failed to create record\n");
+
+ /* format an empty record */
+ r = MsiFormatRecord(0, hrec, NULL, NULL );
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ sz=0;
+ r = MsiFormatRecord(0, hrec, buffer+1, &sz);
+ ok( r == ERROR_MORE_DATA && buffer[0] == 'x', "format failed measuring with buffer\n");
+ ok( sz == 16, "size wrong\n");
+ sz=100;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 16, "size wrong\n");
+ ok( 0 == strcmp(buffer,"1: 2: 3: 4: "), "wrong output\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r==ERROR_SUCCESS, "Unable to close record\n");
+
+ hrec = MsiCreateRecord(6);
+ ok( hrec, "failed to create record\n");
+
+ sz = 100;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 24, "size wrong\n");
+ ok( 0 == strcmp(buffer,"1: 2: 3: 4: 5: 6: "), "wrong output\n");
+
+
+ /* format a format string with everything else empty */
+ r = MsiRecordSetString(hrec, 0, "%1");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ r = MsiFormatRecord(0, hrec, NULL, NULL );
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ r = MsiFormatRecord(0, hrec, buffer, NULL);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
+
+ sz = 123;
+ r = MsiFormatRecord(0, hrec, NULL, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong (%li)\n",sz);
+ sz = sizeof buffer;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"%1"), "wrong output\n");
+
+ /* make the buffer too small */
+ sz = 0;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_MORE_DATA, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"x"), "wrong output\n");
+
+ /* make the buffer a little bit bigger */
+ sz = 1;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_MORE_DATA, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output (%s)\n",buffer);
+
+ /* and again */
+ sz = 2;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_MORE_DATA, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"%"), "wrong output\n");
+
+ /* and again */
+ sz = 3;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"%1"), "wrong output\n");
+
+ /* now try a real format string */
+ r = MsiRecordSetString(hrec, 0, "[1]");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+
+ /* now put something in the first field */
+ r = MsiRecordSetString(hrec, 1, "boo hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+
+ /* now put something in the first field */
+ r = MsiRecordSetString(hrec, 0, "[1] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+
+
+ /* empty string */
+ r = MsiRecordSetString(hrec, 0, "");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 30, "size wrong %li\n",sz);
+ ok( 0 == strcmp(buffer,"1: boo 2: hoo 3: 4: 5: 6: "),
+ "wrong output(%s)\n",buffer);
+
+ /* play games with recursive lookups */
+ r = MsiRecordSetString(hrec, 0, "[[1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"hey hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "[2]");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[[2]] hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[[3]]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"hey hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiCloseHandle(hrec);
+ ok(r==ERROR_SUCCESS, "Unable to close record\n");
+ hrec = MsiCreateRecord(12);
+ ok( hrec, "failed to create record\n");
+
+ r = MsiRecordSetString(hrec, 0, "[[3][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"big hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[3][4][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"big hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[3][[4]][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 10, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[1[]2] hey"), "wrong output (%s)\n",buffer);
+
+ /* incorrect formats */
+ r = MsiRecordSetString(hrec, 0, "[[[3][[4]][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[[[3][[4]][1]] [2]"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[3][[4]][1]] [2]]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 11, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[1[]2] hey]"), "wrong output (%s)\n",buffer);
+
+
+ /* play games with {} */
+
+ r = MsiRecordSetString(hrec, 0, "{[3][1]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 6, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"12 hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[{[3][1]}] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 8, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[12] hey"), "wrong output (%s)\n",buffer);
+
+
+ r = MsiRecordSetString(hrec, 0, "{test} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 10, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{test} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[test]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{[test]} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2][3][4]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 4, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer," hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2][3][dummy]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{2hey1[dummy]} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2][3][4][dummy]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{2hey1[dummy]} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{{[1][2]}[3][4][dummy]}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine{
+ ok( sz == 16, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{{2hey}1[dummy]}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1][2]}[3]{[4][dummy]}}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 0, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,""), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 12, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2]} {{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 15, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"12 {{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{[4]}{[1][2]} {{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 15, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"12 {{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+
+ r = MsiRecordSetString(hrec, 0, "{blah} {[4]}{[1][2]} {{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 22, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{blah} 12 {{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1]}[2]} {[4]}{[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 13, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{{1}2} {}{12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1]}} {[4]}{[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 3, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer," 12"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{{[1]}} {[4]}{[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 3, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer," 12"), "wrong output (%s)\n",buffer);
+ }
+
+ /* now put play games with escaping */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\3asdf]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 16, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo [\\3asdf]"), "wrong output\n");
+
+ /* now put play games with escaping */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\x]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo [\\x]"), "wrong output\n");
+
+ /* now try other formats without a package */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [property]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo [property]"), "wrong output\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1] [~] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 11, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo [~] hoo"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1]");
+ r = MsiRecordSetInteger(hrec, 1, 123456);
+ ok( r == ERROR_SUCCESS, "set integer failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"123456"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "[~]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[~]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ /* MsiFormatRecord doesn't seem to handle a negative too well */
+ r = MsiRecordSetString(hrec, 0, "[-1]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[-1]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[]}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[]}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[0]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[0]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[100]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1] [2]}");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{foo}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{foo}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{boo [1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]}}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{ {[1]}}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( 0 == strcmp(buffer," {hoo}"), "wrong output\n");
+ ok( sz == 6, "size wrong\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]} }");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{hoo} }"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ [1]}}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1] }}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a}{b}{c }{ d}{any text}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{{a} }");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{a} }"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a} {b}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a} b}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a b}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{ }");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{ }"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, " {{a}}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer," }"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ almost {{ any }} text }}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer," text }}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ } { hidden ][ [ }}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[ 1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[ 1]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[01]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{test}} [01");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer," [01"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[\\[]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[\\[]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[foo]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[foo]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[01.]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[01.]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ SetEnvironmentVariable("FOO", "BAR");
+ r = MsiRecordSetString(hrec, 0, "[%FOO]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[%FOO]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ {[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ {[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ {{[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {{hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{{ {{a}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {{a}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{{ {{a}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {{a}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "0{1{2{3{4[1]5}6}7}8}9");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 19, "size wrong\n");
+ ok( 0 == strcmp(buffer,"01{2{3{4hoo56}7}8}9"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "0{1{2[1]3}4");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"01{2hoo34"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "0{1{2[1]3}4");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"01{2hoo34"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1.} [1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[1.} hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[{[1]}]}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[{[1]}]}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1][}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"2["), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1]");
+ r = MsiRecordSetString(hrec, 1, "[2]");
+ r = MsiRecordSetString(hrec, 2, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[2]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "[{{boo}}1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[1]"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "[{{boo}}1]");
+ r = MsiRecordSetString(hrec, 0, "[1{{boo}}]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[1]"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1]{{boo} }}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 11, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo{{boo }}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1{{boo}}]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 12, "size wrong: got %lu, expected 12\n", sz);
+ ok( 0 == strcmp(buffer,"{[1{{boo}}]}"), "wrong output: got %s, expected [1]\n", buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong: got %lu, expected 3\n", sz);
+ ok( 0 == strcmp(buffer,"{{hoo}"), "wrong output: got %s, expected [1]\n", buffer);
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1{{bo}o}}]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 13, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[1{{bo}o}}]}"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1{{b{o}o}}]}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 14, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[1{{b{o}o}}]}"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{ {[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer," {hoo"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ /* {} inside a substitution does strange things... */
+ r = MsiRecordSetString(hrec, 0, "[[1]{}]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[[1]]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[[1]{}[1]]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[[1]2]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[a[1]b[1]c{}d[1]e]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 14, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[a[1]b[1]cd2e]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[a[1]b");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[a[1]b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "a[1]b]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"a2b]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "]a[1]b");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"]a2b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "]a[1]b");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"]a2b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "\\[1]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"\\2"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "\\{[1]}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"\\2"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "a{b[1]c}d");
+ r = MsiRecordSetString(hrec, 1, NULL);
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"ad"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{a[0]b}");
+ r = MsiRecordSetString(hrec, 1, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"a{a[0]b}b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[foo]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[foo]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1][-1][1]}");
+ r = MsiRecordSetString(hrec, 1, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 12, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{foo[-1]foo}"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ MsiCloseHandle( hrec );
+}
+
+static void test_formatrecord_package(void)
+{
+ static const CHAR filename[] = "winetest.msi";
+ char buffer[100];
+ MSIHANDLE hrec;
+ MSIHANDLE package;
+ UINT r;
+ DWORD sz=100;
+
+ package = helper_createpackage( filename );
+ ok(package!=0, "Unable to create package\n");
+
+ hrec = MsiCreateRecord(12);
+ ok( hrec, "failed to create record\n");
+
+ r = MsiFormatRecord(package, 0, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong error\n");
+
+ r = MsiFormatRecord(package, hrec, NULL, NULL );
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec,0,NULL);
+ r = MsiRecordSetString(hrec,1,NULL);
+ r = MsiRecordSetString(hrec,2,NULL);
+ r = MsiRecordSetString(hrec,3,NULL);
+ r = MsiRecordSetString(hrec,4,NULL);
+ r = MsiRecordSetString(hrec,5,NULL);
+ r = MsiRecordSetString(hrec,6,NULL);
+ r = MsiRecordSetString(hrec,7,NULL);
+ r = MsiRecordSetString(hrec,8,NULL);
+ r = MsiRecordSetString(hrec,9,NULL);
+ r = MsiRecordSetString(hrec,10,NULL);
+ r = MsiRecordSetString(hrec,11,NULL);
+ r = MsiRecordSetString(hrec,12,NULL);
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer (%i)\n",r);
+ ok( sz == 51, "size wrong (%li)\n",sz);
+ ok( 0 == strcmp(buffer,"1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: "), "wrong output(%s)\n",buffer);
+
+ /* now put play games with escaping */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\3asdf]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo 3"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\x]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo x"), "wrong output (%s)\n",buffer);
+
+ /* null characters */
+ r = MsiRecordSetString(hrec, 0, "[1] [~] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo "), "wrong output\n");
+
+ /* properties */
+ r = MsiSetProperty(package,"dummy","Bork");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[1] [dummy] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo Bork hoo"), "wrong output\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1] [invalid] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+
+
+ /* nesting tests */
+ r = MsiSetProperty(package,"dummya","foo");
+ r = MsiSetProperty(package,"dummyb","baa");
+ r = MsiSetProperty(package,"adummyc","whoa");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[dummy[1]] [dummy[2]] [[1]dummy[3]]");
+ r = MsiRecordSetString(hrec, 1, "a");
+ r = MsiRecordSetString(hrec, 2, "b");
+ r = MsiRecordSetString(hrec, 3, "c");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"foo baa whoa"), "wrong output (%s)\n",buffer);
+
+
+ r = MsiSetProperty(package,"dummya","1");
+ r = MsiSetProperty(package,"dummyb","[2]");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[dummya] [[dummya]] [dummyb]");
+ r = MsiRecordSetString(hrec, 1, "aaa");
+ r = MsiRecordSetString(hrec, 2, "bbb");
+ r = MsiRecordSetString(hrec, 3, "ccc");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"1 [1] [2]"), "wrong output (%s)\n",buffer);
+
+ r = MsiSetProperty(package,"dummya","1");
+ r = MsiSetProperty(package,"dummyb","a");
+ r = MsiSetProperty(package,"dummyc","\\blath");
+ r = MsiSetProperty(package,"dummyd","[\\blath]");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[dummyc] [[dummyc]] [dummy[dummyb]]");
+ r = MsiRecordSetString(hrec, 1, "aaa");
+ r = MsiRecordSetString(hrec, 2, "bbb");
+ r = MsiRecordSetString(hrec, 3, "ccc");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 10, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"\\blath b 1"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [[\\3asdf]]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ r = MsiRecordSetString(hrec, 3, "yeah");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 11, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo [3]"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [[3]]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ r = MsiRecordSetString(hrec, 3, "\\help");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo h"), "wrong output (%s)\n",buffer);
+
+ MsiCloseHandle(hrec);
+
+ r = MsiCloseHandle(package);
+ ok(r==ERROR_SUCCESS, "Unable to close package\n");
+
+ DeleteFile( filename );
+}
+
+START_TEST(format)
+{
+ test_createpackage();
+ test_formatrecord();
+ test_formatrecord_package();
+}
--- /dev/null
+/*
+ * Copyright (C) 2006 James Hawkins
+ *
+ * A test program for installing MSI products.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+
+#include <windows.h>
+#include <msiquery.h>
+#include <msidefs.h>
+#include <msi.h>
+#include <fci.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static const char *msifile = "winetest.msi";
+CHAR CURR_DIR[MAX_PATH];
+CHAR PROG_FILES_DIR[MAX_PATH];
+
+/* msi database data */
+
+static const CHAR admin_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+ "s72\tS255\tI2\n"
+ "AdminExecuteSequence\tAction\n"
+ "CostFinalize\t\t1000\n"
+ "CostInitialize\t\t800\n"
+ "FileCost\t\t900\n"
+ "InstallAdminPackage\t\t3900\n"
+ "InstallFiles\t\t4000\n"
+ "InstallFinalize\t\t6600\n"
+ "InstallInitialize\t\t1500\n"
+ "InstallValidate\t\t1400";
+
+static const CHAR advt_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+ "s72\tS255\tI2\n"
+ "AdvtExecuteSequence\tAction\n"
+ "CostFinalize\t\t1000\n"
+ "CostInitialize\t\t800\n"
+ "CreateShortcuts\t\t4500\n"
+ "InstallFinalize\t\t6600\n"
+ "InstallInitialize\t\t1500\n"
+ "InstallValidate\t\t1400\n"
+ "PublishComponents\t\t6200\n"
+ "PublishFeatures\t\t6300\n"
+ "PublishProduct\t\t6400\n"
+ "RegisterClassInfo\t\t4600\n"
+ "RegisterExtensionInfo\t\t4700\n"
+ "RegisterMIMEInfo\t\t4900\n"
+ "RegisterProgIdInfo\t\t4800";
+
+static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
+ "s72\tS38\ts72\ti2\tS255\tS72\n"
+ "Component\tComponent\n"
+ "Five\t{8CC92E9D-14B2-4CA4-B2AA-B11D02078087}\tNEWDIR\t2\t\tfive.txt\n"
+ "Four\t{FD37B4EA-7209-45C0-8917-535F35A2F080}\tCABOUTDIR\t2\t\tfour.txt\n"
+ "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n"
+ "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n"
+ "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n"
+ "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata";
+
+static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n"
+ "s72\tS72\tl255\n"
+ "Directory\tDirectory\n"
+ "CABOUTDIR\tMSITESTDIR\tcabout\n"
+ "CHANGEDDIR\tMSITESTDIR\tchanged:second\n"
+ "FIRSTDIR\tMSITESTDIR\tfirst\n"
+ "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
+ "NEWDIR\tCABOUTDIR\tnew\n"
+ "ProgramFilesFolder\tTARGETDIR\t.\n"
+ "TARGETDIR\t\tSourceDir";
+
+static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
+ "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
+ "Feature\tFeature\n"
+ "Five\t\tFive\tThe Five Feature\t5\t3\tNEWDIR\t0\n"
+ "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n"
+ "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n"
+ "Three\t\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n"
+ "Two\t\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0";
+
+static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n"
+ "s38\ts72\n"
+ "FeatureComponents\tFeature_\tComponent_\n"
+ "Five\tFive\n"
+ "Four\tFour\n"
+ "One\tOne\n"
+ "Three\tThree\n"
+ "Two\tTwo";
+
+static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
+ "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
+ "File\tFile\n"
+ "five.txt\tFive\tfive.txt\t1000\t\t\t16384\t5\n"
+ "four.txt\tFour\tfour.txt\t1000\t\t\t16384\t4\n"
+ "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n"
+ "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n"
+ "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2";
+
+static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+ "s72\tS255\tI2\n"
+ "InstallExecuteSequence\tAction\n"
+ "AllocateRegistrySpace\tNOT Installed\t1550\n"
+ "CostFinalize\t\t1000\n"
+ "CostInitialize\t\t800\n"
+ "FileCost\t\t900\n"
+ "InstallFiles\t\t4000\n"
+ "InstallFinalize\t\t6600\n"
+ "InstallInitialize\t\t1500\n"
+ "InstallValidate\t\t1400\n"
+ "LaunchConditions\t\t100\n"
+ "WriteRegistryValues\t\t5000";
+
+static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
+ "i2\ti4\tL64\tS255\tS32\tS72\n"
+ "Media\tDiskId\n"
+ "1\t3\t\t\tDISK1\t\n"
+ "2\t5\t\tmsitest.cab\tDISK2\t\n";
+
+static const CHAR property_dat[] = "Property\tValue\n"
+ "s72\tl0\n"
+ "Property\tProperty\n"
+ "DefaultUIFont\tDlgFont8\n"
+ "INSTALLLEVEL\t3\n"
+ "InstallMode\tTypical\n"
+ "Manufacturer\tWine\n"
+ "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n"
+ "ProductCode\t{F1C3AF50-8B56-4A69-A00C-00773FE42F30}\n"
+ "ProductID\tnone\n"
+ "ProductLanguage\t1033\n"
+ "ProductName\tMSITEST\n"
+ "ProductVersion\t1.1.1\n"
+ "PROMPTROLLBACKCOST\tP\n"
+ "Setup\tSetup\n"
+ "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}";
+
+static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"
+ "s72\ti2\tl255\tL255\tL0\ts72\n"
+ "Registry\tRegistry\n"
+ "Apples\t2\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n"
+ "Oranges\t2\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n"
+ "regdata\t2\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler";
+
+typedef struct _msi_table
+{
+ const CHAR *filename;
+ const CHAR *data;
+ int size;
+} msi_table;
+
+#define ADD_TABLE(x) {#x".idt", x##_dat, sizeof(x##_dat)}
+
+static const msi_table tables[] =
+{
+ ADD_TABLE(admin_exec_seq),
+ ADD_TABLE(advt_exec_seq),
+ ADD_TABLE(component),
+ ADD_TABLE(directory),
+ ADD_TABLE(feature),
+ ADD_TABLE(feature_comp),
+ ADD_TABLE(file),
+ ADD_TABLE(install_exec_seq),
+ ADD_TABLE(media),
+ ADD_TABLE(property),
+ ADD_TABLE(registry)
+};
+
+/* cabinet definitions */
+
+/* make the max size large so there is only one cab file */
+#define MEDIA_SIZE 999999999
+#define FOLDER_THRESHOLD 900000
+
+/* The following defintions were copied from dlls/cabinet/cabinet.h
+ * because they are undocumented in windows.
+ */
+
+/* EXTRACTdest flags */
+#define EXTRACT_FILLFILELIST 0x00000001
+#define EXTRACT_EXTRACTFILES 0x00000002
+
+struct ExtractFileList {
+ LPSTR filename;
+ struct ExtractFileList *next;
+ BOOL unknown; /* always 1L */
+};
+
+/* the first parameter of the function extract */
+typedef struct {
+ long result1; /* 0x000 */
+ long unknown1[3]; /* 0x004 */
+ struct ExtractFileList *filelist; /* 0x010 */
+ long filecount; /* 0x014 */
+ long flags; /* 0x018 */
+ char directory[0x104]; /* 0x01c */
+ char lastfile[0x20c]; /* 0x120 */
+} EXTRACTDEST;
+
+/* cabinet function pointers */
+HMODULE hCabinet;
+static HRESULT (WINAPI *pExtract)(EXTRACTDEST*, LPCSTR);
+
+/* the FCI callbacks */
+
+static void *mem_alloc(ULONG cb)
+{
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+static void mem_free(void *memory)
+{
+ HeapFree(GetProcessHeap(), 0, memory);
+}
+
+static BOOL get_next_cabinet(PCCAB pccab, ULONG cbPrevCab, void *pv)
+{
+ return TRUE;
+}
+
+static long progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
+{
+ return 0;
+}
+
+static int file_placed(PCCAB pccab, char *pszFile, long cbFile,
+ BOOL fContinuation, void *pv)
+{
+ return 0;
+}
+
+static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
+{
+ HANDLE handle;
+ DWORD dwAccess = 0;
+ DWORD dwShareMode = 0;
+ DWORD dwCreateDisposition = OPEN_EXISTING;
+
+ dwAccess = GENERIC_READ | GENERIC_WRITE;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+
+ if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
+ dwCreateDisposition = OPEN_EXISTING;
+ else
+ dwCreateDisposition = CREATE_NEW;
+
+ handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
+ dwCreateDisposition, 0, NULL);
+
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);
+
+ return (INT_PTR)handle;
+}
+
+static UINT fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD dwRead;
+ BOOL res;
+
+ res = ReadFile(handle, memory, cb, &dwRead, NULL);
+ ok(res, "Failed to ReadFile\n");
+
+ return dwRead;
+}
+
+static UINT fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD dwWritten;
+ BOOL res;
+
+ res = WriteFile(handle, memory, cb, &dwWritten, NULL);
+ ok(res, "Failed to WriteFile\n");
+
+ return dwWritten;
+}
+
+static int fci_close(INT_PTR hf, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ ok(CloseHandle(handle), "Failed to CloseHandle\n");
+
+ return 0;
+}
+
+static long fci_seek(INT_PTR hf, long dist, int seektype, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD ret;
+
+ ret = SetFilePointer(handle, dist, NULL, seektype);
+ ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
+
+ return ret;
+}
+
+static int fci_delete(char *pszFile, int *err, void *pv)
+{
+ BOOL ret = DeleteFileA(pszFile);
+ ok(ret, "Failed to DeleteFile %s\n", pszFile);
+
+ return 0;
+}
+
+static BOOL check_record(MSIHANDLE rec, UINT field, LPSTR val)
+{
+ CHAR buffer[0x20];
+ UINT r;
+ DWORD sz;
+
+ sz = sizeof buffer;
+ r = MsiRecordGetString(rec, field, buffer, &sz);
+ return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
+}
+
+static BOOL get_temp_file(char *pszTempName, int cbTempName, void *pv)
+{
+ LPSTR tempname;
+
+ tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
+ GetTempFileNameA(".", "xx", 0, tempname);
+
+ if (tempname && (strlen(tempname) < (unsigned)cbTempName))
+ {
+ lstrcpyA(pszTempName, tempname);
+ HeapFree(GetProcessHeap(), 0, tempname);
+ return TRUE;
+ }
+
+ HeapFree(GetProcessHeap(), 0, tempname);
+
+ return FALSE;
+}
+
+static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
+ USHORT *pattribs, int *err, void *pv)
+{
+ BY_HANDLE_FILE_INFORMATION finfo;
+ FILETIME filetime;
+ HANDLE handle;
+ DWORD attrs;
+ BOOL res;
+
+ handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);
+
+ res = GetFileInformationByHandle(handle, &finfo);
+ ok(res, "Expected GetFileInformationByHandle to succeed\n");
+
+ FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
+ FileTimeToDosDateTime(&filetime, pdate, ptime);
+
+ attrs = GetFileAttributes(pszName);
+ ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");
+
+ return (INT_PTR)handle;
+}
+
+static void add_file(HFCI hfci, char *file)
+{
+ char path[MAX_PATH];
+ BOOL res;
+
+ lstrcpyA(path, CURR_DIR);
+ lstrcatA(path, "\\");
+ lstrcatA(path, file);
+
+ res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
+ get_open_info, tcompTYPE_MSZIP);
+ ok(res, "Expected FCIAddFile to succeed\n");
+}
+
+static void set_cab_parameters(PCCAB pCabParams, const CHAR *name)
+{
+ ZeroMemory(pCabParams, sizeof(CCAB));
+
+ pCabParams->cb = MEDIA_SIZE;
+ pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
+ pCabParams->setID = 0xbeef;
+ lstrcpyA(pCabParams->szCabPath, CURR_DIR);
+ lstrcatA(pCabParams->szCabPath, "\\");
+ lstrcpyA(pCabParams->szCab, name);
+}
+
+static void create_cab_file(const CHAR *name)
+{
+ CCAB cabParams;
+ HFCI hfci;
+ ERF erf;
+ BOOL res;
+
+ set_cab_parameters(&cabParams, name);
+
+ hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
+ fci_read, fci_write, fci_close, fci_seek, fci_delete,
+ get_temp_file, &cabParams, NULL);
+
+ ok(hfci != NULL, "Failed to create an FCI context\n");
+
+ add_file(hfci, "four.txt");
+ add_file(hfci, "five.txt");
+
+ res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
+ ok(res, "Failed to flush the cabinet\n");
+
+ res = FCIDestroy(hfci);
+ ok(res, "Failed to destroy the cabinet\n");
+}
+
+static BOOL init_function_pointers(void)
+{
+ hCabinet = LoadLibraryA("cabinet.dll");
+ if (!hCabinet)
+ return FALSE;
+
+ pExtract = (void *)GetProcAddress(hCabinet, "Extract");
+ if (!pExtract)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL get_program_files_dir(LPSTR buf)
+{
+ HKEY hkey;
+ CHAR temp[MAX_PATH];
+ DWORD type = REG_EXPAND_SZ, size;
+
+ if (RegOpenKey(HKEY_LOCAL_MACHINE,
+ "Software\\Microsoft\\Windows\\CurrentVersion", &hkey))
+ return FALSE;
+
+ size = MAX_PATH;
+ if (RegQueryValueEx(hkey, "ProgramFilesPath", 0, &type, (LPBYTE)temp, &size))
+ return FALSE;
+
+ ExpandEnvironmentStrings(temp, buf, MAX_PATH);
+
+ RegCloseKey(hkey);
+ return TRUE;
+}
+
+static void create_file(const CHAR *name)
+{
+ HANDLE file;
+ DWORD written;
+
+ file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
+ WriteFile(file, name, strlen(name), &written, NULL);
+ WriteFile(file, "\n", strlen("\n"), &written, NULL);
+ CloseHandle(file);
+}
+
+static void create_test_files(void)
+{
+ int len;
+
+ GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+ len = lstrlenA(CURR_DIR);
+
+ if(len && (CURR_DIR[len-1] == '\\'))
+ CURR_DIR[len - 1] = 0;
+
+ get_program_files_dir(PROG_FILES_DIR);
+
+ CreateDirectoryA("msitest", NULL);
+ create_file("msitest\\one.txt");
+ CreateDirectoryA("msitest\\first", NULL);
+ create_file("msitest\\first\\two.txt");
+ CreateDirectoryA("msitest\\second", NULL);
+ create_file("msitest\\second\\three.txt");
+
+ create_file("four.txt");
+ create_file("five.txt");
+ create_cab_file("msitest.cab");
+
+ DeleteFileA("four.txt");
+ DeleteFileA("five.txt");
+}
+
+static BOOL delete_pf(const CHAR *rel_path, BOOL is_file)
+{
+ CHAR path[MAX_PATH];
+
+ lstrcpyA(path, PROG_FILES_DIR);
+ lstrcatA(path, "\\");
+ lstrcatA(path, rel_path);
+
+ if (is_file)
+ return DeleteFileA(path);
+ else
+ return RemoveDirectoryA(path);
+}
+
+static void delete_test_files(void)
+{
+ DeleteFileA("msitest.msi");
+ DeleteFileA("msitest.cab");
+ DeleteFileA("msitest\\second\\three.txt");
+ DeleteFileA("msitest\\first\\two.txt");
+ DeleteFileA("msitest\\one.txt");
+ RemoveDirectoryA("msitest\\second");
+ RemoveDirectoryA("msitest\\first");
+ RemoveDirectoryA("msitest");
+}
+
+static void write_file(const CHAR *filename, const char *data, int data_size)
+{
+ DWORD size;
+
+ HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ WriteFile(hf, data, data_size, &size, NULL);
+ CloseHandle(hf);
+}
+
+static void write_msi_summary_info(MSIHANDLE db)
+{
+ MSIHANDLE summary;
+ UINT r;
+
+ r = MsiGetSummaryInformationA(db, NULL, 4, &summary);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_TEMPLATE, VT_LPSTR, 0, NULL, ";1033");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_REVNUMBER, VT_LPSTR, 0, NULL,
+ "{004757CA-5092-49c2-AD20-28E1CE0DF5F2}");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_PAGECOUNT, VT_I4, 100, NULL, NULL);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_WORDCOUNT, VT_I4, 0, NULL, NULL);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ /* write the summary changes back to the stream */
+ r = MsiSummaryInfoPersist(summary);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ MsiCloseHandle(summary);
+}
+
+static void create_database(const CHAR *name, const msi_table *tables, int num_tables)
+{
+ MSIHANDLE db;
+ UINT r;
+ int j;
+
+ r = MsiOpenDatabaseA(name, MSIDBOPEN_CREATE, &db);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ /* import the tables into the database */
+ for (j = 0; j < num_tables; j++)
+ {
+ const msi_table *table = &tables[j];
+
+ write_file(table->filename, table->data, (table->size - 1) * sizeof(char));
+
+ r = MsiDatabaseImportA(db, CURR_DIR, table->filename);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+ }
+
+ DeleteFileA(table->filename);
+ }
+
+ write_msi_summary_info(db);
+
+ r = MsiDatabaseCommit(db);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ MsiCloseHandle(db);
+}
+
+static void test_MsiInstallProduct(void)
+{
+ UINT r;
+ CHAR path[MAX_PATH];
+ LONG res;
+ HKEY hkey;
+ DWORD num, size, type;
+
+ r = MsiInstallProductA(msifile, NULL);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+ }
+
+ todo_wine
+ {
+ ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest", FALSE), "File not installed\n");
+ }
+
+ res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
+ todo_wine
+ {
+ ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res);
+ }
+
+ size = MAX_PATH;
+ type = REG_SZ;
+ res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
+ todo_wine
+ {
+ ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res);
+ ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
+ }
+
+ size = MAX_PATH;
+ type = REG_SZ;
+ res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
+ todo_wine
+ {
+ ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res);
+ }
+
+ size = sizeof(num);
+ type = REG_DWORD;
+ res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
+ todo_wine
+ {
+ ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res);
+ ok(num == 314, "Expected 314, got %ld\n", num);
+ }
+
+ RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
+}
+
+static void test_MsiSetComponentState(void)
+{
+ MSIHANDLE package;
+ char path[MAX_PATH];
+ UINT r;
+
+ CoInitialize(NULL);
+
+ lstrcpy(path, CURR_DIR);
+ lstrcat(path, "\\");
+ lstrcat(path, msifile);
+
+ r = MsiOpenPackage(path, &package);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiDoAction(package, "CostInitialize");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiDoAction(package, "FileCost");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiDoAction(package, "CostFinalize");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSetComponentState(package, "dangler", INSTALLSTATE_SOURCE);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+ }
+
+ MsiCloseHandle(package);
+ CoUninitialize();
+}
+
+static void test_packagecoltypes(void)
+{
+ MSIHANDLE hdb, view, rec;
+ char path[MAX_PATH];
+ LPSTR query;
+ UINT r, count;
+
+ CoInitialize(NULL);
+
+ lstrcpy(path, CURR_DIR);
+ lstrcat(path, "\\");
+ lstrcat(path, msifile);
+
+ r = MsiOpenDatabase(path, MSIDBOPEN_READONLY, &hdb);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ query = "SELECT * FROM `Media`";
+ r = MsiDatabaseOpenView( hdb, query, &view );
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ }
+
+ r = MsiViewGetColumnInfo( view, MSICOLINFO_NAMES, &rec );
+ count = MsiRecordGetFieldCount( rec );
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n");
+ ok(count == 6, "Expected 6, got %d\n", count);
+ ok(check_record(rec, 1, "DiskId"), "wrong column label\n");
+ ok(check_record(rec, 2, "LastSequence"), "wrong column label\n");
+ ok(check_record(rec, 3, "DiskPrompt"), "wrong column label\n");
+ ok(check_record(rec, 4, "Cabinet"), "wrong column label\n");
+ ok(check_record(rec, 5, "VolumeLabel"), "wrong column label\n");
+ ok(check_record(rec, 6, "Source"), "wrong column label\n");
+ }
+
+ r = MsiViewGetColumnInfo( view, MSICOLINFO_TYPES, &rec );
+ count = MsiRecordGetFieldCount( rec );
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n");
+ ok(count == 6, "Expected 6, got %d\n", count);
+ ok(check_record(rec, 1, "i2"), "wrong column label\n");
+ ok(check_record(rec, 2, "i4"), "wrong column label\n");
+ ok(check_record(rec, 3, "L64"), "wrong column label\n");
+ ok(check_record(rec, 4, "S255"), "wrong column label\n");
+ ok(check_record(rec, 5, "S32"), "wrong column label\n");
+ ok(check_record(rec, 6, "S72"), "wrong column label\n");
+ }
+
+ MsiCloseHandle(hdb);
+ DeleteFile(msifile);
+}
+
+START_TEST(install)
+{
+ if (!init_function_pointers())
+ return;
+
+ create_test_files();
+ create_database(msifile, tables, sizeof(tables) / sizeof(msi_table));
+
+ test_MsiInstallProduct();
+ test_MsiSetComponentState();
+ test_packagecoltypes();
+
+ delete_test_files();
+}
--- /dev/null
+/*
+ * tests for Microsoft Installer functionality
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+typedef struct test_MSIFILEHASHINFO {
+ ULONG dwFileHashInfoSize;
+ ULONG dwData[4];
+} test_MSIFILEHASHINFO, *test_PMSIFILEHASHINFO;
+
+typedef INSTALLSTATE (WINAPI *fnMsiUseFeatureExA)(LPCSTR, LPCSTR ,DWORD, DWORD );
+fnMsiUseFeatureExA pMsiUseFeatureExA;
+typedef UINT (WINAPI *fnMsiOpenPackageExA)(LPCSTR, DWORD, MSIHANDLE*);
+fnMsiOpenPackageExA pMsiOpenPackageExA;
+typedef UINT (WINAPI *fnMsiOpenPackageExW)(LPCWSTR, DWORD, MSIHANDLE*);
+fnMsiOpenPackageExW pMsiOpenPackageExW;
+typedef INSTALLSTATE (WINAPI *fnMsiGetComponentPathA)(LPCSTR, LPCSTR, LPSTR, DWORD*);
+fnMsiGetComponentPathA pMsiGetComponentPathA;
+typedef UINT (WINAPI *fnMsiGetFileHashA)(LPCSTR, DWORD, test_PMSIFILEHASHINFO);
+fnMsiGetFileHashA pMsiGetFileHashA;
+
+static void test_usefeature(void)
+{
+ UINT r;
+
+ if (!pMsiUseFeatureExA)
+ return;
+
+ r = MsiQueryFeatureState(NULL,NULL);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = MsiQueryFeatureState("{9085040-6000-11d3-8cfe-0150048383c9}" ,NULL);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA(NULL,NULL,0,0);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA(NULL, "WORDVIEWFiles", -2, 1 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}",
+ NULL, -2, 0 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{9085040-6000-11d3-8cfe-0150048383c9}",
+ "WORDVIEWFiles", -2, 0 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{0085040-6000-11d3-8cfe-0150048383c9}",
+ "WORDVIEWFiles", -2, 0 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}",
+ "WORDVIEWFiles", -2, 1 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+}
+
+static void test_null(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+
+ r = pMsiOpenPackageExW(NULL, 0, &hpkg);
+ ok( r == ERROR_INVALID_PARAMETER,"wrong error\n");
+
+ r = MsiQueryProductStateW(NULL);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return\n");
+
+ r = MsiEnumFeaturesW(NULL,0,NULL,NULL);
+ ok( r == ERROR_INVALID_PARAMETER,"wrong error\n");
+
+ r = MsiConfigureFeatureW(NULL, NULL, 0);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
+
+ r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", NULL, 0);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
+
+ r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", "foo", 0);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", "foo", INSTALLSTATE_DEFAULT);
+ ok( r == ERROR_UNKNOWN_PRODUCT, "wrong error %d\n", r);
+}
+
+static void test_getcomponentpath(void)
+{
+ INSTALLSTATE r;
+ char buffer[0x100];
+ DWORD sz;
+
+ if(!pMsiGetComponentPathA)
+ return;
+
+ r = pMsiGetComponentPathA( NULL, NULL, NULL, NULL );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "bogus", "bogus", NULL, NULL );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "bogus", "{00000000-0000-0000-000000000000}", NULL, NULL );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ sz = sizeof buffer;
+ buffer[0]=0;
+ r = pMsiGetComponentPathA( "bogus", "{00000000-0000-0000-000000000000}", buffer, &sz );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000000-78E1-11D2-B60F-006097C998E7}",
+ "{00000000-0000-0000-0000-000000000000}", buffer, &sz );
+ ok( r == INSTALLSTATE_UNKNOWN, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000409-78E1-11D2-B60F-006097C998E7}",
+ "{00000000-0000-0000-0000-00000000}", buffer, &sz );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000409-78E1-11D2-B60F-006097C998E7}",
+ "{029E403D-A86A-1D11-5B5B0006799C897E}", buffer, &sz );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000000-78E1-11D2-B60F-006097C9987e}",
+ "{00000000-A68A-11d1-5B5B-0006799C897E}", buffer, &sz );
+ ok( r == INSTALLSTATE_UNKNOWN, "wrong return value\n");
+}
+
+static void test_filehash(void)
+{
+ const char name[] = "msitest.bin";
+ const char data[] = {'a','b','c'};
+ HANDLE handle;
+ UINT r;
+ test_MSIFILEHASHINFO hash;
+ DWORD count = 0;
+
+ if (!pMsiGetFileHashA)
+ return;
+
+ DeleteFile(name);
+
+ memset(&hash, 0, sizeof hash);
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ r = pMsiGetFileHashA(name, 0, NULL);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ memset(&hash, 0, sizeof hash);
+ hash.dwFileHashInfoSize = sizeof hash;
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_FILE_NOT_FOUND, "wrong error %d\n", r);
+
+ handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL);
+ ok(handle != INVALID_HANDLE_VALUE, "failed to create file\n");
+
+ WriteFile(handle, data, sizeof data, &count, NULL);
+ CloseHandle(handle);
+
+ memset(&hash, 0, sizeof hash);
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ memset(&hash, 0, sizeof hash);
+ hash.dwFileHashInfoSize = sizeof hash;
+ r = pMsiGetFileHashA(name, 1, &hash);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
+
+ ok(hash.dwFileHashInfoSize == sizeof hash, "hash size changed\n");
+ ok(hash.dwData[0] == 0x98500190 &&
+ hash.dwData[1] == 0xb04fd23c &&
+ hash.dwData[2] == 0x7d3f96d6 &&
+ hash.dwData[3] == 0x727fe128, "hash of abc incorrect\n");
+
+ DeleteFile(name);
+}
+
+START_TEST(msi)
+{
+ HMODULE hmod = GetModuleHandle("msi.dll");
+ pMsiUseFeatureExA = (fnMsiUseFeatureExA)
+ GetProcAddress(hmod, "MsiUseFeatureExA");
+ pMsiOpenPackageExA = (fnMsiOpenPackageExA)
+ GetProcAddress(hmod, "MsiOpenPackageExA");
+ pMsiOpenPackageExW = (fnMsiOpenPackageExW)
+ GetProcAddress(hmod, "MsiOpenPackageExW");
+ pMsiGetComponentPathA = (fnMsiGetComponentPathA)
+ GetProcAddress(hmod, "MsiGetComponentPathA" );
+ pMsiGetFileHashA = (fnMsiGetFileHashA)
+ GetProcAddress(hmod, "MsiGetFileHashA" );
+
+ test_usefeature();
+ test_null();
+ test_getcomponentpath();
+ test_filehash();
+}
--- /dev/null
+<module name="msi_winetest" type="win32cui" installbase="bin" installname="msi_winetest.exe" allowwarnings="true">\r
+ <include base="msi_winetest">.</include>\r
+ <define name="__USE_W32API" />\r
+ <library>cabinet</library>\r
+ <library>msi</library>\r
+ <library>ole32</library>\r
+ <library>advapi32</library>\r
+ <library>kernel32</library>\r
+ <library>ntdll</library>\r
+ <file>db.c</file>\r
+ <file>format.c</file>\r
+ <file>install.c</file>\r
+ <file>msi.c</file>\r
+ <file>package.c</file>\r
+ <file>record.c</file>\r
+ <file>suminfo.c</file>\r
+ <file>testlist.c</file>\r
+</module>\r
--- /dev/null
+/*
+ * tests for Microsoft Installer functionality
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static const char msifile[] = "winetest.msi";
+
+static UINT run_query( MSIHANDLE hdb, const char *query )
+{
+ MSIHANDLE hview = 0;
+ UINT r;
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, 0);
+ if( r == ERROR_SUCCESS )
+ r = MsiViewClose(hview);
+ MsiCloseHandle(hview);
+ return r;
+}
+
+static UINT create_component_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `Component` ( "
+ "`Component` CHAR(72) NOT NULL, "
+ "`ComponentId` CHAR(38), "
+ "`Directory_` CHAR(72) NOT NULL, "
+ "`Attributes` SHORT NOT NULL, "
+ "`Condition` CHAR(255), "
+ "`KeyPath` CHAR(72) "
+ "PRIMARY KEY `Component`)" );
+}
+
+static UINT create_feature_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `Feature` ( "
+ "`Feature` CHAR(38) NOT NULL, "
+ "`Feature_Parent` CHAR(38), "
+ "`Title` CHAR(64), "
+ "`Description` CHAR(255), "
+ "`Display` SHORT NOT NULL, "
+ "`Level` SHORT NOT NULL, "
+ "`Directory_` CHAR(72), "
+ "`Attributes` SHORT NOT NULL "
+ "PRIMARY KEY `Feature`)" );
+}
+
+static UINT create_feature_components_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `FeatureComponents` ( "
+ "`Feature_` CHAR(38) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL "
+ "PRIMARY KEY `Feature_`, `Component_` )" );
+}
+
+static UINT create_file_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `File` ("
+ "`File` CHAR(72) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL, "
+ "`FileName` CHAR(255) NOT NULL, "
+ "`FileSize` LONG NOT NULL, "
+ "`Version` CHAR(72), "
+ "`Language` CHAR(20), "
+ "`Attributes` SHORT, "
+ "`Sequence` SHORT NOT NULL "
+ "PRIMARY KEY `File`)" );
+}
+
+static UINT add_component_entry( MSIHANDLE hdb, char *values )
+{
+ char insert[] = "INSERT INTO `Component` "
+ "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_feature_entry( MSIHANDLE hdb, char *values )
+{
+ char insert[] = "INSERT INTO `Feature` (`Feature`, `Feature_Parent`, "
+ "`Display`, `Level`, `Attributes`) VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_feature_components_entry( MSIHANDLE hdb, char *values )
+{
+ char insert[] = "INSERT INTO `FeatureComponents` "
+ "(`Feature_`, `Component_`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_file_entry( MSIHANDLE hdb, char *values )
+{
+ char insert[] = "INSERT INTO `File` "
+ "(`File`, `Component_`, `FileName`, `FileSize`, `Version`, `Language`, `Attributes`, `Sequence`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT set_summary_info(MSIHANDLE hdb)
+{
+ UINT res;
+ MSIHANDLE suminfo;
+
+ /* build summmary info */
+ res = MsiGetSummaryInformation(hdb, NULL, 7, &suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to open summaryinfo\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,2, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,3, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,4, VT_LPSTR, 0,NULL,
+ "Wine Hackers");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,7, VT_LPSTR, 0,NULL,
+ ";1033");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,9, VT_LPSTR, 0,NULL,
+ "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 14, VT_I4, 100, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 15, VT_I4, 0, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoPersist(suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to make summary info persist\n" );
+
+ res = MsiCloseHandle( suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to close suminfo\n" );
+
+ return res;
+}
+
+
+MSIHANDLE create_package_db(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+
+ DeleteFile(msifile);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+ if( res != ERROR_SUCCESS )
+ return hdb;
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ res = set_summary_info(hdb);
+
+ res = run_query( hdb,
+ "CREATE TABLE `Directory` ( "
+ "`Directory` CHAR(255) NOT NULL, "
+ "`Directory_Parent` CHAR(255), "
+ "`DefaultDir` CHAR(255) NOT NULL "
+ "PRIMARY KEY `Directory`)" );
+ ok( res == ERROR_SUCCESS , "Failed to create directory table\n" );
+
+ return hdb;
+}
+
+MSIHANDLE package_from_db(MSIHANDLE hdb)
+{
+ UINT res;
+ CHAR szPackage[10];
+ MSIHANDLE hPackage;
+
+ sprintf(szPackage,"#%li",hdb);
+ res = MsiOpenPackage(szPackage,&hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to open package\n" );
+
+ res = MsiCloseHandle(hdb);
+ ok( res == ERROR_SUCCESS , "Failed to close db handle\n" );
+
+ return hPackage;
+}
+
+static void test_createpackage(void)
+{
+ MSIHANDLE hPackage = 0;
+ UINT res;
+
+ hPackage = package_from_db(create_package_db());
+ ok( hPackage != 0, " Failed to create package\n");
+
+ res = MsiCloseHandle( hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to close package\n" );
+ DeleteFile(msifile);
+}
+
+static void test_getsourcepath_bad( void )
+{
+ static const char str[] = { 0 };
+ char buffer[0x80];
+ DWORD sz;
+ UINT r;
+
+ r = MsiGetSourcePath( -1, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, NULL, buffer, &sz );
+ ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, str, NULL, &sz );
+ ok( r == ERROR_INVALID_HANDLE, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, str, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, str, buffer, &sz );
+ ok( r == ERROR_INVALID_HANDLE, "return value wrong\n");
+}
+
+static UINT add_directory_entry( MSIHANDLE hdb, char *values )
+{
+ char insert[] = "INSERT INTO `Directory` (`Directory`,`Directory_Parent`,`DefaultDir`) VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static void test_getsourcepath( void )
+{
+ static const char str[] = { 0 };
+ char buffer[0x80];
+ DWORD sz;
+ UINT r;
+ MSIHANDLE hpkg, hdb;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ sz = 0;
+ buffer[0] = 'x';
+ r = MsiGetSourcePath( hpkg, str, buffer, &sz );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+ ok( buffer[0] == 'x', "buffer modified\n");
+
+ sz = 1;
+ buffer[0] = 'x';
+ r = MsiGetSourcePath( hpkg, str, buffer, &sz );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+ ok( buffer[0] == 'x', "buffer modified\n");
+
+ MsiCloseHandle( hpkg );
+
+
+ /* another test but try create a directory this time */
+ hdb = create_package_db();
+ ok( hdb, "failed to create database\n");
+
+ r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'");
+ ok( r == S_OK, "failed\n");
+
+ hpkg = package_from_db(hdb);
+ ok( hpkg, "failed to create package\n");
+
+ sz = sizeof buffer -1;
+ strcpy(buffer,"x bad");
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+
+ r = MsiDoAction( hpkg, "CostInitialize");
+ ok( r == ERROR_SUCCESS, "cost init failed\n");
+ r = MsiDoAction( hpkg, "CostFinalize");
+ ok( r == ERROR_SUCCESS, "cost finalize failed\n");
+
+ todo_wine {
+ sz = sizeof buffer -1;
+ buffer[0] = 'x';
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "return value wrong\n");
+ ok( sz == strlen(buffer), "returned length wrong\n");
+
+ sz = 0;
+ strcpy(buffer,"x bad");
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "return value wrong\n");
+ }
+ ok( buffer[0] == 'x', "buffer modified\n");
+
+ todo_wine {
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", NULL, NULL );
+ ok( r == ERROR_SUCCESS, "return value wrong\n");
+ }
+
+ r = MsiGetSourcePath( hpkg, "TARGETDIR ", NULL, NULL );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+
+ r = MsiGetSourcePath( hpkg, "targetdir", NULL, NULL );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n");
+
+ todo_wine {
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", NULL, &sz );
+ ok( r == ERROR_SUCCESS, "return value wrong\n");
+ }
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void test_doaction( void )
+{
+ MSIHANDLE hpkg;
+ UINT r;
+
+ r = MsiDoAction( -1, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiDoAction(hpkg, NULL);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiDoAction(0, "boo");
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiDoAction(hpkg, "boo");
+ ok( r == ERROR_FUNCTION_NOT_CALLED, "wrong return val\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void test_gettargetpath_bad(void)
+{
+ char buffer[0x80];
+ MSIHANDLE hpkg;
+ DWORD sz;
+ UINT r;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiGetTargetPath( 0, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetTargetPath( 0, NULL, NULL, &sz );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetTargetPath( 0, "boo", NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiGetTargetPath( 0, "boo", NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiGetTargetPath( hpkg, "boo", NULL, NULL );
+ ok( r == ERROR_DIRECTORY, "wrong return val\n");
+
+ r = MsiGetTargetPath( hpkg, "boo", buffer, NULL );
+ ok( r == ERROR_DIRECTORY, "wrong return val\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void query_file_path(MSIHANDLE hpkg, LPCSTR file, LPSTR buff)
+{
+ UINT r;
+ DWORD size;
+ MSIHANDLE rec;
+
+ rec = MsiCreateRecord( 1 );
+ ok(rec, "MsiCreate record failed\n");
+
+ r = MsiRecordSetString( rec, 0, file );
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r );
+
+ size = MAX_PATH;
+ r = MsiFormatRecord( hpkg, rec, buff, &size );
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r );
+
+ MsiCloseHandle( rec );
+}
+
+static void test_settargetpath(void)
+{
+ char tempdir[MAX_PATH+8], buffer[MAX_PATH];
+ DWORD sz;
+ MSIHANDLE hpkg;
+ UINT r;
+ MSIHANDLE hdb;
+
+ hdb = create_package_db();
+ ok ( hdb, "failed to create package database\n" );
+
+ r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'" );
+ ok( r == S_OK, "failed to add directory entry: %d\n" , r );
+
+ r = run_query( hdb, /* these tables required by Windows Installer for MsiSetTargetPath */
+ "CREATE TABLE `Component` ( "
+ "`Component` CHAR(72) NOT NULL, "
+ "`ComponentId` CHAR(38), "
+ "`Directory_` CHAR(72) NOT NULL, "
+ "`Attributes` SHORT NOT NULL, "
+ "`Condition` CHAR(255), "
+ "`KeyPath` CHAR(72) "
+ "PRIMARY KEY `Component`)" );
+ ok( r == S_OK, "cannot create Component table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `Component` "
+ "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
+ "VALUES( 'WinWorkAround', '{83e2694d-0864-4124-9323-6d37630912a1}', 'TARGETDIR', 8, '', 'FL_dummycomponent')" );
+ ok( r == S_OK, "cannot add dummy component: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `Component` "
+ "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
+ "VALUES( 'TestComp', '{A3FB59C8-C293-4F7E-B8C5-F0E1D8EEE4E5}', 'TestDir', 0, '', 'TestFile')" );
+ ok( r == S_OK, "cannot add test component: %d\n", r );
+
+ r = run_query( hdb,
+ "CREATE TABLE `Feature` ( "
+ "`Feature` CHAR(38) NOT NULL, "
+ "`Feature_Parent` CHAR(38), "
+ "`Title` CHAR(64), "
+ "`Description` CHAR(255), "
+ "`Display` SHORT NOT NULL, "
+ "`Level` SHORT NOT NULL, "
+ "`Directory_` CHAR(72), "
+ "`Attributes` SHORT NOT NULL "
+ "PRIMARY KEY `Feature`)" );
+ ok( r == S_OK, "cannot create Feature table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `Feature` "
+ "(`Feature`, `Feature_Parent`, `Display`, `Level`, `Attributes`) "
+ "VALUES( 'TestFeature', '', 0, 1, 0 )" );
+ ok( r == ERROR_SUCCESS, "cannot add TestFeature to Feature table: %d\n", r );
+
+ r = run_query( hdb,
+ "CREATE TABLE `FeatureComponents` ( "
+ "`Feature_` CHAR(38) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL "
+ "PRIMARY KEY `Feature_` )" );
+ ok( r == S_OK, "cannot create FeatureComponents table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `FeatureComponents` "
+ "(`Feature_`, `Component_`) "
+ "VALUES( 'TestFeature', 'TestComp' )" );
+ ok( r == S_OK, "cannot insert component into FeatureComponents table: %d\n", r );
+
+ add_directory_entry( hdb, "'TestParent', 'TARGETDIR', 'TestParent'" );
+ add_directory_entry( hdb, "'TestDir', 'TestParent', 'TestDir'" );
+
+ r = run_query( hdb,
+ "CREATE TABLE `File` ("
+ "`File` CHAR(72) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL, "
+ "`FileName` CHAR(255) NOT NULL, "
+ "`FileSize` LONG NOT NULL, "
+ "`Version` CHAR(72), "
+ "`Language` CHAR(20), "
+ "`Attributes` SHORT, "
+ "`Sequence` SHORT NOT NULL "
+ "PRIMARY KEY `File`)" );
+ ok( r == S_OK, "cannot create File table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `File` "
+ "(`File`, `Component_`, `FileName`, `FileSize`, `Version`, `Language`, `Attributes`, `Sequence`) "
+ "VALUES( 'TestFile', 'TestComp', 'testfile.txt', 0, '', '1033', 8192, 1 )" );
+ ok( r == S_OK, "cannot add file to the File table: %d\n", r );
+
+ hpkg = package_from_db( hdb );
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiDoAction( hpkg, "CostInitialize");
+ ok( r == ERROR_SUCCESS, "cost init failed\n");
+
+ r = MsiDoAction( hpkg, "FileCost");
+ ok( r == ERROR_SUCCESS, "file cost failed\n");
+
+ r = MsiDoAction( hpkg, "CostFinalize");
+ ok( r == ERROR_SUCCESS, "cost finalize failed\n");
+
+ r = MsiSetTargetPath( 0, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiSetTargetPath( 0, "boo", "C:\\bogusx" );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiSetTargetPath( hpkg, "boo", NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiSetTargetPath( hpkg, "boo", "c:\\bogusx" );
+ ok( r == ERROR_DIRECTORY, "wrong return val\n");
+
+ sz = sizeof tempdir - 1;
+ r = MsiGetTargetPath( hpkg, "TARGETDIR", tempdir, &sz );
+ if ( r == S_OK )
+ {
+ if ( GetTempFileName( tempdir, "_wt", 0, buffer ) )
+ {
+ sprintf( tempdir, "%s\\subdir", buffer );
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath on file returned %d\n", r );
+
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath on 'subdir' of file returned %d\n", r );
+
+ DeleteFile( buffer );
+
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r );
+
+ r = GetFileAttributes( buffer );
+ ok ( r == INVALID_FILE_ATTRIBUTES, "file/directory exists after MsiSetTargetPath. Attributes: %08X\n", r );
+
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath on subsubdir returned %d\n", r );
+ } else {
+ trace("GetTempFileName failed, cannot do some tests\n");
+ }
+ } else {
+ trace( "MsiGetTargetPath failed: %d\n", r );
+ }
+
+ r = MsiSetTargetPath( hpkg, "TestParent", "C:\\one\\two" );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r );
+
+ query_file_path( hpkg, "[#TestFile]", buffer );
+ ok( !lstrcmp(buffer, "C:\\one\\two\\TestDir\\testfile.txt"),
+ "Expected C:\\one\\two\\TestDir\\testfile.txt, got %s\n", buffer );
+
+ MsiCloseHandle( hpkg );
+}
+
+static void test_condition(void)
+{
+ MSICONDITION r;
+ MSIHANDLE hpkg;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiEvaluateCondition(0, NULL);
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, NULL);
+ ok( r == MSICONDITION_NONE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "");
+ ok( r == MSICONDITION_NONE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 = 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 <> 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 = 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 > 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~> 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 > 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~> 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 >= 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~>= 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 >= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~>= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 < 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~< 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 < 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~< 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 <= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~<= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 <= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~<= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 >=");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " ");
+ ok( r == MSICONDITION_NONE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "LicView <> \"1\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "LicView <> \"0\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "LicView <> LicView");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not LicView");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not \"A\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "~not \"A\"");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"0\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 and 2");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0 and 3");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0 and 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0 or 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(0)");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(((((1))))))");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(((((1)))))");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" < \"B\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" > \"B\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"1\" > \"12\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"100\" < \"21\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 < > 0");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(1<<1) == 2");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" = \"a\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" ~ = \"a\" ");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" ~= \"a\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" ~= 1 ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" = 1 ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 1 ~= 1 ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 1 ~= \"1\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 1 = \"1\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 0 = \"1\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 0 < \"100\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 100 > \"0\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 XOR 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMP 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMP 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 IMP 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 EQV 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 EQV 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMP 1 OR 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMPL 1");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" >< \"S\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" ~>< \"s\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" ~>< \"\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" ~>< \"sss\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "mm", "5" );
+
+ r = MsiEvaluateCondition(hpkg, "mm = 5");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm < 6");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm <= 5");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm > 4");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm < 12");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm = \"5\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 = \"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 AND \"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 AND \"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 AND \"1\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "3 >< 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "3 >< 4");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 0 AND 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 0 AND 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 1 OR 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 AND 1 OR 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 AND 0 OR 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 0 AND 1 OR 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "_1 = _1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "( 1 AND 1 ) = 2");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT ( 1 AND 1 )");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT A AND (BBBBBBBBBB=2 OR CCC=1) AND Ddddddddd");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "Installed<>\"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 1 AND 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael<>0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael<0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael>0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael>=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael<=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael~<>0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "asdf" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0asdf" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0 " );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "-0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0000000000000" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "--0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0x00" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "-" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "+0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0.0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+ r = MsiEvaluateCondition(hpkg, "bandalmael<>0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static BOOL check_prop_empty( MSIHANDLE hpkg, char * prop)
+{
+ UINT r;
+ DWORD sz;
+ char buffer[2];
+
+ sz = sizeof buffer;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, prop, buffer, &sz );
+ return r == ERROR_SUCCESS && buffer[0] == 0 && sz == 0;
+}
+
+static void test_props(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+ DWORD sz;
+ char buffer[0x100];
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ /* test invalid values */
+ r = MsiGetProperty( 0, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetProperty( hpkg, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetProperty( hpkg, "boo", NULL, NULL );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ r = MsiGetProperty( hpkg, "boo", buffer, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ /* test retrieving an empty/nonexistent property */
+ sz = sizeof buffer;
+ r = MsiGetProperty( hpkg, "boo", NULL, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( sz == 0, "wrong size returned\n");
+
+ check_prop_empty( hpkg, "boo");
+ sz = 0;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "wrong return val\n");
+ ok( !strcmp(buffer,"x"), "buffer was changed\n");
+ ok( sz == 0, "wrong size returned\n");
+
+ sz = 1;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( buffer[0] == 0, "buffer was not changed\n");
+ ok( sz == 0, "wrong size returned\n");
+
+ /* set the property to something */
+ r = MsiSetProperty( 0, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, "", NULL );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ /* try set and get some illegal property identifiers */
+ r = MsiSetProperty( hpkg, "", "asdf" );
+ ok( r == ERROR_FUNCTION_FAILED, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, "=", "asdf" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, " ", "asdf" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, "'", "asdf" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ sz = sizeof buffer;
+ buffer[0]=0;
+ r = MsiGetProperty( hpkg, "'", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( !strcmp(buffer,"asdf"), "buffer was not changed\n");
+
+ /* set empty values */
+ r = MsiSetProperty( hpkg, "boo", NULL );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( check_prop_empty( hpkg, "boo"), "prop wasn't empty\n");
+
+ r = MsiSetProperty( hpkg, "boo", "" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( check_prop_empty( hpkg, "boo"), "prop wasn't empty\n");
+
+ /* set a non-empty value */
+ r = MsiSetProperty( hpkg, "boo", "xyz" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ sz = 1;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "wrong return val\n");
+ ok( buffer[0] == 0, "buffer was not changed\n");
+ ok( sz == 3, "wrong size returned\n");
+
+ sz = 4;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( !strcmp(buffer,"xyz"), "buffer was not changed\n");
+ ok( sz == 3, "wrong size returned\n");
+
+ sz = 3;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "wrong return val\n");
+ ok( !strcmp(buffer,"xy"), "buffer was not changed\n");
+ ok( sz == 3, "wrong size returned\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
+{
+ MSIHANDLE htab = 0;
+ UINT res;
+
+ res = MsiDatabaseOpenView( hdb, szQuery, &htab );
+ if( res == ERROR_SUCCESS )
+ {
+ UINT r;
+
+ r = MsiViewExecute( htab, hrec );
+ if( r != ERROR_SUCCESS )
+ {
+ res = r;
+ fprintf(stderr,"MsiViewExecute failed %08x\n", res);
+ }
+
+ r = MsiViewClose( htab );
+ if( r != ERROR_SUCCESS )
+ res = r;
+
+ r = MsiCloseHandle( htab );
+ if( r != ERROR_SUCCESS )
+ res = r;
+ }
+ return res;
+}
+
+static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
+{
+ return try_query_param( hdb, szQuery, 0 );
+}
+
+static void test_msipackage(void)
+{
+ MSIHANDLE hdb = 0, hpack = 100;
+ UINT r;
+ const char *query;
+ char name[10];
+
+ DeleteFile(msifile);
+
+ todo_wine {
+ name[0] = 0;
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_SUCCESS, "failed to open package with no name\n");
+ r = MsiCloseHandle(hpack);
+ ok(r == ERROR_SUCCESS, "failed to close package\n");
+ }
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ name[0]='#';
+ name[1]=0;
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_INVALID_HANDLE, "MsiOpenPackage returned wrong code\n");
+
+ todo_wine {
+ /* now try again with our empty database */
+ sprintf(name, "#%ld", hdb);
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_INSTALL_PACKAGE_INVALID, "MsiOpenPackage returned wrong code\n");
+ if (!r) MsiCloseHandle(hpack);
+ }
+
+ /* create a table */
+ query = "CREATE TABLE `Property` ( "
+ "`Property` CHAR(72), `Value` CHAR(0) "
+ "PRIMARY KEY `Property`)";
+ r = try_query(hdb, query);
+ ok(r == ERROR_SUCCESS, "failed to create Properties table\n");
+
+ todo_wine {
+ query = "CREATE TABLE `InstallExecuteSequence` ("
+ "`Action` CHAR(72), `Condition` CHAR(0), `Sequence` INTEGER "
+ "PRIMARY KEY `Action`)";
+ r = try_query(hdb, query);
+ ok(r == ERROR_SUCCESS, "failed to create InstallExecuteSequence table\n");
+
+ sprintf(name, "#%ld", hdb);
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_INSTALL_PACKAGE_INVALID, "MsiOpenPackage returned wrong code\n");
+ if (!r) MsiCloseHandle(hpack);
+ }
+
+ r = MsiCloseHandle(hdb);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle(database) failed\n");
+ DeleteFile(msifile);
+}
+
+static void test_formatrecord2(void)
+{
+ MSIHANDLE hpkg, hrec ;
+ char buffer[0x100];
+ DWORD sz;
+ UINT r;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiSetProperty(hpkg, "Manufacturer", " " );
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+
+ hrec = MsiCreateRecord(2);
+ ok(hrec, "create record failed\n");
+
+ r = MsiRecordSetString( hrec, 0, "[ProgramFilesFolder][Manufacturer]\\asdf");
+ ok( r == ERROR_SUCCESS, "format record failed\n");
+
+ buffer[0] = 0;
+ sz = sizeof buffer;
+ r = MsiFormatRecord( hpkg, hrec, buffer, &sz );
+
+ r = MsiRecordSetString(hrec, 0, "[foo][1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "x[~]x");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"x"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[foo.$%}][1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[\\[]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 1, "size wrong\n");
+ ok( 0 == strcmp(buffer,"["), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ SetEnvironmentVariable("FOO", "BAR");
+ r = MsiRecordSetString(hrec, 0, "[%FOO]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"BAR"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[[1]]");
+ r = MsiRecordSetString(hrec, 1, "%FOO");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"BAR"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ MsiCloseHandle( hrec );
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void test_states(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+ MSIHANDLE hdb;
+ INSTALLSTATE state, action;
+
+ hdb = create_package_db();
+ ok ( hdb, "failed to create package database\n" );
+
+ r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'");
+ ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r );
+
+ r = create_feature_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Feature table: %d\n", r );
+
+ r = create_component_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal */
+ r = add_feature_entry( hdb, "'one', '', 2, 1, 0" );
+ ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesLocalOnly */
+ r = add_component_entry( hdb, "'alpha', '{467EC132-739D-4784-A37B-677AA43DBC94}', 'TARGETDIR', 0, '', 'alpha_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesSourceOnly */
+ r = add_component_entry( hdb, "'beta', '{2C1F189C-24A6-4C34-B26B-994A6C026506}', 'TARGETDIR', 1, '', 'beta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesOptional */
+ r = add_component_entry( hdb, "'gamma', '{C271E2A4-DE2E-4F70-86D1-6984AF7DE2CA}', 'TARGETDIR', 2, '', 'gamma_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource */
+ r = add_feature_entry( hdb, "'two', '', 2, 1, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesLocalOnly */
+ r = add_component_entry( hdb, "'delta', '{938FD4F2-C648-4259-A03C-7AA3B45643F3}', 'TARGETDIR', 0, '', 'delta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesSourceOnly */
+ r = add_component_entry( hdb, "'epsilon', '{D59713B6-C11D-47F2-A395-1E5321781190}', 'TARGETDIR', 1, '', 'epsilon_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesOptional */
+ r = add_component_entry( hdb, "'zeta', '{377D33AB-2FAA-42B9-A629-0C0DAE9B9C7A}', 'TARGETDIR', 2, '', 'zeta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource */
+ r = add_feature_entry( hdb, "'three', '', 2, 1, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesSourceOnly */
+ r = add_component_entry( hdb, "'eta', '{DD89003F-0DD4-41B8-81C0-3411A7DA2695}', 'TARGETDIR', 1, '', 'eta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = create_feature_components_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'alpha'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'beta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'gamma'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'two', 'delta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'two', 'epsilon'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'two', 'zeta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'three', 'eta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = create_file_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create File table: %d\n", r );
+
+ r = add_file_entry( hdb, "'alpha_file', 'alpha', 'alpha.txt', 100, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'beta_file', 'beta', 'beta.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'gamma_file', 'gamma', 'gamma.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'delta_file', 'delta', 'delta.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'epsilon_file', 'epsilon', 'epsilon.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'zeta_file', 'zeta', 'zeta.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ /* compressed file */
+ r = add_file_entry( hdb, "'eta_file', 'eta', 'eta.txt', 0, '', '1033', 16384, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ hpkg = package_from_db( hdb );
+ ok( hpkg, "failed to create package\n");
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", state);
+ ok( action == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", action);
+
+ r = MsiDoAction( hpkg, "CostInitialize");
+ ok( r == ERROR_SUCCESS, "cost init failed\n");
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ r = MsiDoAction( hpkg, "FileCost");
+ ok( r == ERROR_SUCCESS, "file cost failed\n");
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ r = MsiDoAction( hpkg, "CostFinalize");
+ ok( r == ERROR_SUCCESS, "cost finalize failed: %d\n", r);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ todo_wine
+ {
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ todo_wine
+ {
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ todo_wine
+ {
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ todo_wine
+ {
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+ }
+
+ state = 0xdeadbeef;
+ action = 0xdeadbeef;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ todo_wine
+ {
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+ }
+
+ MsiCloseHandle( hpkg );
+}
+
+START_TEST(package)
+{
+ test_createpackage();
+ test_getsourcepath_bad();
+ test_getsourcepath();
+ test_doaction();
+ test_gettargetpath_bad();
+ test_settargetpath();
+ test_props();
+ test_condition();
+ test_msipackage();
+ test_formatrecord2();
+ test_states();
+}
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ *
+ * A test program for MSI records
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static BOOL create_temp_file(char *name)
+{
+ UINT r;
+ unsigned char buffer[26], i;
+ DWORD sz;
+ HANDLE handle;
+
+ r = GetTempFileName(".", "msitest",0,name);
+ if(!r)
+ return r;
+ handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE,
+ 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(handle==INVALID_HANDLE_VALUE)
+ return 0;
+ for(i=0; i<26; i++)
+ buffer[i]=i+'a';
+ r = WriteFile(handle,buffer,sizeof buffer,&sz,NULL);
+ CloseHandle(handle);
+ return r;
+}
+
+static void test_msirecord(void)
+{
+ DWORD r, sz;
+ INT i;
+ MSIHANDLE h;
+ char buf[10];
+ WCHAR bufW[10];
+ const char str[] = "hello";
+ const WCHAR strW[] = { 'h','e','l','l','o',0};
+ char filename[MAX_PATH];
+
+ /* check behaviour with an invalid record */
+ r = MsiRecordGetFieldCount(0);
+ ok(r==-1, "field count for invalid record not -1\n");
+ SetLastError(0);
+ r = MsiRecordIsNull(0, 0);
+ ok(r==0, "invalid handle not considered to be non-null...\n");
+ ok(GetLastError()==0, "MsiRecordIsNull set LastError\n");
+ r = MsiRecordGetInteger(0,0);
+ ok(r == MSI_NULL_INTEGER, "got integer from invalid record\n");
+ r = MsiRecordSetInteger(0,0,0);
+ ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
+ r = MsiRecordSetInteger(0,-1,0);
+ ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
+ SetLastError(0);
+ h = MsiCreateRecord(-1);
+ ok(h==0, "created record with -1 elements\n");
+ h = MsiCreateRecord(0x10000);
+ ok(h==0, "created record with 0x10000 elements\n");
+ /* doesn't set LastError */
+ ok(GetLastError()==0, "MsiCreateRecord set last error\n");
+ r = MsiRecordClearData(0);
+ ok(r == ERROR_INVALID_HANDLE, "MsiRecordClearData returned wrong error\n");
+ r = MsiRecordDataSize(0,0);
+ ok(r == 0, "MsiRecordDataSize returned wrong error\n");
+
+
+ /* check behaviour of a record with 0 elements */
+ h = MsiCreateRecord(0);
+ ok(h!=0, "couldn't create record with zero elements\n");
+ r = MsiRecordGetFieldCount(h);
+ ok(r==0, "field count should be zero\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r, "new record wasn't null\n");
+ r = MsiRecordIsNull(h,1);
+ ok(r, "out of range record wasn't null\n");
+ r = MsiRecordIsNull(h,-1);
+ ok(r, "out of range record wasn't null\n");
+ r = MsiRecordDataSize(h,0);
+ ok(r==0, "size of null record is 0\n");
+ sz = sizeof buf;
+ strcpy(buf,"x");
+ r = MsiRecordGetString(h, 0, buf, &sz);
+ ok(r==ERROR_SUCCESS, "failed to get null string\n");
+ ok(sz==0, "null string too long\n");
+ ok(buf[0]==0, "null string not set\n");
+
+ /* same record, but add an integer to it */
+ r = MsiRecordSetInteger(h, 0, 0);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 0\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r==0, "new record is null after setting an integer\n");
+ r = MsiRecordDataSize(h,0);
+ ok(r==sizeof(DWORD), "size of integer record is 4\n");
+ r = MsiRecordSetInteger(h, 0, 1);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 1\n");
+ r = MsiRecordSetInteger(h, 1, 1);
+ ok(r == ERROR_INVALID_PARAMETER, "set integer at 1\n");
+ r = MsiRecordSetInteger(h, -1, 0);
+ ok(r == ERROR_INVALID_PARAMETER, "set integer at -1\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r==0, "new record is null after setting an integer\n");
+ r = MsiRecordGetInteger(h, 0);
+ ok(r == 1, "failed to get integer\n");
+
+ /* same record, but add a string to it */
+ r = MsiRecordSetString(h, 0, NULL);
+ ok(r == ERROR_SUCCESS, "Failed to set null string at 0\n");
+ r = MsiRecordIsNull(h, 0);
+ ok(r == TRUE, "null string not null field\n");
+ r = MsiRecordSetString(h, 0, "");
+ ok(r == ERROR_SUCCESS, "Failed to set empty string at 0\n");
+ r = MsiRecordIsNull(h, 0);
+ ok(r == TRUE, "null string not null field\n");
+ r = MsiRecordSetString(h,0,str);
+ ok(r == ERROR_SUCCESS, "Failed to set string at 0\n");
+ r = MsiRecordGetInteger(h, 0);
+ ok(r == MSI_NULL_INTEGER, "should get invalid integer\n");
+ r = MsiRecordDataSize(h,0);
+ ok(r==sizeof str-1, "size of string record is strlen\n");
+ buf[0]=0;
+ sz = sizeof buf;
+ r = MsiRecordGetString(h,0,buf,&sz);
+ ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
+ ok(0==strcmp(buf,str), "MsiRecordGetString returned the wrong string\n");
+ ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n");
+ buf[0]=0;
+ sz = sizeof str - 2;
+ r = MsiRecordGetString(h,0,buf,&sz);
+ ok(r == ERROR_MORE_DATA, "small buffer should yield ERROR_MORE_DATA\n");
+ ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n");
+ ok(0==strncmp(buf,str,sizeof str-3), "MsiRecordGetString returned the wrong string\n");
+ ok(buf[sizeof str - 3]==0, "string wasn't nul terminated\n");
+
+ buf[0]=0;
+ sz = sizeof str;
+ r = MsiRecordGetString(h,0,buf,&sz);
+ ok(r == ERROR_SUCCESS, "wrong error\n");
+ ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n");
+ ok(0==strcmp(buf,str), "MsiRecordGetString returned the wrong string\n");
+
+
+ memset(bufW, 0, sizeof bufW);
+ sz = 5;
+ r = MsiRecordGetStringW(h,0,bufW,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok(0==memcmp(bufW,strW,8), "MsiRecordGetString returned the wrong string\n");
+
+ sz = 0;
+ bufW[0] = 'x';
+ r = MsiRecordGetStringW(h,0,bufW,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok('x'==bufW[0], "MsiRecordGetString returned the wrong string\n");
+
+ memset(buf, 0, sizeof buf);
+ sz = 5;
+ r = MsiRecordGetStringA(h,0,buf,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok(0==memcmp(buf,str,4), "MsiRecordGetString returned the wrong string\n");
+
+ sz = 0;
+ buf[0] = 'x';
+ r = MsiRecordGetStringA(h,0,buf,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok('x'==buf[0], "MsiRecordGetString returned the wrong string\n");
+
+ /* same record, check we can wipe all the data */
+ r = MsiRecordClearData(h);
+ ok(r == ERROR_SUCCESS, "Failed to clear record\n");
+ r = MsiRecordClearData(h);
+ ok(r == ERROR_SUCCESS, "Failed to clear record again\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r, "cleared record wasn't null\n");
+
+ /* same record, try converting strings to integers */
+ i = MsiRecordSetString(h,0,"42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == 42, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"-42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == -42, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0," 42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"42 ");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"42.0");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"0x42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"1000000000000000");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == -1530494976, "should get truncated integer\n");
+ i = MsiRecordSetString(h,0,"2147483647");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == 2147483647, "should get maxint\n");
+ i = MsiRecordSetString(h,0,"-2147483647");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == -2147483647, "should get -maxint-1\n");
+ i = MsiRecordSetString(h,0,"4294967297");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == 1, "should get one\n");
+
+ /* same record, try converting integers to strings */
+ r = MsiRecordSetInteger(h, 0, 32);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
+ buf[0]=0;
+ sz = sizeof buf;
+ r = MsiRecordGetString(h, 0, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
+ ok(0==strcmp(buf,"32"), "failed to get string from integer\n");
+ r = MsiRecordSetInteger(h, 0, -32);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
+ buf[0]=0;
+ sz = sizeof buf;
+ r = MsiRecordGetString(h, 0, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
+ ok(0==strcmp(buf,"-32"), "failed to get string from integer\n");
+
+ /* same record, now try streams */
+ r = MsiRecordSetStream(h, 0, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "set NULL stream\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 0, buf, &sz);
+ ok(r == ERROR_INVALID_DATATYPE, "read non-stream type\n");
+ ok(sz == sizeof buf, "set sz\n");
+ r = MsiRecordDataSize( h, -1);
+ ok(r == 0,"MsiRecordDataSize returned wrong size\n");
+ r = MsiRecordDataSize( h, 0);
+ ok(r == 4,"MsiRecordDataSize returned wrong size\n");
+
+ /* same record, now close it */
+ r = MsiCloseHandle(h);
+ ok(r == ERROR_SUCCESS, "Failed to close handle\n");
+
+ /* now try streams in a new record - need to create a file to play with */
+ r = create_temp_file(filename);
+ if(!r)
+ return;
+
+ /* streams can't be inserted in field 0 for some reason */
+ h = MsiCreateRecord(2);
+ ok(h, "couldn't create a two field record\n");
+ r = MsiRecordSetStream(h, 0, filename);
+ ok(r == ERROR_INVALID_PARAMETER, "added stream to field 0\n");
+ r = MsiRecordSetStream(h, 1, filename);
+ ok(r == ERROR_SUCCESS, "failed to add stream to record\n");
+ r = MsiRecordReadStream(h, 1, buf, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
+ /* http://test.winehq.org/data/200503181000/98_jmelgarejo98casa/msi:record.txt */
+ DeleteFile(filename); /* Windows 98 doesn't like this at all, so don't check return. */
+ r = MsiRecordReadStream(h, 1, NULL, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, NULL, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==26,"couldn't get size of stream\n");
+ sz = 0;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==0,"short read\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==sizeof buf,"short read\n");
+ ok(!strncmp(buf,"abcdefghij",10), "read the wrong thing\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==sizeof buf,"short read\n");
+ ok(!strncmp(buf,"klmnopqrst",10), "read the wrong thing\n");
+ memset(buf,0,sizeof buf);
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==6,"short read\n");
+ ok(!strcmp(buf,"uvwxyz"), "read the wrong thing\n");
+ memset(buf,0,sizeof buf);
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==0,"size non-zero at end of stream\n");
+ ok(buf[0]==0, "read something at end of the stream\n");
+ r = MsiRecordSetStream(h, 1, NULL);
+ ok(r == ERROR_SUCCESS, "failed to reset stream\n");
+ sz = 0;
+ r = MsiRecordReadStream(h, 1, NULL, &sz);
+ ok(r == ERROR_SUCCESS, "bytes left wrong after reset\n");
+ ok(sz==26,"couldn't get size of stream\n");
+ r = MsiRecordDataSize(h,1);
+ ok(r == 26,"MsiRecordDataSize returned wrong size\n");
+
+ /* now close the stream record */
+ r = MsiCloseHandle(h);
+ ok(r == ERROR_SUCCESS, "Failed to close handle\n");
+ DeleteFile(filename); /* Delete it for sure, when everything else is closed. */
+}
+
+START_TEST(record)
+{
+ test_msirecord();
+}
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ *
+ * A test program for MSI database files.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+/*
+ * The following are defined in Windows SDK's msidefs.h
+ * but that file doesn't exist in the msvc6 header set.
+ *
+ * Some are already defined in PropIdl.h - undefine them
+ */
+#undef PID_DICTIONARY
+#undef PID_CODEPAGE
+#undef PID_SUBJECT
+#undef PID_SECURITY
+
+#define PID_DICTIONARY 0
+#define PID_CODEPAGE 1
+#define PID_TITLE 2
+#define PID_SUBJECT 3
+#define PID_AUTHOR 4
+#define PID_KEYWORDS 5
+#define PID_COMMENTS 6
+#define PID_TEMPLATE 7
+#define PID_LASTAUTHOR 8
+#define PID_REVNUMBER 9
+#define PID_EDITTINE 10
+#define PID_LASTPRINTED 11
+#define PID_CREATE_DTM 12
+#define PID_LASTSAVE_DTM 13
+#define PID_PAGECOUNT 14
+#define PID_WORDCOUNT 15
+#define PID_CHARCOUNT 16
+#define PID_THUMBNAIL 17
+#define PID_APPNAME 18
+#define PID_SECURITY 19
+#define PID_MSIVERSION PID_PAGECOUNT
+#define PID_MSISOURCE PID_WORDCOUNT
+#define PID_MSIRESTRICT PID_CHARCOUNT
+
+START_TEST(suminfo)
+{
+ const char *msifile = "winetest.msi";
+ MSIHANDLE hdb = 0, hsuminfo;
+ UINT r, count, type;
+ DWORD sz;
+ INT val;
+ FILETIME ft;
+ char buf[0x10];
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ r = MsiGetSummaryInformation(hdb, NULL, 0, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiGetSummaryInformation wrong error\n");
+
+ r = MsiGetSummaryInformation(hdb, NULL, 0, &hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n");
+
+ r = MsiSummaryInfoGetPropertyCount(0, NULL);
+ ok(r == ERROR_INVALID_HANDLE, "getpropcount failed\n");
+
+ r = MsiSummaryInfoGetPropertyCount(hsuminfo, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+
+ count = -1;
+ r = MsiSummaryInfoGetPropertyCount(hsuminfo, &count);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(count == 0, "count should be zero\n");
+
+ r = MsiSummaryInfoGetProperty(hsuminfo, 0, NULL, NULL, NULL, 0, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+
+ type = -1;
+ r = MsiSummaryInfoGetProperty(hsuminfo, 0, &type, NULL, NULL, 0, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(type == 0, "wrong type\n");
+
+ type = -1;
+ val = 1234;
+ r = MsiSummaryInfoGetProperty(hsuminfo, 0, &type, &val, NULL, 0, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(type == 0, "wrong type\n");
+ ok(val == 1234, "wrong val\n");
+
+ buf[0]='x';
+ buf[1]=0;
+ sz = 0x10;
+ r = MsiSummaryInfoGetProperty(hsuminfo, PID_REVNUMBER, &type, &val, NULL, buf, &sz);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(buf[0]=='x', "cleared buffer\n");
+ ok(sz == 0x10, "count wasn't zero\n");
+ ok(type == VT_EMPTY, "should be empty\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "Mike");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 1, NULL, "JungAh");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 1, &ft, "Mike");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, &ft, "JungAh");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiCloseHandle(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* try again with the update count set */
+ r = MsiGetSummaryInformation(hdb, NULL, 1, &hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, 0, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_I4, 0, NULL, "Mike");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_AUTHOR, VT_I4, 0, NULL, "JungAh");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_KEYWORDS, VT_I2, 0, NULL, "Mike");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_COMMENTS, VT_FILETIME, 0, NULL, "JungAh");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TEMPLATE, VT_I2, 0, NULL, "Mike");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_LASTAUTHOR, VT_LPSTR, 0, NULL, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_LASTSAVE_DTM, VT_FILETIME, 0, NULL, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_LASTAUTHOR, VT_LPWSTR, 0, NULL, "h\0i\0\0");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_REVNUMBER, VT_I4, 0, NULL, "Jungah");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_PAGECOUNT, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "Mike");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n");
+
+ sz = 2;
+ strcpy(buf,"x");
+ r = MsiSummaryInfoGetProperty(hsuminfo, PID_TITLE, &type, NULL, NULL, buf, &sz );
+ ok(r == ERROR_MORE_DATA, "MsiSummaryInfoSetProperty failed\n");
+ ok(sz == 4, "count was wrong\n");
+ ok(type == VT_LPSTR, "type was wrong\n");
+ ok(!strcmp(buf,"M"), "buffer was wrong\n");
+
+ sz = 4;
+ strcpy(buf,"x");
+ r = MsiSummaryInfoGetProperty(hsuminfo, PID_TITLE, &type, NULL, NULL, buf, &sz );
+ ok(r == ERROR_MORE_DATA, "MsiSummaryInfoSetProperty failed\n");
+ ok(sz == 4, "count was wrong\n");
+ ok(type == VT_LPSTR, "type was wrong\n");
+ ok(!strcmp(buf,"Mik"), "buffer was wrong\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "JungAh");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, &ft, "Mike");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiCloseHandle(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* try again with a higher update count */
+ r = MsiGetSummaryInformation(hdb, NULL, 10, &hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "JungAh");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, NULL, NULL);
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, &ft, "Mike");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_AUTHOR, VT_LPSTR, 1, &ft, "Mike");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoPersist(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoPersist failed\n");
+
+ MsiDatabaseCommit(hdb);
+
+ r = MsiCloseHandle(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = MsiCloseHandle(hdb);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = DeleteFile(msifile);
+ ok(r, "DeleteFile failed\n");
+}
--- /dev/null
+#define WIN32_LEAN_AND_MEAN\r
+#include <windows.h>\r
+\r
+#define STANDALONE\r
+#include "wine/test.h"\r
+\r
+extern void func_db(void);\r
+extern void func_format(void);\r
+extern void func_install(void);\r
+extern void func_msi(void);\r
+extern void func_package(void);\r
+extern void func_record(void);\r
+extern void func_suminfo(void);\r
+\r
+const struct test winetest_testlist[] =\r
+{\r
+ { "db", func_db },\r
+ { "format", func_format },\r
+ { "install", func_install },\r
+ { "msi", func_msi },\r
+ { "package", func_package },\r
+ { "record", func_record },\r
+ { "suminfo", func_suminfo },\r
+ { 0, 0 }\r
+};\r