From: Rémi Bernon rbernon@codeweavers.com
--- .../tests/Makefile.in | 10 +- .../tests/application.c | 33 ++ .../tests/appxmanifest.xml | 26 ++ dlls/windows.applicationmodel/tests/model.c | 326 ++++++++++++++++++ .../tests/resource.rc | 22 ++ 5 files changed, 414 insertions(+), 3 deletions(-) create mode 100644 dlls/windows.applicationmodel/tests/application.c create mode 100644 dlls/windows.applicationmodel/tests/appxmanifest.xml create mode 100644 dlls/windows.applicationmodel/tests/resource.rc
diff --git a/dlls/windows.applicationmodel/tests/Makefile.in b/dlls/windows.applicationmodel/tests/Makefile.in index 265144f097c..2efd8ab7861 100644 --- a/dlls/windows.applicationmodel/tests/Makefile.in +++ b/dlls/windows.applicationmodel/tests/Makefile.in @@ -1,5 +1,9 @@ TESTDLL = windows.applicationmodel.dll -IMPORTS = combase +IMPORTS = combase advapi32
-C_SRCS = \ - model.c +application_EXTRADLLFLAGS = -Wl,--subsystem,console + +SOURCES = \ + model.c \ + application.c \ + resource.rc diff --git a/dlls/windows.applicationmodel/tests/application.c b/dlls/windows.applicationmodel/tests/application.c new file mode 100644 index 00000000000..9a812d6951a --- /dev/null +++ b/dlls/windows.applicationmodel/tests/application.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 Rémi Bernon 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 + */ + +#if 0 +#pragma makedep testdll +#endif + +#define COBJMACROS +#include <stddef.h> +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" + +int main( int argc, char const *argv[] ) +{ + return 0; +} diff --git a/dlls/windows.applicationmodel/tests/appxmanifest.xml b/dlls/windows.applicationmodel/tests/appxmanifest.xml new file mode 100644 index 00000000000..33cf6ae6c1c --- /dev/null +++ b/dlls/windows.applicationmodel/tests/appxmanifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" + xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" + xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedca... + <Identity Name="WineTest" Publisher="CN=Wine, O=The Wine Project, C=US" Version="1.0.0.0" ProcessorArchitecture="neutral" /> + <Properties> + <DisplayName>WineTest</DisplayName> + <PublisherDisplayName>The Wine Project</PublisherDisplayName> + <Description>Reserved</Description> + <Logo>logo.png</Logo> + </Properties> + <Resources> + <Resource Language="en-us" /> + </Resources> + <Dependencies> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.0.0" MaxVersionTested="10.0.65535.0" /> + </Dependencies> + <Capabilities> + <rescap:Capability Name="runFullTrust" /> + </Capabilities> + <Applications> + <Application Id="WineTest" Executable="application.exe" EntryPoint="Windows.FullTrustApplication"> + <uap:VisualElements BackgroundColor="transparent" DisplayName="WineTest" Square150x150Logo="logo.png" Square44x44Logo="logo.png" Description="WineTest" /> + </Application> + </Applications> +</Package> diff --git a/dlls/windows.applicationmodel/tests/model.c b/dlls/windows.applicationmodel/tests/model.c index 50513e45fd4..b15024f6ca9 100644 --- a/dlls/windows.applicationmodel/tests/model.c +++ b/dlls/windows.applicationmodel/tests/model.c @@ -38,6 +38,94 @@
#include "wine/test.h"
+#define DEFINE_ASYNC_COMPLETED_HANDLER( name, iface_type, async_type ) \ + struct name \ + { \ + iface_type iface_type##_iface; \ + LONG refcount; \ + BOOL invoked; \ + HANDLE event; \ + }; \ + \ + static HRESULT WINAPI name##_QueryInterface( iface_type *iface, REFIID iid, void **out ) \ + { \ + if (IsEqualGUID( iid, &IID_IUnknown ) || IsEqualGUID( iid, &IID_IAgileObject ) || \ + IsEqualGUID( iid, &IID_##iface_type )) \ + { \ + IUnknown_AddRef( iface ); \ + *out = iface; \ + return S_OK; \ + } \ + \ + trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); \ + *out = NULL; \ + return E_NOINTERFACE; \ + } \ + \ + static ULONG WINAPI name##_AddRef( iface_type *iface ) \ + { \ + struct name *impl = CONTAINING_RECORD( iface, struct name, iface_type##_iface ); \ + return InterlockedIncrement( &impl->refcount ); \ + } \ + \ + static ULONG WINAPI name##_Release( iface_type *iface ) \ + { \ + struct name *impl = CONTAINING_RECORD( iface, struct name, iface_type##_iface ); \ + ULONG ref = InterlockedDecrement( &impl->refcount ); \ + if (!ref) free( impl ); \ + return ref; \ + } \ + \ + static HRESULT WINAPI name##_Invoke( iface_type *iface, async_type *async, AsyncStatus status ) \ + { \ + struct name *impl = CONTAINING_RECORD( iface, struct name, iface_type##_iface ); \ + ok( !impl->invoked, "invoked twice\n" ); \ + impl->invoked = TRUE; \ + if (impl->event) SetEvent( impl->event ); \ + return S_OK; \ + } \ + \ + static iface_type##Vtbl name##_vtbl = \ + { \ + name##_QueryInterface, \ + name##_AddRef, \ + name##_Release, \ + name##_Invoke, \ + }; \ + \ + static iface_type *name##_create( HANDLE event ) \ + { \ + struct name *impl; \ + \ + if (!(impl = calloc( 1, sizeof(*impl) ))) return NULL; \ + impl->iface_type##_iface.lpVtbl = &name##_vtbl; \ + impl->event = event; \ + impl->refcount = 1; \ + \ + return &impl->iface_type##_iface; \ + } \ + \ + static DWORD await_##async_type( async_type *async, DWORD timeout ) \ + { \ + iface_type *handler; \ + HANDLE event; \ + HRESULT hr; \ + DWORD ret; \ + \ + event = CreateEventW( NULL, FALSE, FALSE, NULL ); \ + ok( !!event, "CreateEventW failed, error %lu\n", GetLastError() ); \ + handler = name##_create( event ); \ + ok( !!handler, "Failed to create completion handler\n" ); \ + hr = async_type##_put_Completed( async, handler ); \ + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); \ + ret = WaitForSingleObject( event, timeout ); \ + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); \ + CloseHandle( event ); \ + iface_type##_Release( handler ); \ + \ + return ret; \ + } + #define check_interface( obj, iid ) check_interface_( __LINE__, obj, iid ) static void check_interface_( unsigned int line, void *obj, const IID *iid ) { @@ -51,6 +139,238 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid ) IUnknown_Release( unk ); }
+static void load_resource( const WCHAR *name, const WCHAR *type, const WCHAR *filename ) +{ + DWORD written; + HANDLE file; + HRSRC res; + void *ptr; + + file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "failed to create %s, error %lu\n", debugstr_w(filename), GetLastError() ); + + res = FindResourceW( NULL, name, type ); + ok( res != 0, "couldn't find resource\n" ); + ptr = LockResource( LoadResource( GetModuleHandleW( NULL ), res ) ); + WriteFile( file, ptr, SizeofResource( GetModuleHandleW( NULL ), res ), &written, NULL ); + ok( written == SizeofResource( GetModuleHandleW( NULL ), res ), "couldn't write resource\n" ); + CloseHandle( file ); +} + +#define check_async_info( a, b, c ) check_async_info_( __LINE__, a, b, c ) +static void check_async_info_( int line, void *async, AsyncStatus expect_status, HRESULT expect_hr ) +{ + AsyncStatus async_status; + IAsyncInfo *async_info; + HRESULT hr, async_hr; + UINT32 async_id; + + hr = IInspectable_QueryInterface( async, &IID_IAsyncInfo, (void **)&async_info ); + ok_(__FILE__, line)( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + async_id = 0xdeadbeef; + hr = IAsyncInfo_get_Id( async_info, &async_id ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Id returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Id returned %#lx\n", hr ); + ok_(__FILE__, line)( !!async_id, "got id %u\n", async_id ); + + async_status = 0xdeadbeef; + hr = IAsyncInfo_get_Status( async_info, &async_status ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Status returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Status returned %#lx\n", hr ); + ok_(__FILE__, line)( async_status == expect_status, "got status %u\n", async_status ); + + async_hr = 0xdeadbeef; + hr = IAsyncInfo_get_ErrorCode( async_info, &async_hr ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_ErrorCode returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_ErrorCode returned %#lx\n", hr ); + if (expect_status < 4) todo_wine_if(FAILED(expect_hr)) ok_(__FILE__, line)( async_hr == expect_hr, "got error %#lx\n", async_hr ); + else ok_(__FILE__, line)( async_hr == E_ILLEGAL_METHOD_CALL, "got error %#lx\n", async_hr ); + + IAsyncInfo_Release( async_info ); +} + +DEFINE_ASYNC_COMPLETED_HANDLER( async_deployment_result_handler, IAsyncOperationWithProgressCompletedHandler_DeploymentResult_DeploymentProgress, + IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress ) + +static HRESULT uri_create( const WCHAR *uri, IUriRuntimeClass **out ) +{ + static const WCHAR *statics_name = RuntimeClass_Windows_Foundation_Uri; + IUriRuntimeClassFactory *uri_factory; + IActivationFactory *activation; + HSTRING str; + HRESULT hr; + + hr = WindowsCreateString( statics_name, wcslen( statics_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&activation ); + WindowsDeleteString( str ); + + hr = IActivationFactory_QueryInterface( activation, &IID_IUriRuntimeClassFactory, (void **)&uri_factory ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IActivationFactory_Release( activation ); + + hr = WindowsCreateString( uri, wcslen( uri ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IUriRuntimeClassFactory_CreateUri( uri_factory, str, out ); + WindowsDeleteString( str ); + + IUriRuntimeClassFactory_Release( uri_factory ); + return hr; +} + +static HRESULT test_register_package( IPackageManager *manager, IPackage **out ) +{ + static const WCHAR app_model_unlock[] = L"SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"; + IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress *operation; + DWORD size, old_value = 0, new_value = 1; + WCHAR temp[MAX_PATH], path[MAX_PATH + 7]; + IStorageFolder *storage_folder; + HSTRING str, name, publisher; + IIterable_Package *packages; + IStorageItem *storage_item; + IDeploymentResult *result; + IIterator_Package *iter; + IUriRuntimeClass *uri; + HRESULT hr, async_hr; + IPackage *package; + const WCHAR *buf; + UINT res, len; + HKEY hkey; + + GetTempPathW( ARRAY_SIZE(temp), temp ); + GetTempFileNameW( temp, L"winetest-winrt", 0, temp ); + DeleteFileW( temp ); + CreateDirectoryW( temp, NULL ); + + swprintf( path, MAX_PATH, L"%s\%s", temp, L"application.exe" ); + load_resource( L"application.exe", L"TESTDLL", path ); + swprintf( path, MAX_PATH, L"%s\%s", temp, L"appxmanifest.xml" ); + load_resource( L"appxmanifest.xml", (const WCHAR *)RT_RCDATA, path ); + + swprintf( path, MAX_PATH, L"file://%s\%s", temp, L"appxmanifest.xml" ); + hr = uri_create( path, &uri ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + res = RegGetValueW( HKEY_LOCAL_MACHINE, app_model_unlock, L"AllowDevelopmentWithoutDevLicense", RRF_RT_DWORD, NULL, + (BYTE *)&old_value, &size ); + if (res) old_value = 0xdeadbeef; + + res = RegCreateKeyExW( HKEY_LOCAL_MACHINE, app_model_unlock, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ); + ok( !res, "RegCreateKeyExW failed, res %#x\n", res ); + res = RegSetValueExW( hkey, L"AllowDevelopmentWithoutDevLicense", 0, REG_DWORD, (const BYTE *)&new_value, sizeof(new_value) ); + ok( !res, "RegCreateKeyExW failed, res %#x\n", res ); + + /* DeploymentOptions_DevelopmentMode is invalid for AddPackageAsync */ + hr = IPackageManager_AddPackageAsync( manager, uri, NULL, DeploymentOptions_DevelopmentMode, &operation ); + ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); + + hr = IPackageManager_RegisterPackageAsync( manager, uri, NULL, DeploymentOptions_DevelopmentMode, &operation ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( operation != NULL, "got operation %p\n", operation ); + IUriRuntimeClass_Release( uri ); + + res = await_IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress( operation, 5000 ); + ok( res == 0, "await_IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress returned %#x\n", res ); + + hr = IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress_GetResults( operation, &result ); + ok( hr == S_OK, "GetResults returned %#lx\n", hr ); + IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress_Release( operation ); + + if (old_value != 0xdeadbeef) + { + res = RegSetValueExW( hkey, L"AllowDevelopmentWithoutDevLicense", 0, REG_DWORD, (const BYTE *)&old_value, sizeof(old_value) ); + ok( !res, "RegCreateKeyExW failed, res %#x\n", res ); + } + + RegCloseKey( hkey ); + + hr = IDeploymentResult_get_ExtendedErrorCode( result, &async_hr ); + ok( hr == S_OK, "get_ExtendedErrorCode returned %#lx\n", hr ); + ok( async_hr == S_OK || broken( async_hr == 0x80073cff ) /* 32-bit missing license */ || + broken( async_hr == 0x80080204 ) /* W8 */ || broken( async_hr == 0x8004264b ) /* w1064v1507 */, + "got error %#lx\n", async_hr ); + + if (FAILED(async_hr)) + { + win_skip("Failed to register package, skipping tests\n"); + hr = IDeploymentResult_get_ErrorText( result, &str ); + ok( hr == S_OK, "get_ExtendedErrorCode returned %#lx\n", hr ); + trace( "got error %s\n", debugstr_hstring(str) ); + IDeploymentResult_Release( result ); + return async_hr; + } + + IDeploymentResult_Release( result ); + + hr = WindowsCreateString( L"WineTest", wcslen( L"WineTest" ), &name ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = WindowsCreateString( L"CN=Wine, O=The Wine Project, C=US", wcslen( L"CN=Wine, O=The Wine Project, C=US" ), &publisher ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IPackageManager_FindPackagesByNamePublisher( manager, name, publisher, &packages ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( name ); + WindowsDeleteString( publisher ); + + hr = IIterable_Package_First( packages, &iter ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IIterable_Package_Release( packages ); + + hr = IIterator_Package_get_Current( iter, &package ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IIterator_Package_Release( iter ); + + hr = IPackage_get_InstalledLocation( package, &storage_folder ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IStorageFolder_QueryInterface( storage_folder, &IID_IStorageItem, (void **)&storage_item ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IStorageFolder_Release( storage_folder ); + + hr = IStorageItem_get_Path( storage_item, &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IStorageItem_Release( storage_item ); + + buf = WindowsGetStringRawBuffer( str, &len ); + ok( !wcscmp( temp, buf ), "got path %s.\n", debugstr_hstring( str ) ); + WindowsDeleteString( str ); + + *out = package; + return S_OK; +} + +static void test_remove_package( IPackageManager *manager, IPackage *package ) +{ + IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress *operation; + IDeploymentResult *result; + HRESULT hr, async_hr; + IPackageId *id; + HSTRING name; + UINT res; + + hr = IPackage_get_Id( package, &id ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IPackageId_get_FullName( id, &name ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IPackageId_Release( id ); + + hr = IPackageManager_RemovePackageAsync( manager, name, &operation ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( operation != NULL, "got operation %p\n", operation ); + + res = await_IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress( operation, 5000 ); + ok( res == 0, "await_IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress returned %#x\n", res ); + check_async_info( operation, Completed, S_OK ); + + hr = IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress_GetResults( operation, &result ); + ok( hr == S_OK, "GetResults returned %#lx\n", hr ); + hr = IDeploymentResult_get_ExtendedErrorCode( result, &async_hr ); + ok( hr == S_OK, "get_ExtendedErrorCode returned %#lx\n", hr ); + ok( async_hr == S_OK, "got error %#lx\n", async_hr ); + IDeploymentResult_Release( result ); + + IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress_Release( operation ); +} + static void test_PackageManager(void) { static const WCHAR *statics_name = RuntimeClass_Windows_Management_Deployment_PackageManager; @@ -120,6 +440,12 @@ static void test_PackageManager(void) ref = IIterable_Package_Release( packages ); ok( !ref, "got ref %ld.\n", ref );
+ if (SUCCEEDED(test_register_package( manager, &package ))) + { + test_remove_package( manager, package ); + IPackage_Release( package ); + } + skip_tests: ref = IPackageManager_Release( manager ); ok( !ref, "got ref %ld.\n", ref ); diff --git a/dlls/windows.applicationmodel/tests/resource.rc b/dlls/windows.applicationmodel/tests/resource.rc new file mode 100644 index 00000000000..a78e2afd7d9 --- /dev/null +++ b/dlls/windows.applicationmodel/tests/resource.rc @@ -0,0 +1,22 @@ +/* + * Copyright 2022 Rémi Bernon 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 "windef.h" + +/* @makedep: appxmanifest.xml */ +appxmanifest.xml RCDATA appxmanifest.xml