/*
 * comctl32 MRU unit tests
 *
 * Copyright (C) 2004 Jon Griffiths <jon_p_griffiths@yahoo.com>
 *
 * 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 <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "winreg.h"
#include "commctrl.h"
#include "shlwapi.h"

#include "wine/heap.h"
#include "wine/test.h"

/* Keys for testing MRU functions */
#define REG_TEST_BASEKEYA    "Software\\Wine"
#define REG_TEST_BASESUBKEYA "Test"
#define REG_TEST_KEYA    REG_TEST_BASEKEYA "\\" REG_TEST_BASESUBKEYA
#define REG_TEST_SUBKEYA "MRUTest"
#define REG_TEST_FULLKEY REG_TEST_KEYA "\\" REG_TEST_SUBKEYA

/* Undocumented MRU functions */
typedef struct tagMRUINFOA
{
    DWORD   cbSize;
    UINT    uMax;
    UINT    fFlags;
    HKEY    hKey;
    LPCSTR  lpszSubKey;
    int (CALLBACK *lpfnCompare)(LPCSTR, LPCSTR);
} MRUINFOA;

typedef struct tagMRUINFOW
{
    DWORD   cbSize;
    UINT    uMax;
    UINT    fFlags;
    HKEY    hKey;
    LPCWSTR lpszSubKey;
    int (CALLBACK *lpfnCompare)(LPCWSTR, LPCWSTR);
} MRUINFOW;

#define MRU_STRING     0  /* this one's invented */
#define MRU_BINARY     1
#define MRU_CACHEWRITE 2

#define LIST_SIZE 3 /* Max entries for each mru */

static HMODULE hComctl32;
static HANDLE (WINAPI *pCreateMRUListA)(MRUINFOA*);
static void   (WINAPI *pFreeMRUList)(HANDLE);
static INT    (WINAPI *pAddMRUStringA)(HANDLE,LPCSTR);
static INT    (WINAPI *pEnumMRUListA)(HANDLE,INT,LPVOID,DWORD);
static INT    (WINAPI *pEnumMRUListW)(HANDLE,INT,LPVOID,DWORD);
static HANDLE (WINAPI *pCreateMRUListLazyA)(MRUINFOA*, DWORD, DWORD, DWORD);
static HANDLE (WINAPI *pCreateMRUListLazyW)(MRUINFOW*, DWORD, DWORD, DWORD);
static INT    (WINAPI *pFindMRUData)(HANDLE, LPCVOID, DWORD, LPINT);
static INT    (WINAPI *pAddMRUData)(HANDLE, LPCVOID, DWORD);
static HANDLE (WINAPI *pCreateMRUListW)(MRUINFOW*);

static void init_functions(void)
{
    hComctl32 = LoadLibraryA("comctl32.dll");

#define X2(f, ord) p##f = (void*)GetProcAddress(hComctl32, (const char *)ord);
    X2(CreateMRUListA, 151);
    X2(FreeMRUList, 152);
    X2(AddMRUStringA, 153);
    X2(EnumMRUListA, 154);
    X2(CreateMRUListLazyA, 157);
    X2(AddMRUData, 167);
    X2(FindMRUData, 169);
    X2(CreateMRUListW, 400);
    X2(EnumMRUListW, 403);
    X2(CreateMRUListLazyW, 404);
#undef X2
}

/* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
{
    LONG ret;
    DWORD dwMaxSubkeyLen, dwMaxValueLen;
    DWORD dwMaxLen, dwSize;
    CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
    HKEY hSubKey = hKey;

    if(lpszSubKey)
    {
        ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
        if (ret) return ret;
    }

    /* Get highest length for keys, values */
    ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL,
            &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
    if (ret) goto cleanup;

    dwMaxSubkeyLen++;
    dwMaxValueLen++;
    dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
    if (dwMaxLen > ARRAY_SIZE(szNameBuf))
    {
        /* Name too big: alloc a buffer for it */
        if (!(lpszName = heap_alloc(dwMaxLen * sizeof(CHAR))))
        {
            ret = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
    }


    /* Recursively delete all the subkeys */
    while (TRUE)
    {
        dwSize = dwMaxLen;
        if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
                          NULL, NULL, NULL)) break;

        ret = mru_RegDeleteTreeA(hSubKey, lpszName);
        if (ret) goto cleanup;
    }

    if (lpszSubKey)
        ret = RegDeleteKeyA(hKey, lpszSubKey);
    else
        while (TRUE)
        {
            dwSize = dwMaxLen;
            if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
                  NULL, NULL, NULL, NULL)) break;

            ret = RegDeleteValueA(hKey, lpszName);
            if (ret) goto cleanup;
        }

cleanup:
    /* Free buffer if allocated */
    if (lpszName != szNameBuf)
        heap_free(lpszName);
    if(lpszSubKey)
        RegCloseKey(hSubKey);
    return ret;
}

static BOOL create_reg_entries(void)
{
    HKEY hKey = NULL;

    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
    if (!hKey) return FALSE;
    RegCloseKey(hKey);
    return TRUE;
}

static void delete_reg_entries(void)
{
    HKEY hKey;

    if (RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_BASEKEYA, 0, KEY_ALL_ACCESS,
                      &hKey))
        return;
    mru_RegDeleteTreeA(hKey, REG_TEST_BASESUBKEYA);
    RegCloseKey(hKey);
}

static void check_reg_entries(const char *mrulist, const char**items)
{
    char buff[128];
    HKEY hKey = NULL;
    DWORD type, size, ret;
    unsigned int i;

    ok(!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
       "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY);
    if (!hKey) return;

    type = REG_SZ;
    size = sizeof(buff);
    buff[0] = '\0';
    ret = RegQueryValueExA(hKey, "MRUList", NULL, &type, (LPBYTE)buff, &size);

    ok(!ret && buff[0], "Checking MRU: got %d from RegQueryValueExW\n", ret);
    if(ret || !buff[0]) return;

    ok(strcmp(buff, mrulist) == 0, "Checking MRU: Expected list %s, got %s\n",
       mrulist, buff);
    if(strcmp(buff, mrulist)) return;

    for (i = 0; i < strlen(mrulist); i++)
    {
        char name[2];
        name[0] = mrulist[i];
        name[1] = '\0';
        type = REG_SZ;
        size = sizeof(buff);
        buff[0] = '\0';
        ret = RegQueryValueExA(hKey, name, NULL, &type, (LPBYTE)buff, &size);
        ok(!ret && buff[0],
           "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
           i, mrulist[i], ret);
        if(ret || !buff[0]) return;
        ok(!strcmp(buff, items[mrulist[i]-'a']),
           "Checking MRU item %d ('%c'): expected \"%s\", got \"%s\"\n",
           i, mrulist[i], buff, items[mrulist[i] - 'a']);
    }
}

static int CALLBACK cmp_mru_strA(LPCSTR data1, LPCSTR data2)
{
    return lstrcmpiA(data1, data2);
}

static void test_MRUListA(void)
{
    const char *checks[LIST_SIZE+1];
    MRUINFOA infoA;
    HANDLE hMRU;
    HKEY hKey;
    INT iRet;

    if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA || !pEnumMRUListA)
    {
        win_skip("MRU entry points not found\n");
        return;
    }

    if (0)
    {
    /* Create (NULL) - crashes native */
    hMRU = pCreateMRUListA(NULL);
    }

    /* size too small */
    infoA.cbSize = sizeof(infoA) - 2;
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
    ok (!hMRU && !GetLastError(),
        "CreateMRUListA(too small) expected NULL,0 got %p,%d\n",
        hMRU, GetLastError());

    /* size too big */
    infoA.cbSize = sizeof(infoA) + 2;
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
    ok (!hMRU && !GetLastError(),
        "CreateMRUListA(too big) expected NULL,0 got %p,%d\n",
        hMRU, GetLastError());

    /* NULL hKey */
    infoA.cbSize = sizeof(infoA);
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
    ok (!hMRU && !GetLastError(),
        "CreateMRUListA(NULL key) expected NULL,0 got %p,%d\n",
        hMRU, GetLastError());

    /* NULL subkey name */
    infoA.cbSize = sizeof(infoA);
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = NULL;
    infoA.lpfnCompare = cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
    ok (!hMRU && !GetLastError(),
        "CreateMRUListA(NULL name) expected NULL,0 got %p,%d\n",
        hMRU, GetLastError());

    /* Create a string MRU */
    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
    if (!hKey) return;

    infoA.cbSize = sizeof(infoA);
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = hKey;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = cmp_mru_strA;

    hMRU = pCreateMRUListA(&infoA);
    ok(hMRU && !GetLastError(),
       "CreateMRUListA(string) expected non-NULL,0 got %p,%d\n",
       hMRU, GetLastError());

    if (hMRU)
    {
        char buffer[255];
        checks[0] = "Test 1";
        checks[1] = "Test 2";
        checks[2] = "Test 3";
        checks[3] = "Test 4";

        /* Add (NULL list) */
        SetLastError(0);
        iRet = pAddMRUStringA(NULL, checks[0]);
        ok(iRet == -1 && !GetLastError(),
           "AddMRUStringA(NULL list) expected -1,0 got %d,%d\n",
           iRet, GetLastError());

        /* Add (NULL string) */
        if (0)
        {
	/* Some native versions crash when passed NULL or fail to SetLastError()  */
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, NULL);
        ok(iRet == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
           "AddMRUStringA(NULL str) expected 0,ERROR_INVALID_PARAMETER got %d,%d\n",
           iRet, GetLastError());
        }

        /* Add 3 strings. Check the registry is correct after each add */
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[0]);
        ok(iRet == 0 && !GetLastError(),
           "AddMRUStringA(1) expected 0,0 got %d,%d\n",
           iRet, GetLastError());
        check_reg_entries("a", checks);

        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[1]);
        ok(iRet == 1 && !GetLastError(),
           "AddMRUStringA(2) expected 1,0 got %d,%d\n",
           iRet, GetLastError());
        check_reg_entries("ba", checks);

        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[2]);
        ok(iRet == 2 && !GetLastError(),
           "AddMRUStringA(2) expected 2,0 got %d,%d\n",
           iRet, GetLastError());
        check_reg_entries("cba", checks);

        /* Add a duplicate of the 2nd string - it should move to the front,
         * but keep the same index in the registry.
         */
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[1]);
        ok(iRet == 1 && !GetLastError(),
           "AddMRUStringA(re-add 1) expected 1,0 got %d,%d\n",
           iRet, GetLastError());
        check_reg_entries("bca", checks);

        /* Add a new string - replaces the oldest string + moves to the front */
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[3]);
        ok(iRet == 0 && !GetLastError(),
           "AddMRUStringA(add new) expected 0,0 got %d,%d\n",
           iRet, GetLastError());
        checks[0] = checks[3];
        check_reg_entries("abc", checks);

        /* NULL buffer = get list size */
        iRet = pEnumMRUListA(hMRU, 0, NULL, 0);
        ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);

        /* negative item pos = get list size */
        iRet = pEnumMRUListA(hMRU, -1, NULL, 0);
        ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);

        /* negative item pos = get list size */
        iRet = pEnumMRUListA(hMRU, -5, NULL, 0);
        ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);

        /* negative item pos = get list size */
        iRet = pEnumMRUListA(hMRU, -1, buffer, 255);
        ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);

        /* negative item pos = get list size */
        iRet = pEnumMRUListA(hMRU, -5, buffer, 255);
        ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);

        /* check entry 0 */
        buffer[0] = 0;
        iRet = pEnumMRUListA(hMRU, 0, buffer, 255);
        ok(iRet == lstrlenA(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[3]), iRet);
        ok(strcmp(buffer, checks[3]) == 0, "EnumMRUList expected %s, got %s\n", checks[3], buffer);

        /* check entry 0 with a too small buffer */
        buffer[0] = 0;   /* overwritten with 'T' */
        buffer[1] = 'A'; /* overwritten with 0   */
        buffer[2] = 'A'; /* unchanged */
        buffer[3] = 0;   /* unchanged */
        iRet = pEnumMRUListA(hMRU, 0, buffer, 2);
        ok(iRet == lstrlenA(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[3]), iRet);
        ok(strcmp(buffer, "T") == 0, "EnumMRUList expected %s, got %s\n", "T", buffer);
        /* make sure space after buffer has old values */
        ok(buffer[2] == 'A', "EnumMRUList expected %02x, got %02x\n", 'A', buffer[2]);

        /* check entry 1 */
        buffer[0] = 0;
        iRet = pEnumMRUListA(hMRU, 1, buffer, 255);
        ok(iRet == lstrlenA(checks[1]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[1]), iRet);
        ok(strcmp(buffer, checks[1]) == 0, "EnumMRUList expected %s, got %s\n", checks[1], buffer);

        /* check entry 2 */
        buffer[0] = 0;
        iRet = pEnumMRUListA(hMRU, 2, buffer, 255);
        ok(iRet == lstrlenA(checks[2]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[2]), iRet);
        ok(strcmp(buffer, checks[2]) == 0, "EnumMRUList expected %s, got %s\n", checks[2], buffer);

        /* check out of bounds entry 3 */
        strcpy(buffer, "dummy");
        iRet = pEnumMRUListA(hMRU, 3, buffer, 255);
        ok(iRet == -1, "EnumMRUList expected %d, got %d\n", -1, iRet);
        ok(strcmp(buffer, "dummy") == 0, "EnumMRUList expected unchanged buffer %s, got %s\n", "dummy", buffer);

        /* Finished with this MRU */
        pFreeMRUList(hMRU);
    }

    /* FreeMRUList(NULL) crashes on Win98 OSR0 */
}

typedef struct {
    MRUINFOA mruA;
    BOOL ret;
} create_lazya_t;

static const create_lazya_t create_lazyA[] = {
    {{ sizeof(MRUINFOA) + 1, 0, 0, HKEY_CURRENT_USER, NULL, NULL }, FALSE },
    {{ sizeof(MRUINFOA) - 1, 0, 0, HKEY_CURRENT_USER, NULL, NULL }, FALSE },
    {{ sizeof(MRUINFOA) + 1, 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE },
    {{ sizeof(MRUINFOA) - 1, 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE },
    {{ sizeof(MRUINFOA), 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE },
    {{ sizeof(MRUINFOA), 0, 0, HKEY_CURRENT_USER, NULL, NULL }, FALSE },
    {{ sizeof(MRUINFOA), 0, 0, NULL, "WineTest", NULL }, FALSE },
    {{ 0, 0, 0, NULL, "WineTest", NULL }, FALSE },
    {{ 0, 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE }
};

static void test_CreateMRUListLazyA(void)
{
    int i;

    if (!pCreateMRUListLazyA || !pFreeMRUList)
    {
        win_skip("CreateMRUListLazyA or FreeMRUList entry points not found\n");
        return;
    }

    for (i = 0; i < ARRAY_SIZE(create_lazyA); i++)
    {
        const create_lazya_t *ptr = &create_lazyA[i];
        HANDLE hMRU;

        hMRU = pCreateMRUListLazyA((MRUINFOA*)&ptr->mruA, 0, 0, 0);
        if (ptr->ret)
        {
            ok(hMRU != NULL, "%d: got %p\n", i, hMRU);
            pFreeMRUList(hMRU);
        }
        else
            ok(hMRU == NULL, "%d: got %p\n", i, hMRU);
    }
}

static void test_EnumMRUList(void)
{
    if (!pEnumMRUListA || !pEnumMRUListW)
    {
        win_skip("EnumMRUListA/EnumMRUListW entry point not found\n");
        return;
    }

    /* NULL handle */
    if (0)
    {
        /* crashes on NT4, passed on Win2k, XP, 2k3, Vista, 2k8 */
        pEnumMRUListA(NULL, 0, NULL, 0);
        pEnumMRUListW(NULL, 0, NULL, 0);
    }
}

static void test_FindMRUData(void)
{
    INT iRet;

    if (!pFindMRUData)
    {
        win_skip("FindMRUData entry point not found\n");
        return;
    }

    /* NULL handle */
    iRet = pFindMRUData(NULL, NULL, 0, NULL);
    ok(iRet == -1, "FindMRUData expected -1, got %d\n", iRet);
}

static void test_AddMRUData(void)
{
    INT iRet;

    if (!pAddMRUData)
    {
        win_skip("AddMRUData entry point not found\n");
        return;
    }

    /* NULL handle */
    iRet = pFindMRUData(NULL, NULL, 0, NULL);
    ok(iRet == -1, "AddMRUData expected -1, got %d\n", iRet);
}

static void test_CreateMRUListW(void)
{
    static const WCHAR mrutestW[] = {'M','R','U','T','e','s','t',0};
    MRUINFOW infoW;
    void *named;
    HKEY hKey;
    HANDLE hMru;

    if (!pCreateMRUListW)
    {
        win_skip("CreateMRUListW entry point not found\n");
        return;
    }

    /* exported by name too on recent versions */
    named = GetProcAddress(hComctl32, "CreateMRUListW");
    if (named)
        ok(named == pCreateMRUListW, "got %p, expected %p\n", named, pCreateMRUListW);

    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);

    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* smaller size */
    infoW.cbSize = sizeof(infoW) - 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* increased size */
    infoW.cbSize = sizeof(infoW) + 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* zero size */
    infoW.cbSize = 0;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* NULL hKey */
    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = NULL;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
    ok(hMru == NULL, "got %p\n", hMru);

    RegCloseKey(hKey);
}

static void test_CreateMRUListLazyW(void)
{
    static const WCHAR mrutestW[] = {'M','R','U','T','e','s','t',0};
    MRUINFOW infoW;
    void *named;
    HKEY hKey;
    HANDLE hMru;

    if (!pCreateMRUListLazyW)
    {
        win_skip("CreateMRUListLazyW entry point not found\n");
        return;
    }

    /* check that it's not exported by name */
    named = GetProcAddress(hComctl32, "CreateMRUListLazyW");
    ok(named == NULL, "got %p\n", named);

    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);

    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* smaller size */
    infoW.cbSize = sizeof(infoW) - 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* increased size */
    infoW.cbSize = sizeof(infoW) + 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* zero size */
    infoW.cbSize = 0;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* NULL hKey */
    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = NULL;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
    ok(hMru == NULL, "got %p\n", hMru);

    RegCloseKey(hKey);
}

START_TEST(mru)
{
    delete_reg_entries();
    if (!create_reg_entries())
        return;

    init_functions();

    test_MRUListA();
    test_CreateMRUListLazyA();
    test_CreateMRUListLazyW();
    test_EnumMRUList();
    test_FindMRUData();
    test_AddMRUData();
    test_CreateMRUListW();

    delete_reg_entries();
}
