This can then be used to run tests in an appx context, for instance for https://gitlab.winehq.org/wine/wine/-/merge_requests/3548
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- configure.ac | 2 + dlls/windows.applicationmodel/Makefile.in | 9 ++ dlls/windows.applicationmodel/classes.idl | 33 +++++ dlls/windows.applicationmodel/main.c | 46 +++++++ dlls/windows.applicationmodel/package.c | 116 ++++++++++++++++++ dlls/windows.applicationmodel/private.h | 40 ++++++ .../tests/Makefile.in | 5 + dlls/windows.applicationmodel/tests/model.c | 88 +++++++++++++ .../windows.applicationmodel.spec | 3 + include/windows.applicationmodel.core.idl | 2 + include/windows.applicationmodel.idl | 2 + 11 files changed, 346 insertions(+) create mode 100644 dlls/windows.applicationmodel/Makefile.in create mode 100644 dlls/windows.applicationmodel/classes.idl create mode 100644 dlls/windows.applicationmodel/main.c create mode 100644 dlls/windows.applicationmodel/package.c create mode 100644 dlls/windows.applicationmodel/private.h create mode 100644 dlls/windows.applicationmodel/tests/Makefile.in create mode 100644 dlls/windows.applicationmodel/tests/model.c create mode 100644 dlls/windows.applicationmodel/windows.applicationmodel.spec
diff --git a/configure.ac b/configure.ac index e17d28c8601..5dd59b10411 100644 --- a/configure.ac +++ b/configure.ac @@ -3136,6 +3136,8 @@ WINE_CONFIG_MAKEFILE(dlls/win32u/tests) WINE_CONFIG_MAKEFILE(dlls/win87em.dll16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/winaspi.dll16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/windebug.dll16,enable_win16) +WINE_CONFIG_MAKEFILE(dlls/windows.applicationmodel) +WINE_CONFIG_MAKEFILE(dlls/windows.applicationmodel/tests) WINE_CONFIG_MAKEFILE(dlls/windows.devices.bluetooth) WINE_CONFIG_MAKEFILE(dlls/windows.devices.bluetooth/tests) WINE_CONFIG_MAKEFILE(dlls/windows.devices.enumeration) diff --git a/dlls/windows.applicationmodel/Makefile.in b/dlls/windows.applicationmodel/Makefile.in new file mode 100644 index 00000000000..7404721796e --- /dev/null +++ b/dlls/windows.applicationmodel/Makefile.in @@ -0,0 +1,9 @@ +MODULE = windows.applicationmodel.dll +IMPORTS = combase + +C_SRCS = \ + main.c \ + package.c + +IDL_SRCS = \ + classes.idl diff --git a/dlls/windows.applicationmodel/classes.idl b/dlls/windows.applicationmodel/classes.idl new file mode 100644 index 00000000000..c0c706787bc --- /dev/null +++ b/dlls/windows.applicationmodel/classes.idl @@ -0,0 +1,33 @@ +/* + * Runtime Classes for windows.applicationmodel.dll + * + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 + */ + +#pragma makedep register + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +import "windows.foundation.idl"; +import "windows.storage.idl"; +import "windows.system.idl"; + +#define DO_NO_IMPORTS +#include "windows.applicationmodel.core.idl" +#include "windows.applicationmodel.idl" diff --git a/dlls/windows.applicationmodel/main.c b/dlls/windows.applicationmodel/main.c new file mode 100644 index 00000000000..efc6dfd292f --- /dev/null +++ b/dlls/windows.applicationmodel/main.c @@ -0,0 +1,46 @@ +/* WinRT Windows.ApplicationModel Implementation + * + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 "initguid.h" +#include "private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(model); + +HRESULT WINAPI DllGetClassObject( REFCLSID clsid, REFIID riid, void **out ) +{ + FIXME( "clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out ); + return CLASS_E_CLASSNOTAVAILABLE; +} + +HRESULT WINAPI DllGetActivationFactory( HSTRING classid, IActivationFactory **factory ) +{ + const WCHAR *buffer = WindowsGetStringRawBuffer( classid, NULL ); + + TRACE( "class %s, factory %p.\n", debugstr_hstring(classid), factory ); + + *factory = NULL; + + if (!wcscmp( buffer, RuntimeClass_Windows_ApplicationModel_Package )) + IActivationFactory_QueryInterface( package_factory, &IID_IActivationFactory, (void **)factory ); + + if (*factory) return S_OK; + return CLASS_E_CLASSNOTAVAILABLE; +} diff --git a/dlls/windows.applicationmodel/package.c b/dlls/windows.applicationmodel/package.c new file mode 100644 index 00000000000..087d96b5ec8 --- /dev/null +++ b/dlls/windows.applicationmodel/package.c @@ -0,0 +1,116 @@ +/* WinRT Windows.ApplicationModel Package Implementation + * + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 "private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(model); + +struct package_statics +{ + IActivationFactory IActivationFactory_iface; + LONG ref; +}; + +static inline struct package_statics *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct package_statics, IActivationFactory_iface ); +} + +static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct package_statics *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + *out = &impl->IActivationFactory_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI factory_AddRef( IActivationFactory *iface ) +{ + struct package_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI factory_Release( IActivationFactory *iface ) +{ + struct package_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI factory_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IActivationFactory methods */ + factory_ActivateInstance, +}; + +static struct package_statics package_statics = +{ + {&factory_vtbl}, + 1, +}; + +IActivationFactory *package_factory = &package_statics.IActivationFactory_iface; diff --git a/dlls/windows.applicationmodel/private.h b/dlls/windows.applicationmodel/private.h new file mode 100644 index 00000000000..df4b3721a13 --- /dev/null +++ b/dlls/windows.applicationmodel/private.h @@ -0,0 +1,40 @@ +/* WinRT Windows.ApplicationModel Implementation + * + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 + */ + +#ifndef __WINE_WINDOWS_APPLICATIONMODEL_PRIVATE_H +#define __WINE_WINDOWS_APPLICATIONMODEL_PRIVATE_H + +#include <stdarg.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winstring.h" + +#include "activation.h" + +#define WIDL_using_Windows_Foundation +#define WIDL_using_Windows_Foundation_Collections +#include "windows.foundation.h" +#define WIDL_using_Windows_ApplicationModel +#include "windows.applicationmodel.h" + +extern IActivationFactory *package_factory; + +#endif diff --git a/dlls/windows.applicationmodel/tests/Makefile.in b/dlls/windows.applicationmodel/tests/Makefile.in new file mode 100644 index 00000000000..265144f097c --- /dev/null +++ b/dlls/windows.applicationmodel/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = windows.applicationmodel.dll +IMPORTS = combase + +C_SRCS = \ + model.c diff --git a/dlls/windows.applicationmodel/tests/model.c b/dlls/windows.applicationmodel/tests/model.c new file mode 100644 index 00000000000..a86da0e84ad --- /dev/null +++ b/dlls/windows.applicationmodel/tests/model.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 "initguid.h" +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winstring.h" + +#include "roapi.h" + +#define WIDL_using_Windows_Foundation +#define WIDL_using_Windows_Foundation_Collections +#include "windows.foundation.h" +#define WIDL_using_Windows_ApplicationModel +#include "windows.applicationmodel.h" + +#include "wine/test.h" + +#define check_interface( obj, iid ) check_interface_( __LINE__, obj, iid ) +static void check_interface_( unsigned int line, void *obj, const IID *iid ) +{ + IUnknown *iface = obj; + IUnknown *unk; + HRESULT hr; + + hr = IUnknown_QueryInterface( iface, iid, (void **)&unk ); + ok_(__FILE__, line)( hr == S_OK || broken( hr == E_NOINTERFACE ) , "got hr %#lx.\n", hr ); + if (SUCCEEDED(hr)) + IUnknown_Release( unk ); +} + +static void test_PackageStatics(void) +{ + static const WCHAR *package_statics_name = L"Windows.ApplicationModel.Package"; + IActivationFactory *factory; + HSTRING str; + HRESULT hr; + LONG ref; + + hr = WindowsCreateString( package_statics_name, wcslen( package_statics_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); + WindowsDeleteString( str ); + ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + if (hr == REGDB_E_CLASSNOTREG) + { + win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( package_statics_name ) ); + return; + } + + check_interface( factory, &IID_IUnknown ); + check_interface( factory, &IID_IInspectable ); + check_interface( factory, &IID_IAgileObject ); + + ref = IActivationFactory_Release( factory ); + ok( ref == 1, "got ref %ld.\n", ref ); +} + +START_TEST(model) +{ + HRESULT hr; + + hr = RoInitialize( RO_INIT_MULTITHREADED ); + ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr ); + + test_PackageStatics(); + + RoUninitialize(); +} diff --git a/dlls/windows.applicationmodel/windows.applicationmodel.spec b/dlls/windows.applicationmodel/windows.applicationmodel.spec new file mode 100644 index 00000000000..20a8bfa98ea --- /dev/null +++ b/dlls/windows.applicationmodel/windows.applicationmodel.spec @@ -0,0 +1,3 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetActivationFactory(ptr ptr) +@ stdcall -private DllGetClassObject(ptr ptr ptr) diff --git a/include/windows.applicationmodel.core.idl b/include/windows.applicationmodel.core.idl index cc830609f7f..7a4ea85c642 100644 --- a/include/windows.applicationmodel.core.idl +++ b/include/windows.applicationmodel.core.idl @@ -20,10 +20,12 @@ #pragma winrt ns_prefix #endif
+#ifndef DO_NO_IMPORTS import "windows.foundation.idl"; import "windows.storage.idl"; import "windows.system.idl"; import "windows.applicationmodel.idl"; +#endif
namespace Windows.ApplicationModel { runtimeclass AppDisplayInfo; diff --git a/include/windows.applicationmodel.idl b/include/windows.applicationmodel.idl index 47557f7efb9..9c3718dc87d 100644 --- a/include/windows.applicationmodel.idl +++ b/include/windows.applicationmodel.idl @@ -20,10 +20,12 @@ #pragma winrt ns_prefix #endif
+#ifndef DO_NO_IMPORTS import "windows.foundation.idl"; import "windows.storage.idl"; import "windows.system.idl"; import "windows.applicationmodel.core.idl"; +#endif
namespace Windows.ApplicationModel.Core { runtimeclass AppListEntry;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/windows.applicationmodel/tests/model.c | 81 +++++++++++++++++++++ 1 file changed, 81 insertions(+)
diff --git a/dlls/windows.applicationmodel/tests/model.c b/dlls/windows.applicationmodel/tests/model.c index a86da0e84ad..50513e45fd4 100644 --- a/dlls/windows.applicationmodel/tests/model.c +++ b/dlls/windows.applicationmodel/tests/model.c @@ -30,7 +30,11 @@ #define WIDL_using_Windows_Foundation_Collections #include "windows.foundation.h" #define WIDL_using_Windows_ApplicationModel +#define WIDL_using_Windows_ApplicationModel_Core +#define WIDL_using_Windows_Management_Deployment +#define WIDL_using_Windows_Storage #include "windows.applicationmodel.h" +#include "windows.management.deployment.h"
#include "wine/test.h"
@@ -47,6 +51,82 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid ) IUnknown_Release( unk ); }
+static void test_PackageManager(void) +{ + static const WCHAR *statics_name = RuntimeClass_Windows_Management_Deployment_PackageManager; + IStorageFolder *storage_folder; + IStorageItem *storage_item; + IActivationFactory *factory; + IIterable_Package *packages; + IPackageManager *manager; + IIterator_Package *iter; + IPackage *package; + HSTRING str; + HRESULT hr; + LONG ref; + + hr = WindowsCreateString( statics_name, wcslen( statics_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); + WindowsDeleteString( str ); + todo_wine + ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + if (hr == REGDB_E_CLASSNOTREG) + { + todo_wine + win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( statics_name ) ); + return; + } + + check_interface( factory, &IID_IUnknown ); + check_interface( factory, &IID_IInspectable ); + check_interface( factory, &IID_IAgileObject ); + + hr = IActivationFactory_ActivateInstance( factory, (IInspectable **)&manager ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + check_interface( manager, &IID_IUnknown ); + check_interface( manager, &IID_IInspectable ); + check_interface( manager, &IID_IAgileObject ); + check_interface( manager, &IID_IPackageManager ); + + hr = IPackageManager_FindPackages( manager, &packages ); + ok( hr == S_OK || broken(hr == E_ACCESSDENIED) /* w8adm */, "got hr %#lx.\n", hr ); + if (broken(hr == E_ACCESSDENIED)) + { + win_skip("Unable to list packages, skipping package manager tests\n"); + goto skip_tests; + } + + hr = IIterable_Package_First( packages, &iter ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IIterator_Package_get_Current( iter, &package ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + 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 ); + hr = IStorageItem_get_Path( storage_item, &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + ref = IStorageItem_Release( storage_item ); + ok( ref == 1, "got ref %ld.\n", ref ); + ref = IStorageFolder_Release( storage_folder ); + ok( !ref, "got ref %ld.\n", ref ); + IPackage_Release( package ); + ref = IIterator_Package_Release( iter ); + ok( !ref, "got ref %ld.\n", ref ); + ref = IIterable_Package_Release( packages ); + ok( !ref, "got ref %ld.\n", ref ); + +skip_tests: + ref = IPackageManager_Release( manager ); + ok( !ref, "got ref %ld.\n", ref ); + ref = IActivationFactory_Release( factory ); + ok( ref == 1, "got ref %ld.\n", ref ); +} + static void test_PackageStatics(void) { static const WCHAR *package_statics_name = L"Windows.ApplicationModel.Package"; @@ -82,6 +162,7 @@ START_TEST(model) hr = RoInitialize( RO_INIT_MULTITHREADED ); ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr );
+ test_PackageManager(); test_PackageStatics();
RoUninitialize();
From: Rémi Bernon rbernon@codeweavers.com
--- tools/makedep.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/tools/makedep.c b/tools/makedep.c index d3064ea4ff1..97d0f3d587d 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -3066,13 +3066,14 @@ static void output_source_spec( struct makefile *make, struct incl_file *source, struct strarray dll_flags = empty_strarray; struct strarray default_imports = empty_strarray; struct strarray all_libs, dep_libs; - const char *dll_name, *obj_name, *res_name, *output_rsrc, *output_file, *debug_file; + const char *dll_name, *obj_name, *res_name, *output_rsrc, *output_file, *debug_file, *ext = ".dll"; unsigned int arch;
if (!imports.count) imports = make->imports; strarray_addall( &dll_flags, make->extradllflags ); strarray_addall( &dll_flags, get_expanded_file_local_var( make, obj, "EXTRADLLFLAGS" )); if (!strarray_exists( &dll_flags, "-nodefaultlibs" )) default_imports = get_default_imports( make, imports ); + if (strarray_exists( &dll_flags, "-Wl,--subsystem,console" )) ext = ".exe";
for (arch = 0; arch < archs.count; arch++) { @@ -3081,7 +3082,7 @@ static void output_source_spec( struct makefile *make, struct incl_file *source, strarray_addall( &all_libs, add_import_libs( make, &dep_libs, imports, IMPORT_TYPE_DIRECT, arch ) ); strarray_addall( &all_libs, add_import_libs( make, &dep_libs, default_imports, IMPORT_TYPE_DEFAULT, arch ) ); if (!arch) strarray_addall( &all_libs, libs ); - dll_name = arch_module_name( strmake( "%s.dll", obj ), arch ); + dll_name = arch_module_name( strmake( "%s%s", obj, ext ), arch ); obj_name = obj_dir_path( make, strmake( "%s%s.o", arch_dirs[arch], obj )); output_file = obj_dir_path( make, dll_name ); output_rsrc = strmake( "%s.res", dll_name ); @@ -3095,7 +3096,7 @@ static void output_source_spec( struct makefile *make, struct incl_file *source, output_filename( output_file ); output_filename( tools_path( make, "wrc" )); output( "\n" ); - output( "\t%secho "%s.dll TESTDLL \"%s\"" | %s -u -o $@\n", cmd_prefix( "WRC" ), obj, output_file, + output( "\t%secho "%s%s TESTDLL \"%s\"" | %s -u -o $@\n", cmd_prefix( "WRC" ), obj, ext, output_file, tools_path( make, "wrc" ));
output( "%s:", output_file ); @@ -3110,7 +3111,7 @@ static void output_source_spec( struct makefile *make, struct incl_file *source, output_filename( "-s" ); output_filenames( dll_flags ); if (arch) output_filenames( get_expanded_arch_var_array( make, "EXTRADLLFLAGS", arch )); - output_filename( "-shared" ); + if (!strcmp( ext, ".dll" )) output_filename( "-shared" ); output_filename( source->filename ); output_filename( obj_name ); if (res_name) output_filename( res_name );
From: Rémi Bernon rbernon@codeweavers.com
--- .../tests/Makefile.in | 11 +- .../tests/application.c | 29 ++ .../tests/application.spec | 1 + .../tests/appxmanifest.xml | 26 ++ dlls/windows.applicationmodel/tests/model.c | 326 ++++++++++++++++++ .../tests/resource.rc | 22 ++ 6 files changed, 412 insertions(+), 3 deletions(-) create mode 100644 dlls/windows.applicationmodel/tests/application.c create mode 100644 dlls/windows.applicationmodel/tests/application.spec 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..8d249a620df 100644 --- a/dlls/windows.applicationmodel/tests/Makefile.in +++ b/dlls/windows.applicationmodel/tests/Makefile.in @@ -1,5 +1,10 @@ TESTDLL = windows.applicationmodel.dll -IMPORTS = combase +IMPORTS = combase advapi32
-C_SRCS = \ - model.c +application_EXTRADLLFLAGS = -Wl,--subsystem,console + +SOURCES = \ + model.c \ + application.c \ + application.spec \ + resource.rc diff --git a/dlls/windows.applicationmodel/tests/application.c b/dlls/windows.applicationmodel/tests/application.c new file mode 100644 index 00000000000..d437e225587 --- /dev/null +++ b/dlls/windows.applicationmodel/tests/application.c @@ -0,0 +1,29 @@ +/* + * 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 + */ + +#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/application.spec b/dlls/windows.applicationmodel/tests/application.spec new file mode 100644 index 00000000000..76421d7e35b --- /dev/null +++ b/dlls/windows.applicationmodel/tests/application.spec @@ -0,0 +1 @@ +# nothing to export 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
From: Rémi Bernon rbernon@codeweavers.com
--- .../tests/application.c | 16 + dlls/windows.applicationmodel/tests/model.c | 52 ++ .../tests/winrt_test.h | 477 ++++++++++++++++++ 3 files changed, 545 insertions(+) create mode 100644 dlls/windows.applicationmodel/tests/winrt_test.h
diff --git a/dlls/windows.applicationmodel/tests/application.c b/dlls/windows.applicationmodel/tests/application.c index d437e225587..06530b32fec 100644 --- a/dlls/windows.applicationmodel/tests/application.c +++ b/dlls/windows.applicationmodel/tests/application.c @@ -22,8 +22,24 @@
#include "windef.h" #include "winbase.h" +#include "initguid.h" +#include "winstring.h" + +#include "roapi.h" + +#include "winrt_test.h"
int main( int argc, char const *argv[] ) { + HRESULT hr; + + if (!winrt_test_init()) return -1; + + hr = RoInitialize( RO_INIT_MULTITHREADED ); + ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr ); + + RoUninitialize(); + + winrt_test_exit(); return 0; } diff --git a/dlls/windows.applicationmodel/tests/model.c b/dlls/windows.applicationmodel/tests/model.c index b15024f6ca9..ca1da5daded 100644 --- a/dlls/windows.applicationmodel/tests/model.c +++ b/dlls/windows.applicationmodel/tests/model.c @@ -37,6 +37,7 @@ #include "windows.management.deployment.h"
#include "wine/test.h" +#include "winrt_test.h"
#define DEFINE_ASYNC_COMPLETED_HANDLER( name, iface_type, async_type ) \ struct name \ @@ -192,6 +193,9 @@ static void check_async_info_( int line, void *async, AsyncStatus expect_status,
DEFINE_ASYNC_COMPLETED_HANDLER( async_deployment_result_handler, IAsyncOperationWithProgressCompletedHandler_DeploymentResult_DeploymentProgress, IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress ) +DEFINE_ASYNC_COMPLETED_HANDLER( async_applist_handler, IAsyncOperationCompletedHandler_IVectorView_AppListEntry, + IAsyncOperation_IVectorView_AppListEntry ) +DEFINE_ASYNC_COMPLETED_HANDLER( async_boolean_handler, IAsyncOperationCompletedHandler_boolean, IAsyncOperation_boolean )
static HRESULT uri_create( const WCHAR *uri, IUriRuntimeClass **out ) { @@ -371,6 +375,53 @@ static void test_remove_package( IPackageManager *manager, IPackage *package ) IAsyncOperationWithProgress_DeploymentResult_DeploymentProgress_Release( operation ); }
+static void test_execute_package( IPackage *package ) +{ + IAsyncOperation_IVectorView_AppListEntry *async_list; + IAsyncOperation_boolean *async_launch; + IVectorView_AppListEntry *list; + IAppListEntry *app_entry; + IPackage3 *package3; + boolean launched; + HRESULT hr; + UINT res; + + if (!winrt_test_init()) return; + + hr = IPackage_QueryInterface( package, &IID_IPackage3, (void **)&package3 ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IPackage3_GetAppListEntriesAsync( package3, &async_list ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + res = await_IAsyncOperation_IVectorView_AppListEntry( async_list, 5000 ); + ok( res == 0, "await_IAsyncOperation_IVectorView_AppListEntry returned %#x.\n", res ); + check_async_info( async_list, Completed, S_OK ); + hr = IAsyncOperation_IVectorView_AppListEntry_GetResults( async_list, &list ); + ok( hr == S_OK, "GetResults returned %#lx\n", hr ); + IAsyncOperation_IVectorView_AppListEntry_Release( async_list ); + + hr = IVectorView_AppListEntry_GetAt( list, 0, &app_entry ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IVectorView_AppListEntry_Release( list ); + + IPackage3_Release( package3 ); + + + hr = IAppListEntry_LaunchAsync( app_entry, &async_launch ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + res = await_IAsyncOperation_boolean( async_launch, 5000 ); + ok( res == 0, "await_IAsyncOperation_boolean returned %#x.\n", res ); + check_async_info( async_launch, Completed, S_OK ); + + hr = IAsyncOperation_boolean_GetResults( async_launch, &launched ); + ok( hr == S_OK, "GetResults returned %#lx\n", hr ); + ok( launched == TRUE, "got launched %u.\n", launched ); + + IAppListEntry_Release( app_entry ); + + winrt_test_exit(); +} + static void test_PackageManager(void) { static const WCHAR *statics_name = RuntimeClass_Windows_Management_Deployment_PackageManager; @@ -442,6 +493,7 @@ static void test_PackageManager(void)
if (SUCCEEDED(test_register_package( manager, &package ))) { + test_execute_package( package ); test_remove_package( manager, package ); IPackage_Release( package ); } diff --git a/dlls/windows.applicationmodel/tests/winrt_test.h b/dlls/windows.applicationmodel/tests/winrt_test.h new file mode 100644 index 00000000000..fb1bb02d2e7 --- /dev/null +++ b/dlls/windows.applicationmodel/tests/winrt_test.h @@ -0,0 +1,477 @@ +/* + * Copyright 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 + */ + +#ifndef __WINE_WINRT_TEST_H +#define __WINE_WINRT_TEST_H + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" + +/* desktop/winrt shared data */ +struct winetest_shared_data +{ + BOOL running_under_wine; + BOOL winetest_report_success; + BOOL winetest_debug; + LONG failures; + LONG todo_failures; +}; + +static HANDLE winrt_section; +static HANDLE winrt_okfile; +static HANDLE winrt_event; + +#define winrt_test_init() winrt_test_init_( __FILE__, __LINE__ ) +#define winrt_test_exit() winrt_test_exit_( __FILE__, __LINE__ ) + +#ifdef __WINE_WINE_TEST_H + +static BOOL winrt_test_init_( const char *file, int line ) +{ + struct winetest_shared_data *data; + + winrt_section = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(*data), + L"winetest_winrt_section" ); + if (!winrt_section && GetLastError() == ERROR_ACCESS_DENIED) + { + win_skip_(file, line)( "Skipping WinRT tests: failed to create mapping.\n" ); + return FALSE; + } + ok_(file, line)( !!winrt_section, "got error %lu\n", GetLastError() ); + + data = MapViewOfFile( winrt_section, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024 ); + ok_(file, line)( !!data, "MapViewOfFile failed, error %lu\n", GetLastError() ); + data->running_under_wine = !strcmp( winetest_platform, "wine" ); + data->winetest_report_success = winetest_report_success; + data->winetest_debug = winetest_debug; + UnmapViewOfFile( data ); + + winrt_okfile = CreateFileW( L"C:\Users\Public\Documents\winetest_winrt_okfile", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL ); + ok_(file, line)( winrt_okfile != INVALID_HANDLE_VALUE, "failed to create file, error %lu\n", GetLastError() ); + + winrt_event = CreateEventW( 0, FALSE, FALSE, L"winetest_winrt_event" ); + ok_(file, line)( !!winrt_event, "got error %lu\n", GetLastError() ); + + subtest_(file, line)( "application" ); + return TRUE; +} + +static void winrt_test_exit_( const char *file, int line ) +{ + struct winetest_shared_data *data; + char buffer[512]; + DWORD size, res; + + res = WaitForSingleObject( winrt_event, 5000 ); + ok_(file, line)( !res, "WaitForSingleObject returned %#lx\n", res ); + CloseHandle( winrt_event ); + + SetFilePointer( winrt_okfile, 0, NULL, FILE_BEGIN ); + do + { + ReadFile( winrt_okfile, buffer, sizeof(buffer), &size, NULL ); + printf( "%.*s", (int)size, buffer ); + } while (size == sizeof(buffer)); + SetFilePointer( winrt_okfile, 0, NULL, FILE_BEGIN ); + SetEndOfFile( winrt_okfile ); + CloseHandle( winrt_okfile ); + DeleteFileW( L"C:\Users\Public\Documents\winetest_winrt_okfile" ); + + data = MapViewOfFile( winrt_section, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024 ); + ok_(file, line)( !!data, "MapViewOfFile failed, error %lu\n", GetLastError() ); + winetest_add_failures( InterlockedExchange( &data->failures, 0 ) ); + winetest_add_failures( InterlockedExchange( &data->todo_failures, 0 ) ); + UnmapViewOfFile( data ); + CloseHandle( winrt_section ); +} + +#else /* __WINE_WINE_TEST_H */ + +#ifdef __MINGW32__ +#define __WINE_PRINTF_ATTR( fmt, args ) __attribute__((format( printf, fmt, args ))) +#else +#define __WINE_PRINTF_ATTR( fmt, args ) +#endif + +static LONG successes; +static LONG failures; +static LONG skipped; +static LONG todo_successes; +static LONG todo_failures; +static LONG muted_traces; +static LONG muted_skipped; +static LONG muted_todo_successes; + +static int running_under_wine; +static int winetest_debug; +static int winetest_report_success; + +/* silence todos and skips above this threshold */ +static int winetest_mute_threshold = 42; + +/* counts how many times a given line printed a message */ +static LONG line_counters[16384]; + +/* The following data must be kept track of on a per-thread basis */ +struct tls_data +{ + const char *current_file; /* file of current check */ + int current_line; /* line of current check */ + unsigned int todo_level; /* current todo nesting level */ + int todo_do_loop; + char *str_pos; /* position in debug buffer */ + char strings[2000]; /* buffer for debug strings */ + char context[8][128]; /* data to print before messages */ + unsigned int context_count; /* number of context prefixes */ +}; + +static DWORD tls_index; + +static inline struct tls_data *get_tls_data(void) +{ + struct tls_data *data; + + if (!(data = TlsGetValue( tls_index ))) + { + data = calloc( 1, sizeof(*data) ); + data->str_pos = data->strings; + TlsSetValue( tls_index, data ); + } + + return data; +} + +static inline void winetest_set_location( const char *file, int line ) +{ + struct tls_data *data = get_tls_data(); + data->current_file = strrchr( file, '/' ); + if (data->current_file == NULL) data->current_file = strrchr( file, '\' ); + if (data->current_file == NULL) data->current_file = file; + else data->current_file++; + data->current_line = line; +} + +static inline void kvprintf( const char *format, va_list ap ) +{ + struct tls_data *data = get_tls_data(); + DWORD written; + int len = vsnprintf( data->str_pos, sizeof(data->strings) - (data->str_pos - data->strings), format, ap ); + data->str_pos += len; + + if (len && data->str_pos[-1] == '\n') + { + WriteFile( winrt_okfile, data->strings, strlen( data->strings ), &written, NULL ); + data->str_pos = data->strings; + } +} + +static inline void WINAPIV kprintf( const char *format, ... ) __WINE_PRINTF_ATTR( 1, 2 ); +static inline void WINAPIV kprintf( const char *format, ... ) +{ + va_list valist; + + va_start( valist, format ); + kvprintf( format, valist ); + va_end( valist ); +} + +static inline void WINAPIV winetest_printf( const char *msg, ... ) __WINE_PRINTF_ATTR( 1, 2 ); +static inline void WINAPIV winetest_printf( const char *msg, ... ) +{ + struct tls_data *data = get_tls_data(); + va_list valist; + + kprintf( "%s:%d: ", data->current_file, data->current_line ); + va_start( valist, msg ); + kvprintf( msg, valist ); + va_end( valist ); +} + +static inline void winetest_print_context( const char *msgtype ) +{ + struct tls_data *data = get_tls_data(); + unsigned int i; + + winetest_printf( "%s", msgtype ); + for (i = 0; i < data->context_count; ++i) kprintf( "%s: ", data->context[i] ); +} + +static inline LONG winetest_add_line(void) +{ + struct tls_data *data; + int index, count; + + if (winetest_debug > 1) return 0; + + data = get_tls_data(); + index = data->current_line % ARRAY_SIZE(line_counters); + count = InterlockedIncrement( line_counters + index ) - 1; + if (count == winetest_mute_threshold) + winetest_printf( "Line has been silenced after %d occurrences\n", winetest_mute_threshold ); + + return count; +} + +static inline int winetest_vok( int condition, const char *msg, va_list args ) +{ + struct tls_data *data = get_tls_data(); + + if (data->todo_level) + { + if (condition) + { + winetest_print_context( "Test succeeded inside todo block: " ); + kvprintf( msg, args ); + InterlockedIncrement( &todo_failures ); + return 0; + } + else + { + if (!winetest_debug || winetest_add_line() < winetest_mute_threshold) + { + if (winetest_debug > 0) + { + winetest_print_context( "Test marked todo: " ); + kvprintf( msg, args ); + } + InterlockedIncrement( &todo_successes ); + } + else InterlockedIncrement( &muted_todo_successes ); + return 1; + } + } + else + { + if (!condition) + { + winetest_print_context( "Test failed: " ); + kvprintf( msg, args ); + InterlockedIncrement( &failures ); + return 0; + } + else + { + if (winetest_report_success) winetest_printf( "Test succeeded\n" ); + InterlockedIncrement( &successes ); + return 1; + } + } +} + +static inline void WINAPIV winetest_ok( int condition, const char *msg, ... ) __WINE_PRINTF_ATTR( 2, 3 ); +static inline void WINAPIV winetest_ok( int condition, const char *msg, ... ) +{ + va_list args; + va_start( args, msg ); + winetest_vok( condition, msg, args ); + va_end( args ); +} + +static inline void winetest_vskip( const char *msg, va_list args ) +{ + if (winetest_add_line() < winetest_mute_threshold) + { + winetest_print_context( "Driver tests skipped: " ); + kvprintf( msg, args ); + InterlockedIncrement( &skipped ); + } + else InterlockedIncrement( &muted_skipped ); +} + +static inline void WINAPIV winetest_skip( const char *msg, ... ) __WINE_PRINTF_ATTR( 1, 2 ); +static inline void WINAPIV winetest_skip( const char *msg, ... ) +{ + va_list args; + va_start( args, msg ); + winetest_vskip( msg, args ); + va_end( args ); +} + +static inline void WINAPIV winetest_win_skip( const char *msg, ... ) __WINE_PRINTF_ATTR( 1, 2 ); +static inline void WINAPIV winetest_win_skip( const char *msg, ... ) +{ + va_list args; + va_start( args, msg ); + if (!running_under_wine) winetest_vskip( msg, args ); + else winetest_vok( 0, msg, args ); + va_end( args ); +} + +static inline void WINAPIV winetest_trace( const char *msg, ... ) __WINE_PRINTF_ATTR( 1, 2 ); +static inline void WINAPIV winetest_trace( const char *msg, ... ) +{ + va_list args; + + if (!winetest_debug) return; + if (winetest_add_line() < winetest_mute_threshold) + { + winetest_print_context( "" ); + va_start( args, msg ); + kvprintf( msg, args ); + va_end( args ); + } + else InterlockedIncrement( &muted_traces ); +} + +static inline void winetest_start_todo( int is_todo ) +{ + struct tls_data *data = get_tls_data(); + data->todo_level = (data->todo_level << 1) | (is_todo != 0); + data->todo_do_loop = 1; +} + +static inline int winetest_loop_todo(void) +{ + struct tls_data *data = get_tls_data(); + int do_loop = data->todo_do_loop; + data->todo_do_loop = 0; + return do_loop; +} + +static inline void winetest_end_todo(void) +{ + struct tls_data *data = get_tls_data(); + data->todo_level >>= 1; +} + +static inline void WINAPIV winetest_push_context( const char *fmt, ... ) __WINE_PRINTF_ATTR( 1, 2 ); +static inline void WINAPIV winetest_push_context( const char *fmt, ... ) +{ + struct tls_data *data = get_tls_data(); + va_list valist; + + if (data->context_count < ARRAY_SIZE(data->context)) + { + va_start( valist, fmt ); + vsnprintf( data->context[data->context_count], sizeof(data->context[data->context_count]), fmt, valist ); + va_end( valist ); + data->context[data->context_count][sizeof(data->context[data->context_count]) - 1] = 0; + } + ++data->context_count; +} + +static inline void winetest_pop_context(void) +{ + struct tls_data *data = get_tls_data(); + + if (data->context_count) --data->context_count; +} + +static inline int broken( int condition ) +{ + return !running_under_wine && condition; +} + +#ifdef WINETEST_NO_LINE_NUMBERS +# define subtest_(file, line) (winetest_set_location(file, 0), 0) ? (void)0 : winetest_subtest +# define ignore_exceptions_(file, line) (winetest_set_location(file, 0), 0) ? (void)0 : winetest_ignore_exceptions +# define ok_(file, line) (winetest_set_location(file, 0), 0) ? (void)0 : winetest_ok +# define skip_(file, line) (winetest_set_location(file, 0), 0) ? (void)0 : winetest_skip +# define win_skip_(file, line) (winetest_set_location(file, 0), 0) ? (void)0 : winetest_win_skip +# define trace_(file, line) (winetest_set_location(file, 0), 0) ? (void)0 : winetest_trace +# define wait_child_process_(file, line) (winetest_set_location(file, 0), 0) ? (void)0 : winetest_wait_child_process +#else +# define subtest_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_subtest +# define ignore_exceptions_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_ignore_exceptions +# define ok_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_ok +# define skip_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_skip +# define win_skip_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_win_skip +# define trace_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_trace +# define wait_child_process_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_wait_child_process +#endif + +#define ok ok_(__FILE__, __LINE__) +#define skip skip_(__FILE__, __LINE__) +#define trace trace_(__FILE__, __LINE__) +#define win_skip win_skip_(__FILE__, __LINE__) + +#define todo_if(is_todo) for (winetest_start_todo(is_todo); \ + winetest_loop_todo(); \ + winetest_end_todo()) +#define todo_wine todo_if(running_under_wine) +#define todo_wine_if(is_todo) todo_if((is_todo) && running_under_wine) + +static inline BOOL winrt_test_init_( const char *file, int line ) +{ + const struct winetest_shared_data *data; + + tls_index = TlsAlloc(); + + winrt_okfile = CreateFileW( L"C:\Users\Public\Documents\winetest_winrt_okfile", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + if (!winrt_okfile) return FALSE; + + winrt_event = OpenEventW( EVENT_ALL_ACCESS, FALSE, L"winetest_winrt_event" ); + ok_(file, line)( !!winrt_event, "OpenEventW failed, error %lu\n", GetLastError() ); + + winrt_section = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, L"winetest_winrt_section" ); + ok_(file, line)( !!winrt_section, "OpenFileMappingW failed, error %lu\n", GetLastError() ); + + data = MapViewOfFile( winrt_section, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024 ); + ok_(file, line)( !!data, "MapViewOfFile failed, error %lu\n", GetLastError() ); + running_under_wine = data->running_under_wine; + winetest_debug = data->winetest_debug; + winetest_report_success = data->winetest_report_success; + UnmapViewOfFile( data ); + + return TRUE; +} + +static inline void winrt_test_exit_( const char *file, int line ) +{ + char test_name[MAX_PATH], *tmp; + struct winetest_shared_data *data; + const char *source_file; + + source_file = strrchr( file, '/' ); + if (!source_file) source_file = strrchr( file, '\' ); + if (!source_file) source_file = file; + else source_file++; + + strcpy( test_name, source_file ); + if ((tmp = strrchr( test_name, '.' ))) *tmp = 0; + + data = MapViewOfFile( winrt_section, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 1024 ); + ok_(file, line)( !!data, "MapViewOfFile failed, error %lu\n", GetLastError() ); + InterlockedExchangeAdd( &data->failures, failures ); + InterlockedExchangeAdd( &data->todo_failures, todo_failures ); + UnmapViewOfFile( data ); + CloseHandle( winrt_section ); + + if (winetest_debug) + { + kprintf( "%04lx:%s: %ld tests executed (%ld marked as todo, 0 as flaky, %ld %s), %ld skipped.\n", + (DWORD)(DWORD_PTR)GetCurrentProcessId(), test_name, + successes + failures + todo_successes + todo_failures, todo_successes, failures + todo_failures, + (failures + todo_failures != 1) ? "failures" : "failure", skipped ); + } + CloseHandle( winrt_okfile ); + + SetEvent( winrt_event ); + CloseHandle( winrt_event ); +} + +#endif /* __WINE_WINE_TEST_H */ + +#endif /* __WINE_WINRT_TEST_H */
This merge request was approved by Rémi Bernon.
Anything wrong about this?
Requiring a spec file for an exe is not nice. Also I don't think we want yet another copy of the wine/test.h file.
Any suggestion how makedep could decide that the source should be compiled to a resource? Would a pragma be better?
Yes this could be a pragma.
Just to throw it out there, one idea I had while trying to prototype win16 tests was to create whole module directories that could be embedded as test resources, giving as much freedom as exists for normal DLLs or executables. I don't have the patch handy, though.