Commit 50794ce7 authored by Juan Lang's avatar Juan Lang Committed by Alexandre Julliard

Add traces, add unit tests for IPropertyStorage, and fix the problems

they caught.
parent 6d831050
...@@ -22,6 +22,9 @@ ...@@ -22,6 +22,9 @@
#include "windef.h" #include "windef.h"
#include "winbase.h" #include "winbase.h"
#include "dictionary.h" #include "dictionary.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(storage);
struct dictionary_entry struct dictionary_entry
{ {
...@@ -36,12 +39,14 @@ struct dictionary ...@@ -36,12 +39,14 @@ struct dictionary
destroyfunc destroy; destroyfunc destroy;
void *extra; void *extra;
struct dictionary_entry *head; struct dictionary_entry *head;
UINT num_entries;
}; };
struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra) struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra)
{ {
struct dictionary *ret; struct dictionary *ret;
TRACE("(%p, %p, %p)\n", c, d, extra);
if (!c) if (!c)
return NULL; return NULL;
ret = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary)); ret = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary));
...@@ -51,12 +56,15 @@ struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra) ...@@ -51,12 +56,15 @@ struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra)
ret->destroy = d; ret->destroy = d;
ret->extra = extra; ret->extra = extra;
ret->head = NULL; ret->head = NULL;
ret->num_entries = 0;
} }
TRACE("returning %p\n", ret);
return ret; return ret;
} }
void dictionary_destroy(struct dictionary *d) void dictionary_destroy(struct dictionary *d)
{ {
TRACE("(%p)\n", d);
if (d) if (d)
{ {
struct dictionary_entry *p; struct dictionary_entry *p;
...@@ -74,6 +82,11 @@ void dictionary_destroy(struct dictionary *d) ...@@ -74,6 +82,11 @@ void dictionary_destroy(struct dictionary *d)
} }
} }
UINT dictionary_num_entries(struct dictionary *d)
{
return d ? d->num_entries : 0;
}
/* Returns the address of the pointer to the node containing k. (It returns /* Returns the address of the pointer to the node containing k. (It returns
* the address of either h->head or the address of the next member of the * the address of either h->head or the address of the next member of the
* prior node. It's useful when you want to delete.) * prior node. It's useful when you want to delete.)
...@@ -101,6 +114,7 @@ void dictionary_insert(struct dictionary *d, const void *k, const void *v) ...@@ -101,6 +114,7 @@ void dictionary_insert(struct dictionary *d, const void *k, const void *v)
{ {
struct dictionary_entry **prior; struct dictionary_entry **prior;
TRACE("(%p, %p, %p)\n", d, k, v);
if (!d) if (!d)
return; return;
if ((prior = dictionary_find_internal(d, k))) if ((prior = dictionary_find_internal(d, k)))
...@@ -121,6 +135,7 @@ void dictionary_insert(struct dictionary *d, const void *k, const void *v) ...@@ -121,6 +135,7 @@ void dictionary_insert(struct dictionary *d, const void *k, const void *v)
elem->value = (void *)v; elem->value = (void *)v;
elem->next = d->head; elem->next = d->head;
d->head = elem; d->head = elem;
d->num_entries++;
} }
} }
...@@ -129,6 +144,7 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **value) ...@@ -129,6 +144,7 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **value)
struct dictionary_entry **prior; struct dictionary_entry **prior;
BOOL ret = FALSE; BOOL ret = FALSE;
TRACE("(%p, %p, %p)\n", d, k, value);
if (!d) if (!d)
return FALSE; return FALSE;
if (!value) if (!value)
...@@ -138,6 +154,7 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **value) ...@@ -138,6 +154,7 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **value)
*value = (*prior)->value; *value = (*prior)->value;
ret = TRUE; ret = TRUE;
} }
TRACE("returning %d (%p)\n", ret, *value);
return ret; return ret;
} }
...@@ -145,6 +162,7 @@ void dictionary_remove(struct dictionary *d, const void *k) ...@@ -145,6 +162,7 @@ void dictionary_remove(struct dictionary *d, const void *k)
{ {
struct dictionary_entry **prior, *temp; struct dictionary_entry **prior, *temp;
TRACE("(%p, %p)\n", d, k);
if (!d) if (!d)
return; return;
if ((prior = dictionary_find_internal(d, k))) if ((prior = dictionary_find_internal(d, k)))
...@@ -154,17 +172,20 @@ void dictionary_remove(struct dictionary *d, const void *k) ...@@ -154,17 +172,20 @@ void dictionary_remove(struct dictionary *d, const void *k)
d->destroy((*prior)->key, (*prior)->value, d->extra); d->destroy((*prior)->key, (*prior)->value, d->extra);
*prior = (*prior)->next; *prior = (*prior)->next;
HeapFree(GetProcessHeap(), 0, temp); HeapFree(GetProcessHeap(), 0, temp);
d->num_entries--;
} }
} }
void dictionary_enumerate(struct dictionary *d, enumeratefunc e) void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure)
{ {
struct dictionary_entry *p; struct dictionary_entry *p;
TRACE("(%p, %p, %p)\n", d, e, closure);
if (!d) if (!d)
return; return;
if (!e) if (!e)
return; return;
for (p = d->head; p; p = p->next) for (p = d->head; p; p = p->next)
e(p->key, p->value, d->extra); if (!e(p->key, p->value, d->extra, closure))
break;
} }
...@@ -42,7 +42,8 @@ typedef void (*destroyfunc)(void *k, void *v, void *extra); ...@@ -42,7 +42,8 @@ typedef void (*destroyfunc)(void *k, void *v, void *extra);
/* Called for each element in the dictionary. Return FALSE if you don't want /* Called for each element in the dictionary. Return FALSE if you don't want
* to enumerate any more. * to enumerate any more.
*/ */
typedef BOOL (*enumeratefunc)(const void *k, const void *d, void *extra); typedef BOOL (*enumeratefunc)(const void *k, const void *v, void *extra,
void *closure);
/* Constructs a dictionary, using c as a comparison function for keys. /* Constructs a dictionary, using c as a comparison function for keys.
* If d is not NULL, it will be called whenever an item is about to be removed * If d is not NULL, it will be called whenever an item is about to be removed
...@@ -56,6 +57,11 @@ struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra); ...@@ -56,6 +57,11 @@ struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra);
/* Assumes d is not NULL. */ /* Assumes d is not NULL. */
void dictionary_destroy(struct dictionary *d); void dictionary_destroy(struct dictionary *d);
/* Returns how many entries have been stored in the dictionary. If two values
* with the same key are inserted, only one is counted.
*/
UINT dictionary_num_entries(struct dictionary *d);
/* Sets an element with key k and value v to the dictionary. If a value /* Sets an element with key k and value v to the dictionary. If a value
* already exists with key k, its value is replaced, and the destroyfunc (if * already exists with key k, its value is replaced, and the destroyfunc (if
* set) is called for the previous item. * set) is called for the previous item.
...@@ -82,6 +88,6 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **v); ...@@ -82,6 +88,6 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **v);
*/ */
void dictionary_remove(struct dictionary *d, const void *k); void dictionary_remove(struct dictionary *d, const void *k);
void dictionary_enumerate(struct dictionary *d, enumeratefunc e); void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure);
#endif /* ndef __DICTIONARY_H__ */ #endif /* ndef __DICTIONARY_H__ */
...@@ -2,5 +2,6 @@ Makefile ...@@ -2,5 +2,6 @@ Makefile
marshal.ok marshal.ok
moniker.ok moniker.ok
propvariant.ok propvariant.ok
stg_prop.ok
storage32.ok storage32.ok
testlist.c testlist.c
...@@ -10,6 +10,7 @@ CTESTS = \ ...@@ -10,6 +10,7 @@ CTESTS = \
marshal.c \ marshal.c \
moniker.c \ moniker.c \
propvariant.c \ propvariant.c \
stg_prop.c \
storage32.c storage32.c
@MAKE_TEST_RULES@ @MAKE_TEST_RULES@
......
/* IPropertyStorage unit tests
* Copyright 2005 Juan Lang
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#define COBJMACROS
#include "objbase.h"
#include "wine/test.h"
#ifdef NONAMELESSUNION
# define U(x) (x).u
#else
# define U(x) (x)
#endif
/* FIXME: this creates an ANSI storage, need try to find conditions under which
* Unicode translation fails
*/
static void testProps(void)
{
static const WCHAR szDot[] = { '.',0 };
static const WCHAR szPrefix[] = { 's','t','g',0 };
static const WCHAR propName[] = { 'p','r','o','p',0 };
WCHAR filename[MAX_PATH];
HRESULT hr;
IStorage *storage = NULL;
IPropertySetStorage *propSetStorage = NULL;
IPropertyStorage *propertyStorage = NULL;
PROPSPEC spec;
PROPVARIANT var;
if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
return;
DeleteFileW(filename);
hr = StgCreateDocfile(filename,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
ok(SUCCEEDED(hr), "StgCreateDocfile failed: 0x%08lx\n", hr);
hr = StgCreatePropSetStg(storage, 0, &propSetStorage);
ok(SUCCEEDED(hr), "StgCreatePropSetStg failed: 0x%08lx\n", hr);
hr = IPropertySetStorage_Create(propSetStorage,
&FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI,
STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
&propertyStorage);
ok(SUCCEEDED(hr), "QI -> IPropertyStorage failed: 0x%08lx\n", hr);
hr = IPropertyStorage_WriteMultiple(propertyStorage, 0, NULL, NULL, 0);
ok(SUCCEEDED(hr), "WriteMultiple with 0 args failed: 0x%08lx\n", hr);
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, NULL, NULL, 0);
ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
/* test setting one that I can't set */
spec.ulKind = PRSPEC_PROPID;
U(spec).propid = PID_DICTIONARY;
PropVariantClear(&var);
var.vt = VT_I4;
U(var).lVal = 1;
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
ok(hr == STG_E_INVALIDPARAMETER,
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
/* test setting one by name with an invalid propidNameFirst */
spec.ulKind = PRSPEC_LPWSTR;
U(spec).lpwstr = (LPOLESTR)propName;
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var,
PID_DICTIONARY);
ok(hr == STG_E_INVALIDPARAMETER,
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
/* test setting behavior (case-sensitive) */
spec.ulKind = PRSPEC_PROPID;
U(spec).propid = PID_BEHAVIOR;
U(var).lVal = 1;
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
ok(hr == STG_E_INVALIDPARAMETER,
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
/* set one by value.. */
spec.ulKind = PRSPEC_PROPID;
U(spec).propid = PID_FIRST_USABLE;
U(var).lVal = 1;
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
/* finally, set one by name */
spec.ulKind = PRSPEC_LPWSTR;
U(spec).lpwstr = (LPOLESTR)propName;
U(var).lVal = 2;
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var,
PID_FIRST_USABLE);
ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
/* check reading */
hr = IPropertyStorage_ReadMultiple(propertyStorage, 0, NULL, NULL);
ok(SUCCEEDED(hr), "ReadMultiple with 0 args failed: 0x%08lx\n", hr);
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, NULL, NULL);
ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
/* read by propid */
spec.ulKind = PRSPEC_PROPID;
U(spec).propid = PID_FIRST_USABLE;
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
ok(var.vt == VT_I4 && U(var).lVal == 1,
"Didn't get expected type or value for property (got type %d, value %ld)\n",
var.vt, U(var).lVal);
/* read by name */
spec.ulKind = PRSPEC_LPWSTR;
U(spec).lpwstr = (LPOLESTR)propName;
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
ok(var.vt == VT_I4 && U(var).lVal == 2,
"Didn't get expected type or value for property (got type %d, value %ld)\n",
var.vt, U(var).lVal);
/* check deleting */
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 0, NULL);
ok(SUCCEEDED(hr), "DeleteMultiple with 0 args failed: 0x%08lx\n", hr);
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, NULL);
ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
/* contrary to what the docs say, you can't delete the dictionary */
spec.ulKind = PRSPEC_PROPID;
U(spec).propid = PID_DICTIONARY;
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec);
ok(hr == STG_E_INVALIDPARAMETER,
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
/* now delete the first value.. */
U(spec).propid = PID_FIRST_USABLE;
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec);
ok(SUCCEEDED(hr), "DeleteMultiple failed: 0x%08lx\n", hr);
/* and check that it's no longer readable */
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
ok(hr == S_FALSE, "Expected S_FALSE, got 0x%08lx\n", hr);
IPropertyStorage_Release(propertyStorage);
IPropertySetStorage_Release(propSetStorage);
IStorage_Release(storage);
DeleteFileW(filename);
}
START_TEST(stg_prop)
{
testProps();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment