From 77bc9369e0a129bcd5345af331c68b2913dccc7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Bernon?= <rbernon@codeweavers.com>
Date: Sat, 2 Jul 2022 18:18:39 +0200
Subject: [PATCH 2/4] midl/tests: Add tests for midl.exe.

---
 configure.ac                    |   1 +
 programs/midl/tests/Makefile.in |   5 +
 programs/midl/tests/midl.c      | 278 ++++++++++++++++++++++++++++++++
 3 files changed, 284 insertions(+)
 create mode 100644 programs/midl/tests/Makefile.in
 create mode 100644 programs/midl/tests/midl.c

diff --git a/configure.ac b/configure.ac
index 9af2cc1878a..4317595fc0f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3343,6 +3343,7 @@ WINE_CONFIG_MAKEFILE(programs/iexplore)
 WINE_CONFIG_MAKEFILE(programs/ipconfig)
 WINE_CONFIG_MAKEFILE(programs/lodctr)
 WINE_CONFIG_MAKEFILE(programs/midl)
+WINE_CONFIG_MAKEFILE(programs/midl/tests)
 WINE_CONFIG_MAKEFILE(programs/mofcomp)
 WINE_CONFIG_MAKEFILE(programs/mshta)
 WINE_CONFIG_MAKEFILE(programs/msidb)
diff --git a/programs/midl/tests/Makefile.in b/programs/midl/tests/Makefile.in
new file mode 100644
index 00000000000..f2532881c99
--- /dev/null
+++ b/programs/midl/tests/Makefile.in
@@ -0,0 +1,5 @@
+TESTDLL = midl.exe
+IMPORTS = advapi32
+
+C_SRCS = \
+	midl.c
diff --git a/programs/midl/tests/midl.c b/programs/midl/tests/midl.c
new file mode 100644
index 00000000000..2603e63bc4a
--- /dev/null
+++ b/programs/midl/tests/midl.c
@@ -0,0 +1,278 @@
+/*
+ * 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 <stdarg.h>
+#include <stddef.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+
+#include "wine/test.h"
+#include "wine/list.h"
+
+static WCHAR midl[MAX_PATH];
+
+static BOOL midl_test_init(void)
+{
+    WCHAR path[MAX_PATH], version[MAX_PATH], *tmp;
+    DWORD i, res, type, size;
+    HKEY key;
+
+    res = RegOpenKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows Kits\\Installed Roots", &key );
+    if (!res) for (i = 0; !RegEnumKeyW( key, i, version, sizeof(version) ); i++)
+    {
+        size = sizeof(path);
+        res = RegQueryValueExW( key, L"KitsRoot10", NULL, &type, (BYTE *)path, &size );
+        ok( !res, "got res %#lx\n", res );
+        swprintf( midl, MAX_PATH, L"%sbin\\%s\\x86\\midl.exe", path, version );
+        if (GetFileAttributesW( midl ) != INVALID_FILE_ATTRIBUTES) goto found;
+    }
+
+    wcscpy( midl, L"c:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x86\\midl.exe" );
+    if (GetFileAttributesW( midl ) != INVALID_FILE_ATTRIBUTES) goto found;
+    wcscpy( midl, L"c:\\Program Files (x86)\\Microsoft SDKs\\Windows\v7.1a\\bin\\midl.exe" );
+    if (GetFileAttributesW( midl ) != INVALID_FILE_ATTRIBUTES) goto found;
+    wcscpy( midl, L"c:\\Windows\\system32\\midl.exe" );
+    if (GetFileAttributesW( midl ) != INVALID_FILE_ATTRIBUTES) goto found;
+
+    RegCloseKey( key );
+    return FALSE;
+
+found:
+    RegCloseKey( key );
+    if ((tmp = wcsrchr( midl, '\\' ))) *tmp = 0;
+    return TRUE;
+}
+
+static void write_file( const WCHAR *filename, const char *source )
+{
+    HANDLE file;
+    DWORD size;
+    BOOL ret;
+
+    file = CreateFileW( filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+    ok( file != INVALID_HANDLE_VALUE, "CreateFileW failed, error %lu\n", GetLastError() );
+
+    ret = WriteFile( file, source, strlen( source ), &size, NULL );
+    ok( ret, "WriteFile failed, error %lu\n", GetLastError() );
+    ok( size == strlen( source ), "got size %lu\n", size );
+
+    CloseHandle( file );
+}
+
+enum midl_flags
+{
+    MIDL_VERBOSE = 1,
+    MIDL_VERSION = 2,
+    MIDL_WINRT = 4,
+    MIDL_WERROR = 8,
+};
+
+static DWORD check_idl( const char *source, enum midl_flags flags )
+{
+    WCHAR temp[MAX_PATH], app[MAX_PATH], cmd[5 * MAX_PATH], path[MAX_PATH], dir[MAX_PATH];
+    STARTUPINFOW si = {.cb = sizeof(STARTUPINFOW)};
+    PROCESS_INFORMATION pi = {0};
+    DWORD res, exit_code;
+    BOOL ret;
+
+    if (flags & MIDL_VERSION) trace( "Using MIDL from %s\n", debugstr_w(midl) );
+
+    GetTempPathW( ARRAY_SIZE(temp), temp );
+    GetTempFileNameW( temp, L"midl", 0, temp );
+
+    swprintf( dir, ARRAY_SIZE(dir), L"%s.dir", temp );
+    CreateDirectoryW( dir, NULL );
+
+    swprintf( path, ARRAY_SIZE(path), L"%s\\main.idl", dir );
+    write_file( path, source );
+
+    if (!strcmp( winetest_platform, "wine" ))
+    {
+        swprintf( app, ARRAY_SIZE(app), L"%s\\midl.exe", midl );
+        swprintf( cmd, ARRAY_SIZE(cmd), L"\"%s\" main.idl -I. -N -W", app );
+        if (flags & MIDL_WINRT) wcscat( cmd, L" --winrt" );
+    }
+    else
+    {
+        swprintf( app, ARRAY_SIZE(app), L"%s\\midl%s.exe", midl, (flags & MIDL_WINRT ? L"rt" : L"") );
+        swprintf( cmd, ARRAY_SIZE(cmd), L"\"%s\" main.idl -I. -nocpp -W4", app );
+        wcscat( cmd, L" -syntax_check" );
+
+        if (!(flags & MIDL_VERSION)) wcscat( cmd, L" -nologo" );
+        if (flags & MIDL_WERROR) wcscat( cmd, L" -WX" );
+        if (flags & MIDL_WINRT) wcscat( cmd, L" -winrt -nomd -nomidl" );
+    }
+
+    if (flags & MIDL_VERBOSE) trace( "Running %s\n", debugstr_w(cmd) );
+
+    ret = CreateProcessW( app, cmd, NULL, NULL, FALSE, 0, NULL, dir, &si, &pi );
+    ok( ret, "CreateProcessW failed, error %lu\n", GetLastError() );
+
+    res = WaitForSingleObject( pi.hProcess, 1000 );
+    ok( !res, "WaitForSingleObject returned %#lx\n", res );
+    ret = GetExitCodeProcess( pi.hProcess, &exit_code );
+    ok( ret, "GetExitCodeProcess failed, error %lu\n", GetLastError() );
+
+    CloseHandle( pi.hThread );
+    CloseHandle( pi.hProcess );
+
+    swprintf( path, ARRAY_SIZE(path), L"%s\\main.idl", dir );
+    DeleteFileW( path );
+
+    RemoveDirectoryW( dir );
+    DeleteFileW( temp );
+
+    return exit_code;
+}
+
+static void strip_comments( char *text )
+{
+    char *pos, *end, *next = strstr( text, "/*" );
+    int len = next - text;
+
+    while ((pos = next) && (end = strstr( pos, "*/" )))
+    {
+        if (!(next = strstr( end, "/*" ))) next = end + strlen( end );
+        memmove( text + len, end + 2, next - end - 2 );
+        len += next - end - 2;
+    }
+
+    text[len] = 0;
+}
+
+static int isident( char c )
+{
+    return isalnum(c) || c == '_';
+}
+
+static void squash_spaces( char *text )
+{
+    static const char *lookup = " \t\r\n";
+    char prev = 0, *pos, *end, *eod = NULL, *next = strpbrk( text, lookup );
+    int len = next - text;
+
+    while ((pos = next) && (end = pos + strspn( pos, lookup )) > pos)
+    {
+        if (!(next = strpbrk( end, lookup ))) next = end + strlen( end );
+        if (*end == '#' && len)
+        {
+            /* fold escaped newlines */
+            while ((eod = strchr( end, '\n' )) && (eod[-1] == '\\' || (eod[-1] == '\r' && eod[-2] == '\\')))
+            {
+                if (eod[-1] == '\r') eod[-2] = ' ';
+                eod[-1] = eod[0] = ' ';
+            }
+            *--end = '\n';
+        }
+        else if (pos <= eod && next >= eod) *--end = '\n';
+        else if (isident(prev) && isident(*end)) end--;
+        memmove( text + len, end, next - end );
+        len += next - end;
+        prev = next[-1];
+    }
+
+    text[len] = 0;
+}
+
+START_TEST( midl )
+{
+    const char *src;
+    DWORD res;
+
+    if (!midl_test_init())
+    {
+        skip( "midl.exe not found, skipping tests\n" );
+        return;
+    }
+
+    /* check basic success */
+    src = "interface I;";
+    res = check_idl( src, MIDL_WERROR | MIDL_VERBOSE | MIDL_VERSION );
+    ok( !res, "MIDL failed, error %#lx\n", res );
+
+    /* check basic failure */
+    src = "interface {}";
+    res = check_idl( src, 0 );
+    ok( res, "MIDL succeeded\n" );
+
+    /* winrt mode degrades automatically */
+    src = "interface I {}; coclass C { [default] interface I; }";
+    res = check_idl( src, MIDL_WERROR | MIDL_WINRT );
+    todo_wine
+    ok( !res, "MIDL failed, error %#lx\n", res );
+
+    /* legacy mode doesn't upgrade */
+    src = "namespace N {}";
+    res = check_idl( src, 0 );
+    ok( res, "MIDL succeeded\n" );
+    res = check_idl( src, MIDL_WERROR | MIDL_WINRT );
+    ok( !res, "MIDL failed, error %#lx\n", res );
+
+    /* colon is sometimes optional */
+    src = "[local]interface I1 {}\n"
+          "[local]interface I2 {};\n"
+          "[uuid(00000000-0000-0000-0000-000000000000)]coclass C1{interface I1;interface I2;};\n"
+          "[uuid(00000000-0000-0000-0000-000000000001)]coclass C2{interface I1;interface I2;}\n";
+    res = check_idl( src, MIDL_WERROR );
+    ok( !res, "MIDL failed, error %#lx\n", res );
+
+    /* attribute syntax is flexible */
+    src = "[hidden,][,][,,local,][,,][,helpstring(\"\")]"
+          "interface I{}";
+    res = check_idl( src, MIDL_WERROR );
+    ok( !res, "MIDL failed, error %#lx\n", res );
+
+    /* allowed but ignored on forward definitions */
+    src = "[local][local]interface I{}";
+    res = check_idl( src, MIDL_WERROR );
+    todo_wine
+    ok( res, "MIDL succeeded\n" );
+    src = "[local][local]interface I;";
+    res = check_idl( src, MIDL_WERROR );
+    todo_wine
+    ok( !res, "MIDL failed, error %#lx\n", res );
+
+    /* warning: object needs IUnknown */
+    src = "[uuid(00000000-0000-0000-0000-000000000000),object]\n"
+          "interface I{}\n";
+    res = check_idl( src, 0 );
+    ok( !res, "MIDL failed, error %#lx\n", res );
+    res = check_idl( src, MIDL_WERROR );
+    todo_wine
+    ok( res, "MIDL succeeded\n" );
+
+    /* needs a unique uuid */
+    src = "[uuid(00000000-0000-0000-0000-000000000000),object]\n"
+          "interface IUnknown{}\n"
+          "[uuid(00000000-0000-0000-0000-000000000000),object]\n"
+          "interface I:IUnknown{}\n";
+    res = check_idl( src, 0 );
+    todo_wine
+    ok( res, "MIDL succeeded\n" );
+
+    src = "[uuid(00000000-0000-0000-0000-000000000000),object]\n"
+          "interface IUnknown{}\n"
+          "[uuid(00000000-0000-0000-0000-000000000001),object]\n"
+          "interface I:IUnknown{}\n"
+          "[uuid(00000000-0000-0000-0000-000000000002),object]\n"
+          "interface J:I{}\n";
+    res = check_idl( src, 0 );
+    ok( !res, "MIDL failed, error %#lx\n", res );
+}
-- 
2.36.1

