Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
June 2018
- 68 participants
- 1149 messages
[PATCH] wininet/tests: Use the available ARRAY_SIZE() macro
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/wininet/tests/ftp.c | 2 +-
dlls/wininet/tests/http.c | 10 ++++-----
dlls/wininet/tests/internet.c | 16 ++++++-------
dlls/wininet/tests/url.c | 52 +++++++++++++++++++++----------------------
dlls/wininet/tests/urlcache.c | 4 ++--
5 files changed, 42 insertions(+), 42 deletions(-)
diff --git a/dlls/wininet/tests/ftp.c b/dlls/wininet/tests/ftp.c
index d2118faded..6f50104f3b 100644
--- a/dlls/wininet/tests/ftp.c
+++ b/dlls/wininet/tests/ftp.c
@@ -735,7 +735,7 @@ static void test_command(HINTERNET hFtp, HINTERNET hConnect)
return;
}
- for (i = 0; i < sizeof(command_test) / sizeof(command_test[0]); i++)
+ for (i = 0; i < ARRAY_SIZE(command_test); i++)
{
SetLastError(0xdeadbeef);
ret = pFtpCommandA(hFtp, FALSE, FTP_TRANSFER_TYPE_ASCII, command_test[i].cmd, 0, NULL);
diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c
index 0524ddf3fd..ace8512399 100644
--- a/dlls/wininet/tests/http.c
+++ b/dlls/wininet/tests/http.c
@@ -172,7 +172,7 @@ static BOOL (WINAPI *pInternetGetSecurityInfoByURLA)(LPSTR,PCCERT_CHAIN_CONTEXT*
static int strcmp_wa(LPCWSTR strw, const char *stra)
{
WCHAR buf[512];
- MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, ARRAY_SIZE(buf));
return lstrcmpW(strw, buf);
}
@@ -4204,7 +4204,7 @@ static void test_http_status(int port)
DWORD i, size;
BOOL res;
- for(i=0; i < sizeof(http_status_tests)/sizeof(*http_status_tests); i++) {
+ for(i = 0; i < ARRAY_SIZE(http_status_tests); i++) {
send_buffer = http_status_tests[i].response_text;
open_simple_request(&req, "localhost", port, NULL, "/send_from_buffer");
@@ -6543,14 +6543,14 @@ static const struct notification async_send_request_ex_chunked_test[] =
static const struct notification_data notification_data[] = {
{
async_send_request_ex_chunked_test,
- sizeof(async_send_request_ex_chunked_test)/sizeof(async_send_request_ex_chunked_test[0]),
+ ARRAY_SIZE(async_send_request_ex_chunked_test),
"GET",
"test.winehq.org",
"tests/data.php"
},
{
async_send_request_ex_test,
- sizeof(async_send_request_ex_test)/sizeof(async_send_request_ex_test[0]),
+ ARRAY_SIZE(async_send_request_ex_test),
"POST",
"test.winehq.org",
"tests/post.php",
@@ -6565,7 +6565,7 @@ static const struct notification_data notification_data[] = {
},
{
async_send_request_ex_resolve_failure_test,
- sizeof(async_send_request_ex_resolve_failure_test)/sizeof(async_send_request_ex_resolve_failure_test[0]),
+ ARRAY_SIZE(async_send_request_ex_resolve_failure_test),
"GET",
"brokenhost",
"index.html",
diff --git a/dlls/wininet/tests/internet.c b/dlls/wininet/tests/internet.c
index 39c698f5db..7889e40ea8 100644
--- a/dlls/wininet/tests/internet.c
+++ b/dlls/wininet/tests/internet.c
@@ -629,7 +629,7 @@ static void test_cookie_url(void)
ok(!res && GetLastError() == ERROR_INVALID_PARAMETER,
"InternetGetCookeA failed: %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
- len = sizeof(bufw)/sizeof(*bufw);
+ len = ARRAY_SIZE(bufw);
res = InternetGetCookieW(about_blankW, NULL, bufw, &len);
ok(!res && GetLastError() == ERROR_INVALID_PARAMETER,
"InternetGetCookeW failed: %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
@@ -639,7 +639,7 @@ static void test_cookie_url(void)
ok(!res && GetLastError() == ERROR_INVALID_PARAMETER,
"InternetGetCookeExA failed: %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
- len = sizeof(bufw)/sizeof(*bufw);
+ len = ARRAY_SIZE(bufw);
res = pInternetGetCookieExW(about_blankW, NULL, bufw, &len, 0, NULL);
ok(!res && GetLastError() == ERROR_INVALID_PARAMETER,
"InternetGetCookeExW failed: %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
@@ -887,7 +887,7 @@ static void InternetTimeFromSystemTimeW_test(void)
/* test too small buffer size */
SetLastError(0xdeadbeef);
- ret = pInternetTimeFromSystemTimeW( &time, INTERNET_RFC1123_FORMAT, string, sizeof(string)/sizeof(string[0]) );
+ ret = pInternetTimeFromSystemTimeW( &time, INTERNET_RFC1123_FORMAT, string, ARRAY_SIZE(string));
error = GetLastError();
ok( !ret, "InternetTimeFromSystemTimeW should have returned FALSE\n" );
ok( error == ERROR_INSUFFICIENT_BUFFER,
@@ -1084,7 +1084,7 @@ static void test_PrivacyGetSetZonePreferenceW(void)
trace("template %u\n", old_template);
if(old_template == PRIVACY_TEMPLATE_ADVANCED) {
- pref_size = sizeof(pref)/sizeof(WCHAR);
+ pref_size = ARRAY_SIZE(pref);
ret = pPrivacyGetZonePreferenceW(zone, type, &old_template, pref, &pref_size);
ok(ret == 0, "expected ret == 0, got %u\n", ret);
}
@@ -1721,7 +1721,7 @@ static void test_InternetGetConnectedStateExW(void)
flags = 0;
buffer[0] = 0;
- res = pInternetGetConnectedStateExW(&flags, buffer, sizeof(buffer) / sizeof(buffer[0]), 0);
+ res = pInternetGetConnectedStateExW(&flags, buffer, ARRAY_SIZE(buffer), 0);
trace("Internet Connection: Flags 0x%02x - Name '%s'\n", flags, wine_dbgstr_w(buffer));
todo_wine
ok (flags & INTERNET_RAS_INSTALLED, "Missing RAS flag\n");
@@ -1755,21 +1755,21 @@ todo_wine
ok(!buffer[0], "Buffer must not change, got %02X\n", buffer[0]);
buffer[0] = 0;
- res = pInternetGetConnectedStateExW(NULL, buffer, sizeof(buffer) / sizeof(buffer[0]), 0);
+ res = pInternetGetConnectedStateExW(NULL, buffer, ARRAY_SIZE(buffer), 0);
ok(res == TRUE, "Expected TRUE, got %d\n", res);
sz = lstrlenW(buffer);
ok(sz > 0, "Expected a connection name\n");
buffer[0] = 0;
flags = 0;
- res = pInternetGetConnectedStateExW(&flags, buffer, sizeof(buffer) / sizeof(buffer[0]), 0);
+ res = pInternetGetConnectedStateExW(&flags, buffer, ARRAY_SIZE(buffer), 0);
ok(res == TRUE, "Expected TRUE, got %d\n", res);
ok(flags, "Expected at least one flag set\n");
sz = lstrlenW(buffer);
ok(sz > 0, "Expected a connection name\n");
flags = 0;
- res = pInternetGetConnectedStateExW(&flags, NULL, sizeof(buffer) / sizeof(buffer[0]), 0);
+ res = pInternetGetConnectedStateExW(&flags, NULL, ARRAY_SIZE(buffer), 0);
ok(res == TRUE, "Expected TRUE, got %d\n", res);
ok(flags, "Expected at least one flag set\n");
diff --git a/dlls/wininet/tests/url.c b/dlls/wininet/tests/url.c
index 0af58358eb..18c77b6647 100644
--- a/dlls/wininet/tests/url.c
+++ b/dlls/wininet/tests/url.c
@@ -672,17 +672,17 @@ static void InternetCrackUrlW_test(void)
memset(&comp, 0, sizeof comp);
comp.dwStructSize = sizeof(comp);
comp.lpszScheme = scheme;
- comp.dwSchemeLength = sizeof(scheme)/sizeof(scheme[0]);
+ comp.dwSchemeLength = ARRAY_SIZE(scheme);
comp.lpszHostName = host;
- comp.dwHostNameLength = sizeof(host)/sizeof(host[0]);
+ comp.dwHostNameLength = ARRAY_SIZE(host);
comp.lpszUserName = user;
- comp.dwUserNameLength = sizeof(user)/sizeof(user[0]);
+ comp.dwUserNameLength = ARRAY_SIZE(user);
comp.lpszPassword = pwd;
- comp.dwPasswordLength = sizeof(pwd)/sizeof(pwd[0]);
+ comp.dwPasswordLength = ARRAY_SIZE(pwd);
comp.lpszUrlPath = urlpart;
- comp.dwUrlPathLength = sizeof(urlpart)/sizeof(urlpart[0]);
+ comp.dwUrlPathLength = ARRAY_SIZE(urlpart);
comp.lpszExtraInfo = extra;
- comp.dwExtraInfoLength = sizeof(extra)/sizeof(extra[0]);
+ comp.dwExtraInfoLength = ARRAY_SIZE(extra);
SetLastError(0xdeadbeef);
r = InternetCrackUrlW(NULL, 0, 0, &comp );
@@ -721,9 +721,9 @@ static void InternetCrackUrlW_test(void)
memset(&comp, 0, sizeof comp);
comp.dwStructSize = sizeof comp;
comp.lpszHostName = host;
- comp.dwHostNameLength = sizeof(host)/sizeof(host[0]);
+ comp.dwHostNameLength = ARRAY_SIZE(host);
comp.lpszUrlPath = urlpart;
- comp.dwUrlPathLength = sizeof(urlpart)/sizeof(urlpart[0]);
+ comp.dwUrlPathLength = ARRAY_SIZE(urlpart);
r = InternetCrackUrlW(url, 0, 0, &comp );
ok( r, "failed to crack url\n");
@@ -739,11 +739,11 @@ static void InternetCrackUrlW_test(void)
memset(&comp, 0, sizeof comp);
comp.dwStructSize = sizeof comp;
comp.lpszHostName = host;
- comp.dwHostNameLength = sizeof(host)/sizeof(host[0]);
+ comp.dwHostNameLength = ARRAY_SIZE(host);
comp.lpszUrlPath = urlpart;
- comp.dwUrlPathLength = sizeof(urlpart)/sizeof(urlpart[0]);
+ comp.dwUrlPathLength = ARRAY_SIZE(urlpart);
comp.lpszExtraInfo = NULL;
- comp.dwExtraInfoLength = sizeof(extra)/sizeof(extra[0]);
+ comp.dwExtraInfoLength = ARRAY_SIZE(extra);
r = InternetCrackUrlW(url, 0, 0, &comp );
ok( r, "failed to crack url\n");
@@ -763,17 +763,17 @@ static void InternetCrackUrlW_test(void)
memset(&comp, 0, sizeof(comp));
comp.dwStructSize = sizeof(comp);
comp.lpszScheme = scheme;
- comp.dwSchemeLength = sizeof(scheme)/sizeof(scheme[0]);
+ comp.dwSchemeLength = ARRAY_SIZE(scheme);
comp.lpszHostName = host;
- comp.dwHostNameLength = sizeof(host)/sizeof(host[0]);
+ comp.dwHostNameLength = ARRAY_SIZE(host);
comp.lpszUserName = user;
- comp.dwUserNameLength = sizeof(user)/sizeof(user[0]);
+ comp.dwUserNameLength = ARRAY_SIZE(user);
comp.lpszPassword = pwd;
- comp.dwPasswordLength = sizeof(pwd)/sizeof(pwd[0]);
+ comp.dwPasswordLength = ARRAY_SIZE(pwd);
comp.lpszUrlPath = urlpart;
- comp.dwUrlPathLength = sizeof(urlpart)/sizeof(urlpart[0]);
+ comp.dwUrlPathLength = ARRAY_SIZE(urlpart);
comp.lpszExtraInfo = extra;
- comp.dwExtraInfoLength = sizeof(extra)/sizeof(extra[0]);
+ comp.dwExtraInfoLength = ARRAY_SIZE(extra);
r = InternetCrackUrlW(url2, 0, 0, &comp);
ok(!r, "InternetCrackUrl should have failed\n");
@@ -791,17 +791,17 @@ static void InternetCrackUrlW_test(void)
memset(&comp, 0, sizeof comp);
comp.dwStructSize = sizeof comp;
comp.lpszScheme = scheme;
- comp.dwSchemeLength = sizeof(scheme)/sizeof(scheme[0]);
+ comp.dwSchemeLength = ARRAY_SIZE(scheme);
comp.lpszHostName = host;
- comp.dwHostNameLength = sizeof(host)/sizeof(host[0]);
+ comp.dwHostNameLength = ARRAY_SIZE(host);
comp.lpszUserName = user;
- comp.dwUserNameLength = sizeof(user)/sizeof(user[0]);
+ comp.dwUserNameLength = ARRAY_SIZE(user);
comp.lpszPassword = pwd;
- comp.dwPasswordLength = sizeof(pwd)/sizeof(pwd[0]);
+ comp.dwPasswordLength = ARRAY_SIZE(pwd);
comp.lpszUrlPath = urlpart;
- comp.dwUrlPathLength = sizeof(urlpart)/sizeof(urlpart[0]);
+ comp.dwUrlPathLength = ARRAY_SIZE(urlpart);
comp.lpszExtraInfo = extra;
- comp.dwExtraInfoLength = sizeof(extra)/sizeof(extra[0]);
+ comp.dwExtraInfoLength = ARRAY_SIZE(extra);
r = InternetCrackUrlW(url3, 0, 0, &comp );
ok( r, "InternetCrackUrlW failed unexpectedly\n");
ok( host[0] == 'x', "host should be x.org\n");
@@ -812,9 +812,9 @@ static void InternetCrackUrlW_test(void)
memset(&comp, 0, sizeof(comp));
comp.dwStructSize = sizeof(comp);
comp.lpszHostName = host;
- comp.dwHostNameLength = sizeof(host)/sizeof(host[0]);
+ comp.dwHostNameLength = ARRAY_SIZE(host);
comp.lpszUrlPath = urlpart;
- comp.dwUrlPathLength = sizeof(urlpart)/sizeof(urlpart[0]);
+ comp.dwUrlPathLength = ARRAY_SIZE(urlpart);
r = InternetCrackUrlW(url3, 0, ICU_DECODE, &comp);
todo_wine ok(r, "InternetCrackUrlW failed unexpectedly\n");
todo_wine ok(!strcmp_wa(host, "x.org"), "host is %s, should be x.org\n", wine_dbgstr_w(host));
@@ -1224,7 +1224,7 @@ START_TEST(url)
return;
}
- for(i=0; i < sizeof(crack_url_tests)/sizeof(*crack_url_tests); i++)
+ for(i = 0; i < ARRAY_SIZE(crack_url_tests); i++)
test_crack_url(crack_url_tests+i);
test_long_url();
diff --git a/dlls/wininet/tests/urlcache.c b/dlls/wininet/tests/urlcache.c
index 310054b6a7..3799500489 100644
--- a/dlls/wininet/tests/urlcache.c
+++ b/dlls/wininet/tests/urlcache.c
@@ -891,13 +891,13 @@ static void test_urlcacheW(void)
if(ie10_cache) {
if(!MultiByteToWideChar(CP_ACP, 0, urls[6].encoded_url, -1,
- urls[6].url, sizeof(urls[6].url)/sizeof(WCHAR)))
+ urls[6].url, ARRAY_SIZE(urls[6].url)))
urls[6].url[0] = 0;
trace("converted url in test 6: %s\n", wine_dbgstr_w(urls[6].url));
}
- for(i=0; i<sizeof(urls)/sizeof(*urls); i++) {
+ for(i=0; i<ARRAY_SIZE(urls); i++) {
INTERNET_CACHE_ENTRY_INFOA *entry_infoA;
INTERNET_CACHE_ENTRY_INFOW *entry_infoW;
DWORD size;
--
2.14.4
June 21, 2018
[PATCH] appwiz.cpl: Support installing minor updates when installing addons
by Piotr Caban
Signed-off-by: Piotr Caban <piotr(a)codeweavers.com>
---
dlls/appwiz.cpl/addons.c | 5 +++++
1 file changed, 5 insertions(+)
June 21, 2018
Re: [PATCH 3/3] Add new test for DXTn volume textures.
by Marvin
Hi,
While running your changed tests on Windows, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at
https://testbot.winehq.org/JobDetails.pl?Key=39289
Your paranoid android.
=== w1064 (64 bit visual) ===
visual.c:8645: Test failed: Got unexpected color 0x00007580 for quad 2 (different colors).
June 21, 2018
[PATCH 3/3] Add new test for DXTn volume textures.
by Connor McAdams
To properly test the functionality of DXTn volume textures, I have added
new tests for DXT1, DXT3, and DXT5 replacing the old DXT5 test. I also
added a function to get the alpha values of pixels, although there may
have been a reason one wasn't included in the first place, it's the only
real way to make sure the alpha is behaving properly on the formats.
---
dlls/d3d9/tests/visual.c | 190 ++++++++++++++++++++++++++++++++---------------
1 file changed, 131 insertions(+), 59 deletions(-)
diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c
index b36c189..ec121b0 100644
--- a/dlls/d3d9/tests/visual.c
+++ b/dlls/d3d9/tests/visual.c
@@ -186,6 +186,31 @@ static void release_surface_readback(struct surface_readback *rb)
IDirect3DSurface9_Release(rb->surface);
}
+static DWORD getAlphaPixelColor(IDirect3DDevice9 *device, UINT x, UINT y)
+{
+ DWORD ret;
+ IDirect3DSurface9 *rt;
+ struct surface_readback rb;
+ HRESULT hr;
+
+ hr = IDirect3DDevice9_GetRenderTarget(device, 0, &rt);
+ if(FAILED(hr))
+ {
+ trace("Can't get the render target, hr %#x.\n", hr);
+ return 0xdeadbeed;
+ }
+
+ get_rt_readback(rt, &rb);
+ /* Remove the X channel for now. DirectX and OpenGL have different ideas how to treat it apparently, and it isn't
+ * really important for these tests
+ */
+ ret = get_readback_color(&rb, x, y) & 0xffffffff;
+ release_surface_readback(&rb);
+
+ IDirect3DSurface9_Release(rt);
+ return ret;
+}
+
static DWORD getPixelColor(IDirect3DDevice9 *device, UINT x, UINT y)
{
DWORD ret;
@@ -17741,22 +17766,39 @@ done:
DestroyWindow(window);
}
-static void volume_dxt5_test(void)
+static void volume_dxtn_test(void)
{
IDirect3DVolumeTexture9 *texture;
IDirect3DDevice9 *device;
D3DLOCKED_BOX box;
IDirect3D9 *d3d;
- unsigned int i;
+ unsigned int i, x;
ULONG refcount;
DWORD color;
HWND window;
HRESULT hr;
- static const char texture_data[] =
+ static const char dxt1_texture_data[] =
+ {
+ /* A 8x4x2 texture consisting of 4 4x4 blocks. The colors of the blocks are red, green, blue and white. */
+ 0x00, 0xF8, 0x00, 0xF8, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xE0, 0x07, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
+ };
+ static const char dxt3_texture_data[] =
+ {
+ /* A 8x4x2 texture consisting of 4 4x4 blocks. The colors of the blocks are red, green, blue and white. The red block goes from
+ * 15 to 0 on alpha level. */
+ 0xFF, 0xEE, 0xFF, 0xEE, 0xFF, 0xEE, 0xFF, 0xEE, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xDD, 0xFF, 0xDD, 0xFF, 0xDD, 0xFF, 0xDD, 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xCC, 0xFF, 0xCC, 0xFF, 0xCC, 0xFF, 0xCC, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xBB, 0xFF, 0xBB, 0xFF, 0xBB, 0xFF, 0xBB, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+ };
+ static const char dxt5_texture_data[] =
{
/* A 8x4x2 texture consisting of 4 4x4 blocks. The colors of the blocks are red, green, blue and white. */
- 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x80, 0x0D, 0xD8, 0x80, 0x0D, 0xD8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
@@ -17778,70 +17820,100 @@ static void volume_dxt5_test(void)
{{ 1.0f, -1.0f, 1.0f}, { 1.0f, 0.0f, 0.75f}},
{{ 1.0f, 1.0f, 1.0f}, { 1.0f, 1.0f, 0.75f}},
};
- static const DWORD expected_colors[] = {0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ffffff};
-
- window = create_window();
- d3d = Direct3DCreate9(D3D_SDK_VERSION);
- ok(!!d3d, "Failed to create a D3D object.\n");
- if (FAILED(IDirect3D9_CheckDeviceFormat(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
- D3DFMT_X8R8G8B8, 0, D3DRTYPE_VOLUMETEXTURE, D3DFMT_DXT5)))
+ static const DWORD expected_colors[3][8] = {
{
- skip("DXT5 volume textures are not supported, skipping test.\n");
- goto done;
- }
- if (!(device = create_device(d3d, window, window, TRUE)))
+ 0xFFFF0000, 0x00000000, 0xFF00FF00, 0xFF00FF00,
+ 0xFF0000FF, 0xFF0000FF, 0xFFFFFFFF, 0xFFFFFFFF,
+ },
{
- skip("Failed to create a D3D device, skipping tests.\n");
- goto done;
+ 0xFFFF0000, 0xEEFF0000, 0xFF00FF00, 0xDD00FF00,
+ 0xFF0000FF, 0xCC0000FF, 0xFFFFFFFF, 0xBBFFFFFF,
+ },
+ {
+ 0xFFFF0000, 0x00FF0000, 0xFF00FF00, 0xFF00FF00,
+ 0xFF0000FF, 0xFF0000FF, 0xFFFFFFFF, 0xFFFFFFFF
}
+ };
+ static const char *const dxtn[3] = {"DXT1", "DXT3", "DXT5"};
+ static const unsigned int fmt[3] = {D3DFMT_DXT1, D3DFMT_DXT3, D3DFMT_DXT5};
- hr = IDirect3DDevice9_CreateVolumeTexture(device, 8, 4, 2, 1, 0, D3DFMT_DXT5,
- D3DPOOL_MANAGED, &texture, NULL);
- ok(SUCCEEDED(hr), "Failed to create volume texture, hr %#x.\n", hr);
+ for (x = 0; x < 3; x++)
+ {
+ window = create_window();
+ d3d = Direct3DCreate9(D3D_SDK_VERSION);
+ ok(!!d3d, "Failed to create a D3D object.\n");
+ if (FAILED(IDirect3D9_CheckDeviceFormat(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
+ D3DFMT_A8B8G8R8, 0, D3DRTYPE_VOLUMETEXTURE, fmt[x])))
+ {
+ skip("%s volume textures are not supported, skipping test.\n", dxtn[x]);
+ continue;
+ }
+ if (!(device = create_device(d3d, window, window, TRUE)))
+ {
+ skip("Failed to create a D3D device, skipping tests.\n");
+ continue;
+ }
- hr = IDirect3DVolumeTexture9_LockBox(texture, 0, &box, NULL, 0);
- ok(SUCCEEDED(hr), "Failed to lock volume texture, hr %#x.\n", hr);
- memcpy(box.pBits, texture_data, sizeof(texture_data));
- hr = IDirect3DVolumeTexture9_UnlockBox(texture, 0);
- ok(SUCCEEDED(hr), "Failed to unlock volume texture, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_CreateVolumeTexture(device, 8, 4, 2, 1, 0, fmt[x],
+ D3DPOOL_MANAGED, &texture, NULL);
+ ok(SUCCEEDED(hr), "Failed to create volume texture, hr %#x.\n", hr);
- hr = IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0));
- ok(SUCCEEDED(hr), "Failed to set FVF, hr %#x.\n", hr);
- hr = IDirect3DDevice9_SetTexture(device, 0, (IDirect3DBaseTexture9 *)texture);
- ok(SUCCEEDED(hr), "Failed to set texture, hr %#x.\n", hr);
- hr = IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
- ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr);
- hr = IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
- ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr);
- hr = IDirect3DDevice9_SetTextureStageState(device, 1, D3DTSS_COLOROP, D3DTOP_DISABLE);
- ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr);
- hr = IDirect3DDevice9_SetSamplerState(device, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
- ok(SUCCEEDED(hr), "Failed to set mag filter, hr %#x.\n", hr);
+ hr = IDirect3DVolumeTexture9_LockBox(texture, 0, &box, NULL, 0);
+ ok(SUCCEEDED(hr), "Failed to lock volume texture, hr %#x.\n", hr);
+ switch (x)
+ {
+ case 0:
+ memcpy(box.pBits, dxt1_texture_data, sizeof(dxt1_texture_data));
+ break;
+ case 1:
+ memcpy(box.pBits, dxt3_texture_data, sizeof(dxt3_texture_data));
+ break;
+ case 2:
+ memcpy(box.pBits, dxt5_texture_data, sizeof(dxt5_texture_data));
+ break;
+ }
+ hr = IDirect3DVolumeTexture9_UnlockBox(texture, 0);
+ ok(SUCCEEDED(hr), "Failed to unlock volume texture, hr %#x.\n", hr);
- hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00ff00ff, 1.0f, 0);
- ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
- hr = IDirect3DDevice9_BeginScene(device);
- ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
- hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quads[0], sizeof(*quads));
- ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
- hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quads[4], sizeof(*quads));
- ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
- hr = IDirect3DDevice9_EndScene(device);
- ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0));
+ ok(SUCCEEDED(hr), "Failed to set FVF, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_SetTexture(device, 0, (IDirect3DBaseTexture9 *)texture);
+ ok(SUCCEEDED(hr), "Failed to set texture, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_SetTextureStageState(device, 1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_SetSamplerState(device, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
+ ok(SUCCEEDED(hr), "Failed to set mag filter, hr %#x.\n", hr);
- for (i = 0; i < 4; i++)
- {
- color = getPixelColor(device, 80 + 160 * i, 240);
- ok (color_match(color, expected_colors[i], 1),
- "Expected color 0x%08x, got 0x%08x, case %u.\n", expected_colors[i], color, i);
+ hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00ff00ff, 1.0f, 0);
+ ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_BeginScene(device);
+ ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quads[0], sizeof(*quads));
+ ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quads[4], sizeof(*quads));
+ ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+ hr = IDirect3DDevice9_EndScene(device);
+ ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+ for (i = 0; i < 8; i++)
+ {
+ color = getAlphaPixelColor(device, 40 + 80 * i, 240);
+ ok (color_match(color, expected_colors[x][i], 1),
+ "Expected color 0x%08x, got 0x%08x, case %u.\n", expected_colors[x][i], color, i);
+ }
+
+ hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
+ ok(SUCCEEDED(hr), "Failed to present, hr %#x.\n", hr);
+ IDirect3DVolumeTexture9_Release(texture);
+
+ refcount = IDirect3DDevice9_Release(device);
+ ok(!refcount, "Device has %u references left.\n", refcount);
}
- hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
- ok(SUCCEEDED(hr), "Failed to present, hr %#x.\n", hr);
- IDirect3DVolumeTexture9_Release(texture);
- refcount = IDirect3DDevice9_Release(device);
- ok(!refcount, "Device has %u references left.\n", refcount);
-done:
IDirect3D9_Release(d3d);
DestroyWindow(window);
}
@@ -24131,7 +24203,7 @@ START_TEST(visual)
zenable_test();
fog_special_test();
volume_srgb_test();
- volume_dxt5_test();
+ volume_dxtn_test();
add_dirty_rect_test();
multisampled_depth_buffer_test();
resz_test();
--
2.7.4
June 21, 2018
[PATCH 2/3] Add format change before upload for non-volume textures
by Connor McAdams
This patch keeps non-volume DXTn textures from being converted to
b8g8r8a8. I have tested it and it does work converting them all, but the
GPU can handle them normally. Volume textures are the only ones the
GPU handles incorrectly.
---
dlls/wined3d/texture.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c
index c316906..a998b9c 100644
--- a/dlls/wined3d/texture.c
+++ b/dlls/wined3d/texture.c
@@ -1948,6 +1948,32 @@ void wined3d_texture_upload_data(struct wined3d_texture *texture, unsigned int s
bo.addr += src_box->left * format->byte_count;
}
+ if (format->id == WINED3DFMT_DXT5 || format->id == WINED3DFMT_DXT4 ||
+ format->id == WINED3DFMT_DXT3 || format->id == WINED3DFMT_DXT2 ||
+ format->id == WINED3DFMT_DXT1)
+ {
+ if (target == GL_TEXTURE_3D)
+ {
+ struct wined3d_format temp;
+
+ f = *format;
+ temp.upload = f.upload;
+ format = &f;
+ format = wined3d_get_format(gl_info, WINED3DFMT_B8G8R8A8_UNORM, WINED3DUSAGE_TEXTURE);
+ f = *format;
+ f.upload = temp.upload;
+ f.conv_byte_count = 4;
+ format = &f;
+ texture->resource.format_flags &= ~WINED3DFMT_FLAG_BLOCKS;
+ }
+ else
+ {
+ f = *format;
+ f.upload = NULL;
+ format = &f;
+ }
+ }
+
if (format->upload)
{
unsigned int dst_row_pitch, dst_slice_pitch;
--
2.7.4
June 21, 2018
[PATCH 1/3] Add decompression functions for DXT1, DXT3, and DXT5
by Connor McAdams
This adds decompression functions for DXT1, DXT3, and DXT5 formats,
which covers all the possible DXT formats. These are used for
decompressing DXTn volume textures, and converting them to b8g8r8a8.
---
dlls/wined3d/utils.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 305 insertions(+), 16 deletions(-)
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index 937c1bc..ce1ccdf 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -840,6 +840,305 @@ static void convert_s8_uint_d24_float(const BYTE *src, BYTE *dst, UINT src_row_p
}
}
+static void dxt5_decompress_block(const BYTE *src, BYTE *dst, UINT width, UINT height, UINT depth,
+ unsigned int x_pos, unsigned int y_pos, unsigned int z_pos, UINT dst_slice_pitch, UINT64 cur_block)
+{
+ DWORD alpha_lookup;
+ DWORD bgra;
+ DWORD temp;
+ UINT64 alpha_block, alpha_index, color_block, color_index;
+ DWORD *Dest;
+ const UINT64 *Source;
+ unsigned short color[2];
+ unsigned int i, x, y;
+ unsigned char alpha_val, color_val;
+ unsigned char alpha[8];
+ unsigned char r[4];
+ unsigned char g[4];
+ unsigned char b[4];
+
+ Source = (const UINT64 *)(src + cur_block * 16);
+ alpha_block = Source[0];
+ color_block = Source[1];
+
+ alpha[0] = alpha_block & 0xFF;
+ alpha[1] = (alpha_block >> 8) & 0xFF;
+
+ /* Generate alpha lookup values */
+ if (alpha[0] > alpha[1])
+ {
+ for (i = 0; i < 6; i++)
+ alpha[2 + i] = (((6 - i) * alpha[0]) + ((1 + i) * alpha[1])) / 7;
+ }
+ else if (alpha[0] <= alpha[1])
+ {
+ for (i = 0; i < 4; i++)
+ alpha[2 + i] = (((4 - i) * alpha[0]) + ((1 + i) * alpha[1])) / 5;
+ alpha[6] = 0;
+ alpha[7] = 255;
+ }
+
+ /* Generate color lookup values */
+ color[0] = color_block & 0xFFFF;
+ color[1] = (color_block >> 16) & 0xFFFF;
+
+ for (i = 0; i < 2; i++)
+ {
+ temp = (color[i] >> 11) * 255 + 16;
+ r[i] = (temp / 32 + temp) / 32;
+ temp = ((color[i] >> 5) & 0x3F) * 255 + 32;
+ g[i] = (temp / 64 + temp) / 64;
+ temp = (color[i] & 0x1F) * 255 + 16;
+ b[i] = (temp / 32 + temp) / 32;
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ r[2 + i] = (2 * r[0 + i] + r[1 - i]) / 3;
+ g[2 + i] = (2 * g[0 + i] + g[1 - i]) / 3;
+ b[2 + i] = (2 * b[0 + i] + b[1 - i]) / 3;
+ }
+
+ color_index = (color_block >> 32) & 0xFFFFFFFF;
+ alpha_index = (alpha_block >> 16);
+
+ Dest = (DWORD *)(dst + z_pos * dst_slice_pitch);
+
+ for (y = 0; y < 4; y++) {
+ if (y_pos + y >= height)
+ break;
+ for (x = 0; x < 4; x++) {
+ if (x_pos + x >= width)
+ break;
+
+ color_val = 0;
+ alpha_val = 0;
+ bgra = 0;
+
+ color_val = (color_index >> (y * 8));
+ color_val = (color_val >> (x * 2)) & 0x3;
+ alpha_lookup = (alpha_index >> (y * 12)) & 0xFFF;
+ alpha_val = (alpha_lookup >> (x * 3)) & 0x7;
+ bgra = ((alpha[alpha_val] << 24) | (r[color_val] << 16) | (g[color_val] << 8) | b[color_val]);
+ Dest[(y_pos + y) * width + (x_pos + x)] = bgra;
+ }
+ }
+}
+
+static void convert_dxt5_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+ UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth)
+{
+ UINT64 current_block;
+ unsigned int x, y, z;
+
+ current_block = 0;
+
+ for (z = 0; z < depth; z++)
+ {
+ for (y = 0; y < height; y += 4)
+ {
+ for (x = 0; x < width; x += 4)
+ {
+ dxt5_decompress_block(src, dst, width, height, depth, x, y, z, dst_slice_pitch, current_block);
+ current_block++;
+ }
+ }
+ }
+}
+
+static void dxt3_decompress_block(const BYTE *src, BYTE *dst, UINT width, UINT height, UINT depth,
+ unsigned int x_pos, unsigned int y_pos, unsigned int z_pos, UINT dst_slice_pitch, UINT64 cur_block)
+{
+ DWORD bgra;
+ DWORD temp;
+ UINT64 alpha_block, alpha_lookup, color_block, color_index;
+ DWORD *Dest;
+ const UINT64 *Source;
+ unsigned short color[2];
+ unsigned int i, x, y;
+ unsigned char alpha_val, color_val;
+ unsigned char r[4];
+ unsigned char g[4];
+ unsigned char b[4];
+
+ Source = (const UINT64 *)(src + cur_block * 16);
+ alpha_block = Source[0];
+ color_block = Source[1];
+
+ /* Generate color lookup values */
+ color[0] = color_block & 0xFFFF;
+ color[1] = (color_block >> 16) & 0xFFFF;
+
+ for (i = 0; i < 2; i++)
+ {
+ temp = (color[i] >> 11) * 255 + 16;
+ r[i] = (temp / 32 + temp) / 32;
+ temp = ((color[i] >> 5) & 0x3F) * 255 + 32;
+ g[i] = (temp / 64 + temp) / 64;
+ temp = (color[i] & 0x1F) * 255 + 16;
+ b[i] = (temp / 32 + temp) / 32;
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ r[2 + i] = (2 * r[0 + i] + r[1 - i]) / 3;
+ g[2 + i] = (2 * g[0 + i] + g[1 - i]) / 3;
+ b[2 + i] = (2 * b[0 + i] + b[1 - i]) / 3;
+ }
+
+ color_index = (color_block >> 32) & 0xFFFFFFFF;
+ Dest = (DWORD *)(dst + z_pos * dst_slice_pitch);
+
+ for (y = 0; y < 4; y++) {
+ if (y_pos + y >= height)
+ break;
+ for (x = 0; x < 4; x++) {
+ if (x_pos + x >= width)
+ break;
+
+ color_val = 0;
+ alpha_val = 0;
+ bgra = 0;
+
+ color_val = (color_index >> (y * 8));
+ color_val = (color_val >> (x * 2)) & 0x3;
+
+ alpha_lookup = (alpha_block >> (y * 16)) & 0xFFFF;
+ alpha_val = (alpha_lookup >> (x * 4)) & 0xF;
+ temp = alpha_val * 255 + 8;
+ alpha_val = (temp / 16 + temp) / 16;
+
+ bgra = ((alpha_val << 24) | (r[color_val] << 16) | (g[color_val] << 8) | b[color_val]);
+ Dest[(y_pos + y) * width + (x_pos + x)] = bgra;
+ }
+ }
+}
+
+static void convert_dxt3_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+ UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth)
+{
+ UINT64 current_block;
+ unsigned int x, y, z;
+
+ current_block = 0;
+
+ for (z = 0; z < depth; z++)
+ {
+ for (y = 0; y < height; y += 4)
+ {
+ for (x = 0; x < width; x += 4)
+ {
+ dxt3_decompress_block(src, dst, width, height, depth, x, y, z, dst_slice_pitch, current_block);
+ current_block++;
+ }
+ }
+ }
+}
+
+static void dxt1_decompress_block(const BYTE *src, BYTE *dst, UINT width, UINT height, UINT depth,
+ unsigned int x_pos, unsigned int y_pos, unsigned int z_pos, UINT dst_slice_pitch, UINT64 cur_block)
+{
+ DWORD bgra;
+ DWORD temp;
+ UINT64 color_block, color_index;
+ DWORD *Dest;
+ const UINT64 *Source;
+ unsigned short color[2];
+ unsigned int i, x, y;
+ unsigned char color_val;
+ unsigned char alpha;
+ unsigned char r[4];
+ unsigned char g[4];
+ unsigned char b[4];
+ unsigned char use_alpha;
+
+ Source = (const UINT64 *)(src + cur_block * 8);
+ color_block = Source[0];
+
+ /* Generate color lookup values */
+ color[0] = color_block & 0xFFFF;
+ color[1] = (color_block >> 16) & 0xFFFF;
+
+ for (i = 0; i < 2; i++)
+ {
+ temp = (color[i] >> 11) * 255 + 16;
+ r[i] = (temp / 32 + temp) / 32;
+ temp = ((color[i] >> 5) & 0x3F) * 255 + 32;
+ g[i] = (temp / 64 + temp) / 64;
+ temp = (color[i] & 0x1F) * 255 + 16;
+ b[i] = (temp / 32 + temp) / 32;
+ }
+
+ if (color[0] > color[1])
+ {
+ for (i = 0; i < 2; i++)
+ {
+ r[2 + i] = (2 * r[0 + i] + r[1 - i]) / 3;
+ g[2 + i] = (2 * g[0 + i] + g[1 - i]) / 3;
+ b[2 + i] = (2 * b[0 + i] + b[1 - i]) / 3;
+ }
+ use_alpha = 0;
+ }
+ else if (color[0] <= color[1])
+ {
+ r[2] = (r[0] + r[1]) / 2;
+ g[2] = (g[0] + g[1]) / 2;
+ b[2] = (b[0] + b[1]) / 2;
+
+ r[3] = 0;
+ g[3] = 0;
+ b[3] = 0;
+
+ use_alpha = 1;
+ }
+
+ color_index = (color_block >> 32) & 0xFFFFFFFF;
+ Dest = (DWORD *)(dst + z_pos * dst_slice_pitch);
+
+ for (y = 0; y < 4; y++) {
+ if (y_pos + y >= height)
+ break;
+ for (x = 0; x < 4; x++) {
+ if (x_pos + x >= width)
+ break;
+
+ color_val = 0;
+ bgra = 0;
+
+ color_val = (color_index >> (y * 8));
+ color_val = (color_val >> (x * 2)) & 0x3;
+ if (color_val == 3 && use_alpha == 1)
+ alpha = 0;
+ else
+ alpha = 255;
+
+ bgra = ((alpha << 24) | (r[color_val] << 16) | (g[color_val] << 8) | b[color_val]);
+ Dest[(y_pos + y) * width + (x_pos + x)] = bgra;
+ }
+ }
+}
+
+static void convert_dxt1_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+ UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth)
+{
+ UINT64 current_block;
+ unsigned int x, y, z;
+
+ current_block = 0;
+
+ for (z = 0; z < depth; z++)
+ {
+ for (y = 0; y < height; y += 4)
+ {
+ for (x = 0; x < width; x += 4)
+ {
+ dxt1_decompress_block(src, dst, width, height, depth, x, y, z, dst_slice_pitch, current_block);
+ current_block++;
+ }
+ }
+ }
+}
+
static void x8_d24_unorm_upload(const BYTE *src, BYTE *dst,
unsigned int src_row_pitch, unsigned int src_slice_pitch,
unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
@@ -1118,27 +1417,27 @@ static const struct wined3d_format_texture_info format_texture_info[] =
GL_RGBA, GL_UNSIGNED_BYTE, 0,
WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING
| WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_COMPRESSED,
- EXT_TEXTURE_COMPRESSION_S3TC, NULL},
+ EXT_TEXTURE_COMPRESSION_S3TC, convert_dxt1_b8g8r8a8_unorm},
{WINED3DFMT_DXT2, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0,
WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING
| WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_COMPRESSED,
- EXT_TEXTURE_COMPRESSION_S3TC, NULL},
+ EXT_TEXTURE_COMPRESSION_S3TC, convert_dxt3_b8g8r8a8_unorm},
{WINED3DFMT_DXT3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0,
WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING
| WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_COMPRESSED,
- EXT_TEXTURE_COMPRESSION_S3TC, NULL},
+ EXT_TEXTURE_COMPRESSION_S3TC, convert_dxt3_b8g8r8a8_unorm},
{WINED3DFMT_DXT4, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0,
WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING
| WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_COMPRESSED,
- EXT_TEXTURE_COMPRESSION_S3TC, NULL},
+ EXT_TEXTURE_COMPRESSION_S3TC, convert_dxt5_b8g8r8a8_unorm},
{WINED3DFMT_DXT5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0,
WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING
| WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_COMPRESSED,
- EXT_TEXTURE_COMPRESSION_S3TC, NULL},
+ EXT_TEXTURE_COMPRESSION_S3TC, convert_dxt5_b8g8r8a8_unorm},
{WINED3DFMT_BC1_UNORM, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0,
WINED3DFMT_FLAG_TEXTURE | WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING | WINED3DFMT_FLAG_FILTERING
@@ -3394,17 +3693,7 @@ static void apply_format_fixups(struct wined3d_adapter *adapter, struct wined3d_
* for dx9 GPUs support it, some do not, so not supporting DXTn volumes is OK for d3d9.
*
* Note that GL_NV_texture_compression_vtc adds this functionality to OpenGL, but the
- * block layout is not compatible with the one used by d3d. See volume_dxt5_test. */
- idx = get_format_idx(WINED3DFMT_DXT1);
- gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
- idx = get_format_idx(WINED3DFMT_DXT2);
- gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
- idx = get_format_idx(WINED3DFMT_DXT3);
- gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
- idx = get_format_idx(WINED3DFMT_DXT4);
- gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
- idx = get_format_idx(WINED3DFMT_DXT5);
- gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
+ * block layout is not compatible with the one used by d3d. See volume_dxtn_test. */
idx = get_format_idx(WINED3DFMT_BC1_UNORM);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_BC1_UNORM_SRGB);
--
2.7.4
June 21, 2018
[PATCH 0/3] Add DXTn 3D texture support.
by Connor McAdams
This patch series adds support for using DXTn volume textures, by
decompressing and converting them using the CPU. This fixes issues with games
such as Halo Online, and others mentioned in the bugzilla page here:
https://bugs.winehq.org/show_bug.cgi?id=39253
Let me know what needs changed or fixed, this is my first patch, so I assume
it wont be perfect. :)
Thanks,
Connor McAdams (Conmanx360 in IRC)
Connor McAdams (3):
Add decompression functions for DXT1, DXT3, and DXT5
Add format change before upload for non-volume textures
Add new test for DXTn volume textures.
dlls/d3d9/tests/visual.c | 190 +++++++++++++++++++---------
dlls/wined3d/texture.c | 26 ++++
dlls/wined3d/utils.c | 321 ++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 462 insertions(+), 75 deletions(-)
--
2.7.4
June 21, 2018
[PATCH 2/2] msi: Add support for re-caching package
by Piotr Caban
Signed-off-by: Piotr Caban <piotr(a)codeweavers.com>
---
dlls/msi/action.c | 42 ++++++++++++++++++++++++++++++++++++++----
dlls/msi/msi.c | 27 +++++++++++++++++++++------
dlls/msi/msipriv.h | 4 +++-
dlls/msi/package.c | 14 ++++++++++++--
dlls/msi/tests/package.c | 6 +++---
5 files changed, 77 insertions(+), 16 deletions(-)
June 21, 2018
[PATCH 1/2] msi: Report error when cached installer has different version
by Piotr Caban
Signed-off-by: Piotr Caban <piotr(a)codeweavers.com>
---
dlls/msi/package.c | 114
+++++++++++++++++++++++++----------------------
dlls/msi/tests/package.c | 6 +--
2 files changed, 63 insertions(+), 57 deletions(-)
June 21, 2018
[PATCH] winex11.drv: Move condition to the proper place.
by Andrey Gusev
Signed-off-by: Andrey Gusev <andrey.goosev(a)gmail.com>
---
dlls/winex11.drv/event.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index a0bfe052df..4aee41586f 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -1627,8 +1627,8 @@ static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
}
}
- if( p_data ) XFree(p_data);
}
+ if( p_data ) XFree(p_data);
}
--
2.17.1
June 21, 2018
[PATCH v2] comctl32/imagelist: fix ImageList_Read/Write.
by Denis Malikov
Fix for versions x600 and x620 and pointer calculation for mixing image and mask bits.
Tested on *.reg files extracted from:
- XP/2003 key HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\TrayNotify;
- Vista/7 key HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify
Signed-off-by: Denis Malikov <mdn40000(a)mail.ru>
---
dlls/comctl32/imagelist.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/dlls/comctl32/imagelist.c b/dlls/comctl32/imagelist.c
index a08d60752e..eadbf134b4 100644
--- a/dlls/comctl32/imagelist.c
+++ b/dlls/comctl32/imagelist.c
@@ -80,9 +80,11 @@ struct _IMAGELIST
BOOL color_table_set;
LONG ref; /* reference count */
+ USHORT usVersion; /* keep stream version here */
};
#define IMAGELIST_MAGIC 0x53414D58
+#define IMAGELIST_VERSION 0x101
/* Header used by ImageList_Read() and ImageList_Write() */
#include "pshpack2.h"
@@ -806,6 +808,7 @@ ImageList_Create (INT cx, INT cy, UINT flags,
himl->clrFg = CLR_DEFAULT;
himl->clrBk = CLR_NONE;
himl->color_table_set = FALSE;
+ himl->usVersion = 0;
/* initialize overlay mask indices */
for (nCount = 0; nCount < MAX_OVERLAYIMAGE; nCount++)
@@ -2267,7 +2270,9 @@ HIMAGELIST WINAPI ImageList_Read(IStream *pstm)
return NULL;
if (ilHead.usMagic != (('L' << 8) | 'I'))
return NULL;
- if (ilHead.usVersion != 0x101) /* probably version? */
+ if (ilHead.usVersion != IMAGELIST_VERSION &&
+ ilHead.usVersion != 0x600 && /* XP/2003 version */
+ ilHead.usVersion != 0x620) /* Vista/7 version */
return NULL;
TRACE("cx %u, cy %u, flags 0x%04x, cCurImage %u, cMaxImage %u\n",
@@ -2277,6 +2282,9 @@ HIMAGELIST WINAPI ImageList_Read(IStream *pstm)
if (!himl)
return NULL;
+ /* keep version from stream */
+ himl->usVersion = ilHead.usVersion;
+
if (!(image_bits = read_bitmap(pstm, image_info)))
{
WARN("failed to read bitmap from stream\n");
@@ -2296,23 +2304,25 @@ HIMAGELIST WINAPI ImageList_Read(IStream *pstm)
{
DWORD *ptr = image_bits;
BYTE *mask_ptr = mask_bits;
- int stride = himl->cy * image_info->bmiHeader.biWidth;
+ int stride = himl->cy * (ilHead.usVersion != IMAGELIST_VERSION ? himl->cx : image_info->bmiHeader.biWidth);
+ int image_step = ilHead.usVersion != IMAGELIST_VERSION ? 1 : TILE_COUNT;
+ int mask_step = ilHead.usVersion != IMAGELIST_VERSION ? 4 : 8;
if (image_info->bmiHeader.biHeight > 0) /* bottom-up */
{
ptr += image_info->bmiHeader.biHeight * image_info->bmiHeader.biWidth - stride;
- mask_ptr += (image_info->bmiHeader.biHeight * image_info->bmiHeader.biWidth - stride) / 8;
+ mask_ptr += (image_info->bmiHeader.biHeight * image_info->bmiHeader.biWidth - stride) / mask_step;
stride = -stride;
image_info->bmiHeader.biHeight = himl->cy;
}
else image_info->bmiHeader.biHeight = -himl->cy;
- for (i = 0; i < ilHead.cCurImage; i += TILE_COUNT)
+ for (i = 0; i < ilHead.cCurImage; i += image_step)
{
- add_dib_bits( himl, i, min( ilHead.cCurImage - i, TILE_COUNT ),
+ add_dib_bits( himl, i, min( ilHead.cCurImage - i, image_step ),
himl->cx, himl->cy, image_info, mask_info, ptr, mask_ptr );
ptr += stride;
- mask_ptr += stride / 8;
+ mask_ptr += stride / mask_step;
}
}
else
--
2.16.2.windows.1
June 21, 2018
[PATCH 7/7] ninput: Add RegisterOutputCallbackInteractionContext() stub.
by Józef Kucia
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
dlls/ninput/main.c | 13 +++++++++++++
dlls/ninput/ninput.spec | 2 +-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/dlls/ninput/main.c b/dlls/ninput/main.c
index 01703749d087..5d2be62fec21 100644
--- a/dlls/ninput/main.c
+++ b/dlls/ninput/main.c
@@ -148,6 +148,19 @@ HRESULT WINAPI SetInteractionConfigurationInteractionContext(HINTERACTIONCONTEXT
return S_OK;
}
+HRESULT WINAPI RegisterOutputCallbackInteractionContext(HINTERACTIONCONTEXT handle,
+ INTERACTION_CONTEXT_OUTPUT_CALLBACK callback, void *data)
+{
+ struct interaction_context *context = context_from_handle(handle);
+
+ FIXME("context %p, callback %p, data %p: stub!.\n", context, callback, data);
+
+ if (!context)
+ return E_HANDLE;
+
+ return S_OK;
+}
+
HRESULT WINAPI ProcessInertiaInteractionContext(HINTERACTIONCONTEXT context)
{
FIXME("context %p: stub!\n", context);
diff --git a/dlls/ninput/ninput.spec b/dlls/ninput/ninput.spec
index 70d296cc597f..e2897872b683 100644
--- a/dlls/ninput/ninput.spec
+++ b/dlls/ninput/ninput.spec
@@ -12,7 +12,7 @@
@ stub ProcessBufferedPacketsInteractionContext
@ stdcall ProcessInertiaInteractionContext(ptr)
@ stub ProcessPointerFramesInteractionContext
-@ stub RegisterOutputCallbackInteractionContext
+@ stdcall RegisterOutputCallbackInteractionContext(ptr ptr ptr)
@ stub RemovePointerInteractionContext
@ stub ResetInteractionContext
@ stub SetCrossSlideParametersInteractionContext
--
2.16.4
June 21, 2018
[PATCH 6/7] ninput: Add SetInteractionConfigurationInteractionContext() stub.
by Józef Kucia
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
dlls/ninput/main.c | 17 +++++++++++++++++
dlls/ninput/ninput.spec | 2 +-
dlls/ninput/tests/ninput.c | 36 ++++++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/dlls/ninput/main.c b/dlls/ninput/main.c
index 90f9e7e0c4ee..01703749d087 100644
--- a/dlls/ninput/main.c
+++ b/dlls/ninput/main.c
@@ -131,6 +131,23 @@ HRESULT WINAPI SetPropertyInteractionContext(HINTERACTIONCONTEXT handle,
}
}
+HRESULT WINAPI SetInteractionConfigurationInteractionContext(HINTERACTIONCONTEXT handle,
+ UINT32 count, const INTERACTION_CONTEXT_CONFIGURATION *configuration)
+{
+ struct interaction_context *context = context_from_handle(handle);
+
+ FIXME("context %p, count %u, configuration %p: stub!.\n", context, count, configuration);
+
+ if (!context)
+ return E_HANDLE;
+ if (!count)
+ return E_INVALIDARG;
+ if (!configuration)
+ return E_POINTER;
+
+ return S_OK;
+}
+
HRESULT WINAPI ProcessInertiaInteractionContext(HINTERACTIONCONTEXT context)
{
FIXME("context %p: stub!\n", context);
diff --git a/dlls/ninput/ninput.spec b/dlls/ninput/ninput.spec
index 166e3d1ec8c1..70d296cc597f 100644
--- a/dlls/ninput/ninput.spec
+++ b/dlls/ninput/ninput.spec
@@ -17,7 +17,7 @@
@ stub ResetInteractionContext
@ stub SetCrossSlideParametersInteractionContext
@ stub SetInertiaParameterInteractionContext
-@ stub SetInteractionConfigurationInteractionContext
+@ stdcall SetInteractionConfigurationInteractionContext(ptr long ptr)
@ stub SetMouseWheelParameterInteractionContext
@ stub SetPivotInteractionContext
@ stdcall SetPropertyInteractionContext(ptr long long)
diff --git a/dlls/ninput/tests/ninput.c b/dlls/ninput/tests/ninput.c
index 156e3e898366..7fd389378b01 100644
--- a/dlls/ninput/tests/ninput.c
+++ b/dlls/ninput/tests/ninput.c
@@ -82,8 +82,44 @@ static void test_properties(void)
ok(hr == S_OK, "Failed to destroy context, hr %#x.\n", hr);
}
+static void test_configuration(void)
+{
+ HINTERACTIONCONTEXT context;
+ HRESULT hr;
+
+ static const INTERACTION_CONTEXT_CONFIGURATION config[] =
+ {
+ {
+ INTERACTION_ID_MANIPULATION,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION |
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_X |
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_Y |
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_SCALING |
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_INERTIA |
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_SCALING_INERTIA
+ },
+ };
+
+ hr = CreateInteractionContext(&context);
+ ok(hr == S_OK, "Failed to create context, hr %#x.\n", hr);
+
+ hr = SetInteractionConfigurationInteractionContext(NULL, 0, NULL);
+ ok(hr == E_HANDLE, "Got hr %#x.\n", hr);
+ hr = SetInteractionConfigurationInteractionContext(context, 0, NULL);
+ ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+ hr = SetInteractionConfigurationInteractionContext(context, 1, NULL);
+ ok(hr == E_POINTER, "Got hr %#x.\n", hr);
+
+ hr = SetInteractionConfigurationInteractionContext(context, ARRAY_SIZE(config), config);
+ ok(hr == S_OK, "Failed to set configuration, hr %#x.\n", hr);
+
+ hr = DestroyInteractionContext(context);
+ ok(hr == S_OK, "Failed to destroy context, hr %#x.\n", hr);
+}
+
START_TEST(ninput)
{
test_context();
test_properties();
+ test_configuration();
}
--
2.16.4
June 21, 2018
[PATCH 5/7] ninput: Implement SetPropertyInteractionContext().
by Józef Kucia
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
dlls/ninput/main.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++
dlls/ninput/ninput.spec | 4 ++--
dlls/ninput/tests/ninput.c | 48 +++++++++++++++++++++++++++++++++++++
3 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/dlls/ninput/main.c b/dlls/ninput/main.c
index 932d159ab390..90f9e7e0c4ee 100644
--- a/dlls/ninput/main.c
+++ b/dlls/ninput/main.c
@@ -72,6 +72,65 @@ HRESULT WINAPI DestroyInteractionContext(HINTERACTIONCONTEXT handle)
return S_OK;
}
+HRESULT WINAPI GetPropertyInteractionContext(HINTERACTIONCONTEXT handle,
+ INTERACTION_CONTEXT_PROPERTY property, UINT32 *value)
+{
+ struct interaction_context *context = context_from_handle(handle);
+
+ TRACE("context %p, property %#x, value %p.\n", context, property, value);
+
+ if (!context)
+ return E_HANDLE;
+ if (!value)
+ return E_POINTER;
+
+ switch (property)
+ {
+ case INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS:
+ case INTERACTION_CONTEXT_PROPERTY_INTERACTION_UI_FEEDBACK:
+ FIXME("Unhandled property %#x.\n", property);
+ *value = 0;
+ return E_NOTIMPL;
+
+ case INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS:
+ *value = context->filter_pointers;
+ return S_OK;
+
+ default:
+ WARN("Invalid property %#x.\n", property);
+ return E_INVALIDARG;
+ }
+}
+
+HRESULT WINAPI SetPropertyInteractionContext(HINTERACTIONCONTEXT handle,
+ INTERACTION_CONTEXT_PROPERTY property, UINT32 value)
+{
+ struct interaction_context *context = context_from_handle(handle);
+
+ TRACE("context %p, property %#x, value %#x.\n", context, property, value);
+
+ if (!context)
+ return E_HANDLE;
+
+ switch (property)
+ {
+ case INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS:
+ case INTERACTION_CONTEXT_PROPERTY_INTERACTION_UI_FEEDBACK:
+ FIXME("Unhandled property %#x.\n", property);
+ return E_NOTIMPL;
+
+ case INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS:
+ if (value != FALSE && value != TRUE)
+ return E_INVALIDARG;
+ context->filter_pointers = value;
+ return S_OK;
+
+ default:
+ WARN("Invalid property %#x.\n", property);
+ return E_INVALIDARG;
+ }
+}
+
HRESULT WINAPI ProcessInertiaInteractionContext(HINTERACTIONCONTEXT context)
{
FIXME("context %p: stub!\n", context);
diff --git a/dlls/ninput/ninput.spec b/dlls/ninput/ninput.spec
index fe7c6b3c53b3..166e3d1ec8c1 100644
--- a/dlls/ninput/ninput.spec
+++ b/dlls/ninput/ninput.spec
@@ -7,7 +7,7 @@
@ stub GetInertiaParameterInteractionContext
@ stub GetInteractionConfigurationInteractionContext
@ stub GetMouseWheelParameterInteractionContext
-@ stub GetPropertyInteractionContext
+@ stdcall GetPropertyInteractionContext(ptr long ptr)
@ stub GetStateInteractionContext
@ stub ProcessBufferedPacketsInteractionContext
@ stdcall ProcessInertiaInteractionContext(ptr)
@@ -20,5 +20,5 @@
@ stub SetInteractionConfigurationInteractionContext
@ stub SetMouseWheelParameterInteractionContext
@ stub SetPivotInteractionContext
-@ stub SetPropertyInteractionContext
+@ stdcall SetPropertyInteractionContext(ptr long long)
@ stub StopInteractionContext
diff --git a/dlls/ninput/tests/ninput.c b/dlls/ninput/tests/ninput.c
index d894ab8a847a..156e3e898366 100644
--- a/dlls/ninput/tests/ninput.c
+++ b/dlls/ninput/tests/ninput.c
@@ -35,7 +35,55 @@ static void test_context(void)
ok(hr == E_HANDLE, "Got hr %#x.\n", hr);
}
+static void test_properties(void)
+{
+ HINTERACTIONCONTEXT context;
+ UINT32 value;
+ HRESULT hr;
+
+ hr = CreateInteractionContext(&context);
+ ok(hr == S_OK, "Failed to create context, hr %#x.\n", hr);
+
+ hr = GetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, &value);
+ ok(hr == S_OK, "Failed to get property, hr %#x.\n", hr);
+ ok(value == TRUE, "Got unexpected value %#x.\n", value);
+
+ hr = SetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, TRUE);
+ ok(hr == S_OK, "Failed to set property, hr %#x.\n", hr);
+ hr = GetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, &value);
+ ok(hr == S_OK, "Failed to get property, hr %#x.\n", hr);
+ ok(value == TRUE, "Got unexpected value %#x.\n", value);
+
+ hr = SetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, FALSE);
+ ok(hr == S_OK, "Failed to set property, hr %#x.\n", hr);
+ hr = GetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, &value);
+ ok(hr == S_OK, "Failed to get property, hr %#x.\n", hr);
+ ok(value == FALSE, "Got unexpected value %#x.\n", value);
+
+ hr = SetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, 2);
+ ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+ hr = SetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, 3);
+ ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+
+ hr = SetPropertyInteractionContext(context, 0xdeadbeef, 0);
+ ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+ hr = GetPropertyInteractionContext(context, 0xdeadbeef, &value);
+ ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
+
+ hr = GetPropertyInteractionContext(context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, NULL);
+ ok(hr == E_POINTER, "Got hr %#x.\n", hr);
+
+ hr = SetPropertyInteractionContext(NULL, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, FALSE);
+ ok(hr == E_HANDLE, "Got hr %#x.\n", hr);
+ hr = GetPropertyInteractionContext(NULL, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, &value);
+ ok(hr == E_HANDLE, "Got hr %#x.\n", hr);
+
+ hr = DestroyInteractionContext(context);
+ ok(hr == S_OK, "Failed to destroy context, hr %#x.\n", hr);
+}
+
START_TEST(ninput)
{
test_context();
+ test_properties();
}
--
2.16.4
June 21, 2018
[PATCH 4/7] ninput: Add ProcessInertiaInteractionContext() stub.
by Józef Kucia
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
dlls/ninput/main.c | 6 ++++++
dlls/ninput/ninput.spec | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/ninput/main.c b/dlls/ninput/main.c
index 1aa3c4652b6e..932d159ab390 100644
--- a/dlls/ninput/main.c
+++ b/dlls/ninput/main.c
@@ -72,6 +72,12 @@ HRESULT WINAPI DestroyInteractionContext(HINTERACTIONCONTEXT handle)
return S_OK;
}
+HRESULT WINAPI ProcessInertiaInteractionContext(HINTERACTIONCONTEXT context)
+{
+ FIXME("context %p: stub!\n", context);
+ return E_NOTIMPL;
+}
+
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved)
{
TRACE("(%p, %d, %p)\n", inst, reason, reserved);
diff --git a/dlls/ninput/ninput.spec b/dlls/ninput/ninput.spec
index 0717cb1c2f47..fe7c6b3c53b3 100644
--- a/dlls/ninput/ninput.spec
+++ b/dlls/ninput/ninput.spec
@@ -10,7 +10,7 @@
@ stub GetPropertyInteractionContext
@ stub GetStateInteractionContext
@ stub ProcessBufferedPacketsInteractionContext
-@ stub ProcessInertiaInteractionContext
+@ stdcall ProcessInertiaInteractionContext(ptr)
@ stub ProcessPointerFramesInteractionContext
@ stub RegisterOutputCallbackInteractionContext
@ stub RemovePointerInteractionContext
--
2.16.4
June 21, 2018
[PATCH 3/7] ninput: Implement CreateInteractionContext().
by Józef Kucia
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
For bug 44720
---
configure | 1 +
configure.ac | 1 +
dlls/ninput/Makefile.in | 1 +
dlls/ninput/main.c | 53 ++++++++++++++++++++++++++++++++++++++++---
dlls/ninput/ninput.spec | 4 ++--
dlls/ninput/tests/Makefile.in | 4 ++++
dlls/ninput/tests/ninput.c | 41 +++++++++++++++++++++++++++++++++
7 files changed, 100 insertions(+), 5 deletions(-)
create mode 100644 dlls/ninput/tests/Makefile.in
create mode 100644 dlls/ninput/tests/ninput.c
diff --git a/configure b/configure
index bcb660d56982..26aa115002d9 100755
--- a/configure
+++ b/configure
@@ -19023,6 +19023,7 @@ wine_fn_config_makefile dlls/netprofm enable_netprofm
wine_fn_config_makefile dlls/netprofm/tests enable_tests
wine_fn_config_makefile dlls/newdev enable_newdev
wine_fn_config_makefile dlls/ninput enable_ninput
+wine_fn_config_makefile dlls/ninput/tests enable_tests
wine_fn_config_makefile dlls/normaliz enable_normaliz
wine_fn_config_makefile dlls/npmshtml enable_npmshtml
wine_fn_config_makefile dlls/npptools enable_npptools
diff --git a/configure.ac b/configure.ac
index 469fa8d72977..0f7e130c322e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3522,6 +3522,7 @@ WINE_CONFIG_MAKEFILE(dlls/netprofm)
WINE_CONFIG_MAKEFILE(dlls/netprofm/tests)
WINE_CONFIG_MAKEFILE(dlls/newdev)
WINE_CONFIG_MAKEFILE(dlls/ninput)
+WINE_CONFIG_MAKEFILE(dlls/ninput/tests)
WINE_CONFIG_MAKEFILE(dlls/normaliz)
WINE_CONFIG_MAKEFILE(dlls/npmshtml)
WINE_CONFIG_MAKEFILE(dlls/npptools)
diff --git a/dlls/ninput/Makefile.in b/dlls/ninput/Makefile.in
index 3d3cc7fe85e8..8f1039b73c86 100644
--- a/dlls/ninput/Makefile.in
+++ b/dlls/ninput/Makefile.in
@@ -1,4 +1,5 @@
MODULE = ninput.dll
+IMPORTLIB = ninput
C_SRCS = \
main.c
diff --git a/dlls/ninput/main.c b/dlls/ninput/main.c
index 0f23128cfe4d..1aa3c4652b6e 100644
--- a/dlls/ninput/main.c
+++ b/dlls/ninput/main.c
@@ -22,19 +22,66 @@
#include "windef.h"
#include "winbase.h"
#include "wine/debug.h"
+#include "wine/heap.h"
+
+#include "interactioncontext.h"
WINE_DEFAULT_DEBUG_CHANNEL(ninput);
-BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD reason, LPVOID lpv)
+struct interaction_context
+{
+ BOOL filter_pointers;
+};
+
+static struct interaction_context *context_from_handle(HINTERACTIONCONTEXT handle)
+{
+ return (struct interaction_context *)handle;
+}
+
+HRESULT WINAPI CreateInteractionContext(HINTERACTIONCONTEXT *handle)
+{
+ struct interaction_context *context;
+
+ TRACE("handle %p.\n", handle);
+
+ if (!handle)
+ return E_POINTER;
+
+ if (!(context = heap_alloc(sizeof(*context))))
+ return E_OUTOFMEMORY;
+
+ context->filter_pointers = TRUE;
+
+ TRACE("Created context %p.\n", context);
+
+ *handle = (HINTERACTIONCONTEXT)context;
+
+ return S_OK;
+}
+
+HRESULT WINAPI DestroyInteractionContext(HINTERACTIONCONTEXT handle)
+{
+ struct interaction_context *context = context_from_handle(handle);
+
+ TRACE("context %p.\n", context);
+
+ if (!context)
+ return E_HANDLE;
+
+ heap_free(context);
+ return S_OK;
+}
+
+BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved)
{
- TRACE("(%p, %d, %p)\n", hInstDLL, reason, lpv);
+ TRACE("(%p, %d, %p)\n", inst, reason, reserved);
switch (reason)
{
case DLL_WINE_PREATTACH:
return FALSE; /* prefer native version */
case DLL_PROCESS_ATTACH:
- DisableThreadLibraryCalls(hInstDLL);
+ DisableThreadLibraryCalls(inst);
break;
}
return TRUE;
diff --git a/dlls/ninput/ninput.spec b/dlls/ninput/ninput.spec
index 372beb20eb15..0717cb1c2f47 100644
--- a/dlls/ninput/ninput.spec
+++ b/dlls/ninput/ninput.spec
@@ -1,8 +1,8 @@
@ stub DefaultInputHandler
@ stub AddPointerInteractionContext
@ stub BufferPointerPacketsInteractionContext
-@ stub CreateInteractionContext
-@ stub DestroyInteractionContext
+@ stdcall CreateInteractionContext(ptr)
+@ stdcall DestroyInteractionContext(ptr)
@ stub GetCrossSlideParameterInteractionContext
@ stub GetInertiaParameterInteractionContext
@ stub GetInteractionConfigurationInteractionContext
diff --git a/dlls/ninput/tests/Makefile.in b/dlls/ninput/tests/Makefile.in
new file mode 100644
index 000000000000..9c0b44fa17dc
--- /dev/null
+++ b/dlls/ninput/tests/Makefile.in
@@ -0,0 +1,4 @@
+TESTDLL = ninput.dll
+IMPORTS = ninput
+
+C_SRCS = ninput.c
diff --git a/dlls/ninput/tests/ninput.c b/dlls/ninput/tests/ninput.c
new file mode 100644
index 000000000000..d894ab8a847a
--- /dev/null
+++ b/dlls/ninput/tests/ninput.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 Józef Kucia
+ *
+ * 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 "interactioncontext.h"
+#include "wine/test.h"
+
+static void test_context(void)
+{
+ HINTERACTIONCONTEXT context;
+ HRESULT hr;
+
+ hr = CreateInteractionContext(&context);
+ ok(hr == S_OK, "Failed to create context, hr %#x.\n", hr);
+ hr = DestroyInteractionContext(context);
+ ok(hr == S_OK, "Failed to destroy context, hr %#x.\n", hr);
+
+ hr = CreateInteractionContext(NULL);
+ ok(hr == E_POINTER, "Got hr %#x.\n", hr);
+ hr = DestroyInteractionContext(NULL);
+ ok(hr == E_HANDLE, "Got hr %#x.\n", hr);
+}
+
+START_TEST(ninput)
+{
+ test_context();
+}
--
2.16.4
June 21, 2018
[PATCH 2/7] include: Add interactioncontext.h.
by Józef Kucia
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
include/Makefile.in | 1 +
include/interactioncontext.h | 195 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 196 insertions(+)
create mode 100644 include/interactioncontext.h
diff --git a/include/Makefile.in b/include/Makefile.in
index 78ab095d77e7..72a701090047 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -314,6 +314,7 @@ SOURCES = \
inputscope.idl \
inseng.idl \
inspectable.idl \
+ interactioncontext.h \
intshcut.h \
ipexport.h \
iphlpapi.h \
diff --git a/include/interactioncontext.h b/include/interactioncontext.h
new file mode 100644
index 000000000000..a643b626cd7e
--- /dev/null
+++ b/include/interactioncontext.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2018 Józef Kucia
+ *
+ * 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 INTERACTION_CONTEXT_H
+#define INTERACTION_CONTEXT_H
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+DECLARE_HANDLE(HINTERACTIONCONTEXT);
+
+typedef enum INTERACTION_CONTEXT_PROPERTY
+{
+ INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS = 1,
+ INTERACTION_CONTEXT_PROPERTY_INTERACTION_UI_FEEDBACK = 2,
+ INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS = 3,
+ INTERACTION_CONTEXT_PROPERTY_MAX = 0xffffffff,
+} INTERACTION_CONTEXT_PROPERTY;
+
+typedef enum INTERACTION_ID
+{
+ INTERACTION_ID_NONE = 0,
+ INTERACTION_ID_MANIPULATION = 1,
+ INTERACTION_ID_TAP = 2,
+ INTERACTION_ID_SECONDARY_TAP = 3,
+ INTERACTION_ID_HOLD = 4,
+ INTERACTION_ID_DRAG = 5,
+ INTERACTION_ID_CROSS_SLIDE = 6,
+ INTERACTION_ID_MAX = 0xffffffff,
+} INTERACTION_ID;
+
+typedef enum INTERACTION_FLAGS
+{
+ INTERACTION_FLAG_NONE = 0x00000000,
+ INTERACTION_FLAG_BEGIN = 0x00000001,
+ INTERACTION_FLAG_END = 0x00000002,
+ INTERACTION_FLAG_CANCEL = 0x00000004,
+ INTERACTION_FLAG_INERTIA = 0x00000008,
+ INTERACTION_FLAG_MAX = 0xffffffff,
+} INTERACTION_FLAGS;
+
+typedef enum INTERACTION_CONFIGURATION_FLAGS
+{
+ INTERACTION_CONFIGURATION_FLAG_NONE = 0x00000000,
+
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION = 0x00000001,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_X = 0x00000002,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_Y = 0x00000004,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_ROTATION = 0x00000008,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_SCALING = 0x00000010,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_INERTIA = 0x00000020,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_ROTATION_INERTIA = 0x00000040,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_SCALING_INERTIA = 0x00000080,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_RAILS_X = 0x00000100,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_RAILS_Y = 0x00000200,
+ INTERACTION_CONFIGURATION_FLAG_MANIPULATION_EXACT = 0x00000400,
+
+ INTERACTION_CONFIGURATION_FLAG_TAP = 0x00000001,
+ INTERACTION_CONFIGURATION_FLAG_TAP_DOUBLE = 0x00000002,
+
+ INTERACTION_CONFIGURATION_FLAG_SECONDARY_TAP = 0x00000001,
+
+ INTERACTION_CONFIGURATION_FLAG_HOLD = 0x00000001,
+ INTERACTION_CONFIGURATION_FLAG_HOLD_MOUSE = 0x00000002,
+
+ INTERACTION_CONFIGURATION_FLAG_DRAG = 0x00000001,
+
+ INTERACTION_CONFIGURATION_FLAG_CROSS_SLIDE = 0x00000001,
+ INTERACTION_CONFIGURATION_FLAG_CROSS_SLIDE_HORIZONTAL = 0x00000002,
+ INTERACTION_CONFIGURATION_FLAG_CROSS_SLIDE_SELECT = 0x00000004,
+ INTERACTION_CONFIGURATION_FLAG_CROSS_SLIDE_SPEED_BUMP = 0x00000008,
+ INTERACTION_CONFIGURATION_FLAG_CROSS_SLIDE_REARRANGE = 0x00000010,
+ INTERACTION_CONFIGURATION_FLAG_CROSS_SLIDE_EXACT = 0x00000020,
+
+ INTERACTION_CONFIGURATION_FLAG_MAX = 0xffffffff,
+} INTERACTION_CONFIGURATION_FLAGS;
+
+typedef struct INTERACTION_CONTEXT_CONFIGURATION
+{
+ INTERACTION_ID interactionId;
+ INTERACTION_CONFIGURATION_FLAGS enable;
+
+} INTERACTION_CONTEXT_CONFIGURATION;
+
+typedef struct MANIPULATION_TRANSFORM
+{
+ float translationX;
+ float translationY;
+ float scale;
+ float expansion;
+ float rotation;
+} MANIPULATION_TRANSFORM;
+
+typedef struct MANIPULATION_VELOCITY
+{
+ float velocityX;
+ float velocityY;
+ float velocityExapnsion;
+ float velocityAngular;
+} MANIPULATION_VELOCITY;
+
+typedef enum MANIPULATION_RAILS_STATE
+{
+ MANIPULATION_RAILS_STATE_UNDECIDED = 0,
+ MANIPULATION_RAILS_STATE_FREE = 1,
+ MANIPULATION_RAILS_STATE_RAILED = 2,
+ MANIPULATION_RAILS_STATE_MAX = 0xffffffff,
+} MANIPULATION_RAILS_STATE;
+
+typedef struct INTERACTION_ARGUMENTS_MANIPULATION
+{
+ MANIPULATION_TRANSFORM delta;
+ MANIPULATION_TRANSFORM cumulative;
+ MANIPULATION_VELOCITY veolcity;
+ MANIPULATION_RAILS_STATE railsState;
+} INTERACTION_ARGUMENTS_MANIPULATION;
+
+typedef struct INTERACTION_ARGUMENTS_TAP
+{
+ UINT32 count;
+} INTERACTION_ARGUMENTS_TAP;
+
+typedef enum CROSS_SLIDE_FLAGS
+{
+ CROSS_SLIDE_FLAGS_NONE = 0x00000000,
+ CROSS_SLIDE_FLAGS_SELECT = 0x00000001,
+ CROSS_SLIDE_FLAGS_SPEED_BUMP = 0x00000002,
+ CROSS_SLIDE_FLAGS_REARRANGE = 0x00000004,
+ CROSS_SLIDE_FLAGS_MAX = 0xffffffff,
+} CROSS_SLIDE_FLAGS;
+
+typedef struct INTERACTION_ARGUMENTS_CROSS_SLIDE
+{
+ CROSS_SLIDE_FLAGS flags;
+} INTERACTION_ARGUMENTS_CROSS_SLIDE;
+
+typedef struct INTERACTION_CONTEXT_OUTPUT
+{
+ INTERACTION_ID interactionId;
+ INTERACTION_FLAGS interactionFlags;
+ POINTER_INPUT_TYPE inputType;
+ float x;
+ float y;
+ union
+ {
+ INTERACTION_ARGUMENTS_MANIPULATION manipulation;
+ INTERACTION_ARGUMENTS_TAP tap;
+ INTERACTION_ARGUMENTS_CROSS_SLIDE crossSlide;
+ } arguments;
+} INTERACTION_CONTEXT_OUTPUT;
+
+typedef void (CALLBACK *INTERACTION_CONTEXT_OUTPUT_CALLBACK)(void *data,
+ const INTERACTION_CONTEXT_OUTPUT *output);
+
+HRESULT WINAPI CreateInteractionContext(HINTERACTIONCONTEXT *context);
+HRESULT WINAPI DestroyInteractionContext(HINTERACTIONCONTEXT context);
+
+HRESULT WINAPI ProcessInertiaInteractionContext(HINTERACTIONCONTEXT context);
+
+HRESULT WINAPI RegisterOutputCallbackInteractionContext(HINTERACTIONCONTEXT context,
+ INTERACTION_CONTEXT_OUTPUT_CALLBACK callback, void *data);
+
+HRESULT WINAPI GetInteractionConfigurationInteractionContext(HINTERACTIONCONTEXT context,
+ UINT32 count, INTERACTION_CONTEXT_CONFIGURATION *configuration);
+HRESULT WINAPI SetInteractionConfigurationInteractionContext(HINTERACTIONCONTEXT context,
+ UINT32 count, const INTERACTION_CONTEXT_CONFIGURATION *configuration);
+
+HRESULT WINAPI GetPropertyInteractionContext(HINTERACTIONCONTEXT context,
+ INTERACTION_CONTEXT_PROPERTY proerty, UINT32 *value);
+HRESULT WINAPI SetPropertyInteractionContext(HINTERACTIONCONTEXT context,
+ INTERACTION_CONTEXT_PROPERTY property, UINT32 value);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif
--
2.16.4
June 21, 2018
[PATCH 1/7] include: Add POINTER_INPUT_TYPE.
by Józef Kucia
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
include/winuser.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/include/winuser.h b/include/winuser.h
index f546a4a1f3da..ceaf82dcb528 100644
--- a/include/winuser.h
+++ b/include/winuser.h
@@ -3377,6 +3377,15 @@ typedef struct tagPOINTER_DEVICE_INFO {
WCHAR productString[POINTER_DEVICE_PRODUCT_STRING_MAX];
} POINTER_DEVICE_INFO;
+enum tagPOINTER_INPUT_TYPE
+{
+ PT_POINTER = 1,
+ PT_TOUCH = 2,
+ PT_PEN = 3,
+ PT_MOUSE = 4,
+};
+typedef DWORD POINTER_INPUT_TYPE;
+
#if defined(_WINGDI_) && !defined(NOGDI)
WINUSERAPI LONG WINAPI ChangeDisplaySettingsA(LPDEVMODEA,DWORD);
WINUSERAPI LONG WINAPI ChangeDisplaySettingsW(LPDEVMODEW,DWORD);
--
2.16.4
June 21, 2018
[PATCH] shell32/tests: Don't test CommandLineToArgv() in test_argify().
by Zebediah Figura
This is essentially redundant. We should have tests for all behaviours of
CommandLineToArgv() above; the purpose of test_argify() is to test how
ShellExecute() et al. parses its parameters, and to show that this is
different from CommandLineToArgv().
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/shell32/tests/shlexec.c | 167 +++++++++++++++----------------------------
1 file changed, 59 insertions(+), 108 deletions(-)
diff --git a/dlls/shell32/tests/shlexec.c b/dlls/shell32/tests/shlexec.c
index c67cc46..ab77fa1 100644
--- a/dlls/shell32/tests/shlexec.c
+++ b/dlls/shell32/tests/shlexec.c
@@ -1418,8 +1418,8 @@ typedef struct
const char* verb;
const char* params;
int todo;
- cmdline_tests_t cmd;
- cmdline_tests_t broken;
+ const char *cmd;
+ const char *broken;
} argify_tests_t;
static const argify_tests_t argify_tests[] =
@@ -1429,33 +1429,28 @@ static const argify_tests_t argify_tests[] =
* parameters string, including the trailing spaces, no matter what
* arguments have already been used.
*/
- {"Params232S", "p2 p3 p4 ", 0xc2,
- {" p2 p3 \"p2\" \"p2 p3 p4 \"",
- {"", "p2", "p3", "p2", "p2 p3 p4 ", NULL}, 0}},
+ {"Params232S", "p2 p3 p4 ", TRUE,
+ " p2 p3 \"p2\" \"p2 p3 p4 \""},
/* Unquoted argument references like %2 don't automatically quote their
* argument. Similarly, when they are quoted they don't escape the quotes
* that their argument may contain.
*/
- {"Params232S", "\"p two\" p3 p4 ", 0x3f3,
- {" p two p3 \"p two\" \"\"p two\" p3 p4 \"",
- {"", "p", "two", "p3", "p two", "p", "two p3 p4 ", NULL}, 0}},
+ {"Params232S", "\"p two\" p3 p4 ", TRUE,
+ " p two p3 \"p two\" \"\"p two\" p3 p4 \""},
/* Only single digits are supported so only %1 to %9. Shown here with %20
* because %10 is a pain.
*/
- {"Params20", "p", 0,
- {" \"p0\"",
- {"", "p0", NULL}, 0}},
+ {"Params20", "p", FALSE,
+ " \"p0\""},
/* Only (double-)quotes have a special meaning. */
- {"Params23456", "'p2 p3` p4\\ $even", 0x40,
- {" \"'p2\" \"p3`\" \"p4\\\" \"$even\" \"\"",
- {"", "'p2", "p3`", "p4\" $even \"", NULL}, 0}},
+ {"Params23456", "'p2 p3` p4\\ $even", FALSE,
+ " \"'p2\" \"p3`\" \"p4\\\" \"$even\" \"\""},
- {"Params23456", "p=2 p-3 p4\tp4\rp4\np4", 0x1c2,
- {" \"p=2\" \"p-3\" \"p4\tp4\rp4\np4\" \"\" \"\"",
- {"", "p=2", "p-3", "p4\tp4\rp4\np4", "", "", NULL}, 0}},
+ {"Params23456", "p=2 p-3 p4\tp4\rp4\np4", TRUE,
+ " \"p=2\" \"p-3\" \"p4\tp4\rp4\np4\" \"\" \"\""},
/* In unquoted strings, quotes are treated are a parameter separator just
* like spaces! However they can be doubled to get a literal quote.
@@ -1463,125 +1458,102 @@ static const argify_tests_t argify_tests[] =
* 2n quotes -> n quotes
* 2n+1 quotes -> n quotes and a parameter separator
*/
- {"Params23456789", "one\"quote \"p four\" one\"quote p7", 0xff3,
- {" \"one\" \"quote\" \"p four\" \"one\" \"quote\" \"p7\" \"\" \"\"",
- {"", "one", "quote", "p four", "one", "quote", "p7", "", "", NULL}, 0}},
+ {"Params23456789", "one\"quote \"p four\" one\"quote p7", TRUE,
+ " \"one\" \"quote\" \"p four\" \"one\" \"quote\" \"p7\" \"\" \"\""},
- {"Params23456789", "two\"\"quotes \"p three\" two\"\"quotes p5", 0xf2,
- {" \"two\"quotes\" \"p three\" \"two\"quotes\" \"p5\" \"\" \"\" \"\" \"\"",
- {"", "twoquotes p", "three twoquotes", "p5", "", "", "", "", NULL}, 0}},
+ {"Params23456789", "two\"\"quotes \"p three\" two\"\"quotes p5", TRUE,
+ " \"two\"quotes\" \"p three\" \"two\"quotes\" \"p5\" \"\" \"\" \"\" \"\""},
- {"Params23456789", "three\"\"\"quotes \"p four\" three\"\"\"quotes p6", 0xff3,
- {" \"three\"\" \"quotes\" \"p four\" \"three\"\" \"quotes\" \"p6\" \"\" \"\"",
- {"", "three\"", "quotes", "p four", "three\"", "quotes", "p6", "", "", NULL}, 0}},
+ {"Params23456789", "three\"\"\"quotes \"p four\" three\"\"\"quotes p6", TRUE,
+ " \"three\"\" \"quotes\" \"p four\" \"three\"\" \"quotes\" \"p6\" \"\" \"\""},
- {"Params23456789", "four\"\"\"\"quotes \"p three\" four\"\"\"\"quotes p5", 0xf3,
- {" \"four\"\"quotes\" \"p three\" \"four\"\"quotes\" \"p5\" \"\" \"\" \"\" \"\"",
- {"", "four\"quotes p", "three fourquotes p5 \"", "", "", "", NULL}, 0}},
+ {"Params23456789", "four\"\"\"\"quotes \"p three\" four\"\"\"\"quotes p5", TRUE,
+ " \"four\"\"quotes\" \"p three\" \"four\"\"quotes\" \"p5\" \"\" \"\" \"\" \"\""},
/* Quoted strings cannot be continued by tacking on a non space character
* either.
*/
- {"Params23456", "\"p two\"p3 \"p four\"p5 p6", 0x1f3,
- {" \"p two\" \"p3\" \"p four\" \"p5\" \"p6\"",
- {"", "p two", "p3", "p four", "p5", "p6", NULL}, 0}},
+ {"Params23456", "\"p two\"p3 \"p four\"p5 p6", TRUE,
+ " \"p two\" \"p3\" \"p four\" \"p5\" \"p6\""},
/* In quoted strings, the quotes are halved and an odd number closes the
* string. Specifically:
* 2n quotes -> n quotes
* 2n+1 quotes -> n quotes and closes the string and hence the parameter
*/
- {"Params23456789", "\"one q\"uote \"p four\" \"one q\"uote p7", 0xff3,
- {" \"one q\" \"uote\" \"p four\" \"one q\" \"uote\" \"p7\" \"\" \"\"",
- {"", "one q", "uote", "p four", "one q", "uote", "p7", "", "", NULL}, 0}},
+ {"Params23456789", "\"one q\"uote \"p four\" \"one q\"uote p7", TRUE,
+ " \"one q\" \"uote\" \"p four\" \"one q\" \"uote\" \"p7\" \"\" \"\""},
- {"Params23456789", "\"two \"\" quotes\" \"p three\" \"two \"\" quotes\" p5", 0x1ff3,
- {" \"two \" quotes\" \"p three\" \"two \" quotes\" \"p5\" \"\" \"\" \"\" \"\"",
- {"", "two ", "quotes p", "three two", " quotes", "p5", "", "", "", "", NULL}, 0}},
+ {"Params23456789", "\"two \"\" quotes\" \"p three\" \"two \"\" quotes\" p5", TRUE,
+ " \"two \" quotes\" \"p three\" \"two \" quotes\" \"p5\" \"\" \"\" \"\" \"\""},
- {"Params23456789", "\"three q\"\"\"uotes \"p four\" \"three q\"\"\"uotes p7", 0xff3,
- {" \"three q\"\" \"uotes\" \"p four\" \"three q\"\" \"uotes\" \"p7\" \"\" \"\"",
- {"", "three q\"", "uotes", "p four", "three q\"", "uotes", "p7", "", "", NULL}, 0}},
+ {"Params23456789", "\"three q\"\"\"uotes \"p four\" \"three q\"\"\"uotes p7", TRUE,
+ " \"three q\"\" \"uotes\" \"p four\" \"three q\"\" \"uotes\" \"p7\" \"\" \"\""},
- {"Params23456789", "\"four \"\"\"\" quotes\" \"p three\" \"four \"\"\"\" quotes\" p5", 0xff3,
- {" \"four \"\" quotes\" \"p three\" \"four \"\" quotes\" \"p5\" \"\" \"\" \"\" \"\"",
- {"", "four \"", "quotes p", "three four", "", "quotes p5 \"", "", "", "", NULL}, 0}},
+ {"Params23456789", "\"four \"\"\"\" quotes\" \"p three\" \"four \"\"\"\" quotes\" p5", TRUE,
+ " \"four \"\" quotes\" \"p three\" \"four \"\" quotes\" \"p5\" \"\" \"\" \"\" \"\""},
/* The quoted string rules also apply to consecutive quotes at the start
* of a parameter but don't count the opening quote!
*/
- {"Params23456789", "\"\"twoquotes \"p four\" \"\"twoquotes p7", 0xbf3,
- {" \"\" \"twoquotes\" \"p four\" \"\" \"twoquotes\" \"p7\" \"\" \"\"",
- {"", "", "twoquotes", "p four", "", "twoquotes", "p7", "", "", NULL}, 0}},
+ {"Params23456789", "\"\"twoquotes \"p four\" \"\"twoquotes p7", TRUE,
+ " \"\" \"twoquotes\" \"p four\" \"\" \"twoquotes\" \"p7\" \"\" \"\""},
- {"Params23456789", "\"\"\"three quotes\" \"p three\" \"\"\"three quotes\" p5", 0x6f3,
- {" \"\"three quotes\" \"p three\" \"\"three quotes\" \"p5\" \"\" \"\" \"\" \"\"",
- {"", "three", "quotes p", "three \"three", "quotes p5 \"", "", "", "", NULL}, 0}},
+ {"Params23456789", "\"\"\"three quotes\" \"p three\" \"\"\"three quotes\" p5", TRUE,
+ " \"\"three quotes\" \"p three\" \"\"three quotes\" \"p5\" \"\" \"\" \"\" \"\""},
- {"Params23456789", "\"\"\"\"fourquotes \"p four\" \"\"\"\"fourquotes p7", 0xbf3,
- {" \"\"\" \"fourquotes\" \"p four\" \"\"\" \"fourquotes\" \"p7\" \"\" \"\"",
- {"", "\"", "fourquotes", "p four", "\"", "fourquotes", "p7", "", "", NULL}, 0}},
+ {"Params23456789", "\"\"\"\"fourquotes \"p four\" \"\"\"\"fourquotes p7", TRUE,
+ " \"\"\" \"fourquotes\" \"p four\" \"\"\" \"fourquotes\" \"p7\" \"\" \"\""},
/* An unclosed quoted string gets lost! */
- {"Params23456", "p2 \"p3\" \"p4 is lost", 0x1c3,
- {" \"p2\" \"p3\" \"\" \"\" \"\"",
- {"", "p2", "p3", "", "", "", NULL}, 0},
- {" \"p2\" \"p3\" \"p3\" \"\" \"\"",
- {"", "p2", "p3", "p3", "", "", NULL}, 0}},
+ {"Params23456", "p2 \"p3\" \"p4 is lost", TRUE,
+ " \"p2\" \"p3\" \"\" \"\" \"\"",
+ " \"p2\" \"p3\" \"p3\" \"\" \"\""}, /* NT4/2k */
/* Backslashes have no special meaning even when preceding quotes. All
* they do is start an unquoted string.
*/
- {"Params23456", "\\\"p\\three \"pfour\\\" pfive", 0x73,
- {" \"\\\" \"p\\three\" \"pfour\\\" \"pfive\" \"\"",
- {"", "\" p\\three pfour\"", "pfive", "", NULL}, 0}},
+ {"Params23456", "\\\"p\\three \"pfour\\\" pfive", TRUE,
+ " \"\\\" \"p\\three\" \"pfour\\\" \"pfive\" \"\""},
/* Environment variables are left untouched. */
- {"Params23456", "%TMPDIR% %t %c", 0,
- {" \"%TMPDIR%\" \"%t\" \"%c\" \"\" \"\"",
- {"", "%TMPDIR%", "%t", "%c", "", "", NULL}, 0}},
+ {"Params23456", "%TMPDIR% %t %c", FALSE,
+ " \"%TMPDIR%\" \"%t\" \"%c\" \"\" \"\""},
/* %~2 is equivalent to %*. However %~3 and higher include the spaces
* before the parameter!
* (but not the previous parameter's closing quote fortunately)
*/
- {"Params2345Etc", "p2 p3 \"p4\" p5 p6 ", 0x3f3,
- {" ~2=\"p2 p3 \"p4\" p5 p6 \" ~3=\" p3 \"p4\" p5 p6 \" ~4=\" \"p4\" p5 p6 \" ~5= p5 p6 ",
- {"", "~2=p2 p3 p4 p5 p6 ", "~3= p3 p4 p5 p6 ", "~4= p4 p5 p6 ", "~5=", "p5", "p6", NULL}, 0}},
+ {"Params2345Etc", "p2 p3 \"p4\" p5 p6 ", TRUE,
+ " ~2=\"p2 p3 \"p4\" p5 p6 \" ~3=\" p3 \"p4\" p5 p6 \" ~4=\" \"p4\" p5 p6 \" ~5= p5 p6 "},
/* %~n works even if there is no nth parameter. */
- {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 ", 0x12,
- {" ~9=\" \"",
- {"", "~9= ", NULL}, 0}},
+ {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 ", TRUE,
+ " ~9=\" \""},
- {"Params9Etc", "p2 p3 p4 p5 p6 p7 ", 0x12,
- {" ~9=\"\"",
- {"", "~9=", NULL}, 0}},
+ {"Params9Etc", "p2 p3 p4 p5 p6 p7 ", TRUE,
+ " ~9=\"\""},
/* The %~n directives also transmit the tenth parameter and beyond. */
- {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 and beyond!", 0x12,
- {" ~9=\" p9 p10 p11 and beyond!\"",
- {"", "~9= p9 p10 p11 and beyond!", NULL}, 0}},
+ {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 and beyond!", TRUE,
+ " ~9=\" p9 p10 p11 and beyond!\""},
/* Bad formatting directives lose their % sign, except those followed by
* a tilde! Environment variables are not expanded but lose their % sign.
*/
- {"ParamsBad", "p2 p3 p4 p5", 0x12,
- {" \"% - %~ %~0 %~1 %~a %~* a b c TMPDIR\"",
- {"", "% - %~ %~0 %~1 %~a %~* a b c TMPDIR", NULL}, 0}},
+ {"ParamsBad", "p2 p3 p4 p5", TRUE,
+ " \"% - %~ %~0 %~1 %~a %~* a b c TMPDIR\""},
- {NULL, NULL, 0, {NULL, {NULL}, 0}}
+ {0}
};
static void test_argify(void)
{
- BOOL has_cl2a = TRUE;
char fileA[MAX_PATH], params[2*MAX_PATH+12];
INT_PTR rc;
const argify_tests_t* test;
- const cmdline_tests_t *bad;
+ const char *bad;
const char* cmd;
- unsigned i, count;
/* Test with a long parameter */
for (rc = 0; rc < MAX_PATH; rc++)
@@ -1614,21 +1586,11 @@ static void test_argify(void)
test = argify_tests;
while (test->params)
{
- bad = test->broken.cmd ? &test->broken : &test->cmd;
+ bad = test->broken ? test->broken : test->cmd;
- /* trace("***** verb='%s' params='%s'\n", test->verb, test->params); */
rc = shell_execute_ex(SEE_MASK_DOENVSUBST, test->verb, fileA, test->params, NULL, NULL);
okShell(rc > 32, "failed: rc=%lu\n", rc);
- count = 0;
- while (test->cmd.args[count])
- count++;
- /* +4 for the shlexec arguments, -1 because of the added ""
- * argument for the CommandLineToArgvW() tests.
- */
- todo_wine_if(test->todo & 0x1)
- okChildInt("argcA", 4 + count - 1);
-
cmd = getChildString("Child", "cmdlineA");
/* Our commands are such that the verb immediately precedes the
* part we are interested in.
@@ -1636,20 +1598,9 @@ static void test_argify(void)
if (cmd) cmd = strstr(cmd, test->verb);
if (cmd) cmd += strlen(test->verb);
if (!cmd) cmd = "(null)";
- todo_wine_if(test->todo & 0x2)
- okShell(!strcmp(cmd, test->cmd.cmd) || broken(!strcmp(cmd, bad->cmd)),
- "the cmdline is '%s' instead of '%s'\n", cmd, test->cmd.cmd);
-
- for (i = 0; i < count - 1; i++)
- {
- char argname[18];
- sprintf(argname, "argvA%d", 4 + i);
- todo_wine_if(test->todo & (1 << (i+4)))
- okChildStringBroken(argname, test->cmd.args[i+1], bad->args[i+1]);
- }
-
- if (has_cl2a)
- has_cl2a = test_one_cmdline(&(test->cmd));
+ todo_wine_if(test->todo)
+ okShell(!strcmp(cmd, test->cmd) || broken(!strcmp(cmd, bad)),
+ "expected '%s', got '%s'\n", cmd, test->cmd);
test++;
}
}
--
2.7.4
June 21, 2018
[PATCH] gdi32/tests: Test writing to the default palette.
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
Mostly to rule out one proposed reason for the ddraw test failures on Windows.
dlls/gdi32/tests/palette.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/dlls/gdi32/tests/palette.c b/dlls/gdi32/tests/palette.c
index 045f530..0043f55 100644
--- a/dlls/gdi32/tests/palette.c
+++ b/dlls/gdi32/tests/palette.c
@@ -142,6 +142,15 @@ static void test_palette_entries(void)
ok(res == 1, "GetPaletteEntries should have returned 1 but returned %d\n", res);
ok( palEntry.peFlags == getEntryResult.peFlags, "palEntry.peFlags (%#x) != getEntryResult.peFlags (%#x)\n", palEntry.peFlags, getEntryResult.peFlags );
+
+ /* Try setting the system palette */
+ hpal = GetStockObject(DEFAULT_PALETTE);
+ res = SetPaletteEntries(hpal, 0, 1, &palEntry);
+ ok(!res, "SetPaletteEntries() should have failed\n");
+
+ res = GetPaletteEntries(hpal, 0, 1, &getEntryResult);
+ ok(res == 1, "GetPaletteEntries should have returned 1 but returned %d\n", res);
+ ok(memcmp(&palEntry, &getEntryResult, sizeof(PALETTEENTRY)), "entries should not match\n");
}
static void test_halftone_palette(void)
--
2.7.4
June 21, 2018
[PATCH] po: Update Finnish translation.
by Lauri Kenttä
Signed-off-by: Lauri Kenttä <lauri.kentta(a)gmail.com>
---
po/fi.po | 2 ++
1 file changed, 2 insertions(+)
diff --git a/po/fi.po b/po/fi.po
index 7e8bb62be9..abe62894f7 100644
--- a/po/fi.po
+++ b/po/fi.po
@@ -14743,6 +14743,8 @@ msgid ""
"A program on your system has crashed, but WineDbg was unable to attach to "
"the process to obtain a backtrace."
msgstr ""
+"Ohjelma koneellasi kaatui, mutta WineDbg ei pystynyt liittymään siihen eikä "
+"keräämään virheenjäljitystietoja (backtrace)."
#: winedbg.rc:41
msgid "(unidentified)"
--
2.17.1
June 21, 2018
[PATCH 4/4] testbot/web: Allow sumitting jobs to verify patches compile on Wine.
by Francois Gouget
For now the Wine VMs are unchecked by default since all they do is a
compilation check which is mostly redundant with the regular build step.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
testbot/web/Submit.pl | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/testbot/web/Submit.pl b/testbot/web/Submit.pl
index afebc722b..9fa252a11 100644
--- a/testbot/web/Submit.pl
+++ b/testbot/web/Submit.pl
@@ -244,10 +244,14 @@ sub GenerateFields($)
{
$VMs->AddFilter("Type", ["win64"]);
}
- else
+ elsif ($self->{FileType} eq "exe32")
{
$VMs->AddFilter("Type", ["win32", "win64"]);
}
+ else
+ {
+ $VMs->AddFilter("Type", ["win32", "win64", "wine"]);
+ }
if ($self->{ShowAll})
{
# All but the retired and deleted ones
@@ -272,6 +276,10 @@ sub GenerateFields($)
$Status = " [". $VM->Status ."]";
$Checked = undef;
}
+ elsif ($VM->Type eq "wine")
+ {
+ $Checked = undef;
+ }
if ($Checked and
($self->GetParam("Page") == 1 || $self->GetParam($FieldName)))
{
@@ -844,6 +852,39 @@ sub OnSubmit($)
}
}
+ if ($FileType eq "patchdlls")
+ {
+ my $Tasks;
+ my $VMs = CreateVMs();
+ $VMs->AddFilter("Type", ["wine"]);
+ my $SortedKeys = $VMs->SortKeysBySortOrder($VMs->GetKeys());
+ foreach my $VMKey (@$SortedKeys)
+ {
+ my $VM = $VMs->GetItem($VMKey);
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ next if (!$self->GetParam($FieldName)); # skip unselected VMs
+
+ if (!$Tasks)
+ {
+ # First create the Wine test step
+ my $WineStep = $Steps->Add();
+ $WineStep->FileName($BaseName);
+ $WineStep->FileType($FileType);
+ $WineStep->InStaging(!1);
+ $WineStep->Type("build");
+ $WineStep->DebugLevel($self->GetParam("DebugLevel"));
+ $WineStep->ReportSuccessfulTests(defined($self->GetParam("ReportSuccessfulTests")));
+ $Tasks = $WineStep->Tasks;
+ }
+
+ # Then add a task for this VM
+ my $Task = $Tasks->Add();
+ $Task->VM($VM);
+ $Task->CmdLineArg("win32");
+ $Task->Timeout($WineReconfigTimeout);
+ }
+ }
+
# Now save it all (or whatever's left to save)
my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
if (defined($ErrMessage))
--
2.17.1
June 21, 2018
[PATCH 3/4] testbot: Make sure the patches apply and compile in Wine.
by Francois Gouget
This adds the base infrastructure for later testing the patches on Wine.
This adds a "wine" VM type which applies to all VMs that can compile and
run Wine, regardless of the underlying operating system. Adapting to the
underlying operating system is thus the responsibility of the VM-side
scripts.
This also updates GetPatchImpacts() to detect non-Wine scripts (such as
TestBot patches) so the TestBot does not report them as bad patches
for failing to apply on top of Wine.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
Note: This requires updating the database with update37.sql, and
restarting the TestBot engine and web server.
testbot/bin/CheckForWinetestUpdate.pl | 47 ++-
testbot/bin/WineRunReconfig.pl | 36 +-
testbot/bin/WineRunWineTest.pl | 423 ++++++++++++++++++++
testbot/bin/build/WineReconfig.pl | 258 ++++++++++++
testbot/bin/build/WineTest.pl | 282 +++++++++++++
testbot/ddl/update37.sql | 4 +
testbot/ddl/winetestbot.sql | 2 +-
testbot/lib/WineTestBot/Config.pm | 9 +-
testbot/lib/WineTestBot/Engine/Scheduler.pm | 3 +-
testbot/lib/WineTestBot/PatchUtils.pm | 80 +++-
testbot/lib/WineTestBot/Patches.pm | 213 +++++-----
testbot/lib/WineTestBot/PendingPatchSets.pm | 14 +-
testbot/lib/WineTestBot/StepsTasks.pm | 2 +-
testbot/lib/WineTestBot/Tasks.pm | 5 +-
testbot/lib/WineTestBot/VMs.pm | 8 +-
15 files changed, 1234 insertions(+), 152 deletions(-)
create mode 100755 testbot/bin/WineRunWineTest.pl
create mode 100755 testbot/bin/build/WineReconfig.pl
create mode 100755 testbot/bin/build/WineTest.pl
create mode 100644 testbot/ddl/update37.sql
diff --git a/testbot/bin/CheckForWinetestUpdate.pl b/testbot/bin/CheckForWinetestUpdate.pl
index 80c57b9e1..74c59955f 100755
--- a/testbot/bin/CheckForWinetestUpdate.pl
+++ b/testbot/bin/CheckForWinetestUpdate.pl
@@ -63,7 +63,8 @@ my %WineTestUrls = (
64 => "http://test.winehq.org/builds/winetest64-latest.exe"
);
-my %TaskTypes = (build => 1, base32 => 1, winetest32 => 1, all64 => 1);
+my %TaskTypes = (build => 1, base32 => 1, winetest32 => 1, all64 => 1,
+ wine => 1);
my $Debug;
@@ -212,7 +213,7 @@ sub AddJob($$$)
my $Tasks = $NewStep->Tasks;
foreach my $VMKey (@{$VMs->SortKeysBySortOrder($VMs->GetKeys())})
{
- Debug(" $VMKey\n");
+ Debug(" $VMKey exe$Bits\n");
my $Task = $Tasks->Add();
$Task->VM($VMs->GetItem($VMKey));
$Task->Timeout($SuiteTimeout);
@@ -246,11 +247,23 @@ sub AddJob($$$)
return 1;
}
-sub AddReconfigJob()
+sub AddReconfigJob($)
{
- my $Remarks = "Update Wine to latest git";
+ my ($VMType) = @_;
+
+ my $Remarks = "Update the $VMType VMs";
Debug("Creating the '$Remarks' job\n");
+ my $VMs = CreateVMs();
+ $VMs->AddFilter("Type", [$VMType]);
+ $VMs->FilterEnabledRole();
+ if ($VMs->GetItemsCount() == 0)
+ {
+ # There is nothing to do
+ Debug(" Found no VM\n");
+ return 1;
+ }
+
# First create a new job
my $Jobs = CreateJobs();
my $NewJob = $Jobs->Add();
@@ -265,15 +278,16 @@ sub AddReconfigJob()
$NewStep->FileType("none");
$NewStep->InStaging(!1);
- # Add a task for the build VM
- my $VMs = CreateVMs();
- $VMs->AddFilter("Type", ["build"]);
- $VMs->AddFilter("Role", ["base"]);
- my $BuildVM = ${$VMs->GetItems()}[0];
- Debug(" ", $BuildVM->GetKey(), "\n");
- my $Task = $NewStep->Tasks->Add();
- $Task->VM($BuildVM);
- $Task->Timeout($ReconfigTimeout);
+ # And a task for each VM
+ my $SortedKeys = $VMs->SortKeysBySortOrder($VMs->GetKeys());
+ foreach my $VMKey (@$SortedKeys)
+ {
+ my $VM = $VMs->GetItem($VMKey);
+ Debug(" $VMKey $VMType reconfig\n");
+ my $Task = $NewStep->Tasks->Add();
+ $Task->VM($VM);
+ $Task->Timeout($ReconfigTimeout);
+ }
# Save it all
my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
@@ -358,7 +372,8 @@ if (defined $Usage)
#
my $Rc = 0;
-if ($OptTypes{build} or $OptTypes{base32} or $OptTypes{winetest32})
+if ($OptTypes{build} or $OptTypes{base32} or $OptTypes{winetest32} or
+ $OptTypes{wine})
{
my ($Create, $LatestBaseName) = UpdateWineTest($OptCreate, 32);
if ($Create < 0)
@@ -370,9 +385,11 @@ if ($OptTypes{build} or $OptTypes{base32} or $OptTypes{winetest32})
# A new executable means there have been commits so update Wine. Create
# this job first purely to make the WineTestBot job queue look nice, and
# arbitrarily do it only for 32-bit executables to avoid redundant updates.
- $Rc = 1 if ($OptTypes{build} and !AddReconfigJob());
+ $Rc = 1 if ($OptTypes{build} and !AddReconfigJob("build"));
$Rc = 1 if ($OptTypes{base32} and !AddJob("base", $LatestBaseName, 32));
$Rc = 1 if ($OptTypes{winetest32} and !AddJob("", $LatestBaseName, 32));
+
+ $Rc = 1 if ($OptTypes{wine} and !AddReconfigJob("wine"));
}
}
diff --git a/testbot/bin/WineRunReconfig.pl b/testbot/bin/WineRunReconfig.pl
index 23b038a1f..e418e5bb9 100755
--- a/testbot/bin/WineRunReconfig.pl
+++ b/testbot/bin/WineRunReconfig.pl
@@ -5,7 +5,7 @@
# See the bin/build/Reconfig.pl script.
#
# Copyright 2009 Ge van Geldorp
-# Copyright 2013-2016 Francois Gouget
+# Copyright 2013-2018 Francois Gouget
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -312,9 +312,9 @@ sub FatalTAError($$)
# Check the VM and Step
#
-if ($VM->Type ne "build")
+if ($VM->Type ne "build" and $VM->Type ne "wine")
{
- FatalError("This is not a build VM! (" . $VM->Type . ")\n");
+ FatalError("This is neither a build nor a Wine VM! (" . $VM->Type . ")\n");
}
elsif (!$Debug and $VM->Status ne "running")
{
@@ -337,11 +337,12 @@ if ($Step->FileType ne "none")
# Use our own log so it can be used for reference
# even after another task has run.
-my $Script = "#!/bin/sh\n".
- "( set -x\n".
- " git pull &&\n".
- " ../bin/build/Reconfig.pl\n".
- ") >Reconfig.log 2>&1\n";
+my $Script = $VM->Type eq "wine" ? "WineReconfig.pl" : "Reconfig.pl";
+$Script = "#!/bin/sh\n".
+ "( set -x\n".
+ " git pull &&\n".
+ " ../bin/build/$Script\n".
+ ") >Reconfig.log 2>&1\n";
my $TA = $VM->GetAgent();
Debug(Elapsed($Start), " Sending the script: [$Script]\n");
if (!$TA->SendFileFromString($Script, "task", $TestAgent::SENDFILE_EXE))
@@ -410,16 +411,19 @@ elsif (!defined $TAError)
if ($NewStatus eq "completed")
{
use File::Copy;
- for my $Bitness ("32", "64")
+ if ($VM->Type eq "build")
{
- Debug(Elapsed($Start), " Retrieving the $Bitness bit TestLauncher to '$TaskDir/TestLauncher$Bitness.exe'\n");
- if ($TA->GetFile("../src/TestLauncher/TestLauncher$Bitness.exe", "$TaskDir/TestLauncher$Bitness.exe"))
+ for my $Bitness ("32", "64")
{
- copy "$TaskDir/TestLauncher$Bitness.exe", "$DataDir/latest/TestLauncher$Bitness.exe";
- }
- elsif (!defined $TAError)
- {
- $TAError = "An error occurred while retrieving the $Bitness bit TestLauncher: ". $TA->GetLastError();
+ Debug(Elapsed($Start), " Retrieving the $Bitness bit TestLauncher to '$TaskDir/TestLauncher$Bitness.exe'\n");
+ if ($TA->GetFile("../src/TestLauncher/TestLauncher$Bitness.exe", "$TaskDir/TestLauncher$Bitness.exe"))
+ {
+ copy "$TaskDir/TestLauncher$Bitness.exe", "$DataDir/latest/TestLauncher$Bitness.exe";
+ }
+ elsif (!defined $TAError)
+ {
+ $TAError = "An error occurred while retrieving the $Bitness bit TestLauncher: ". $TA->GetLastError();
+ }
}
}
diff --git a/testbot/bin/WineRunWineTest.pl b/testbot/bin/WineRunWineTest.pl
new file mode 100755
index 000000000..0aba7ecfc
--- /dev/null
+++ b/testbot/bin/WineRunWineTest.pl
@@ -0,0 +1,423 @@
+#!/usr/bin/perl -Tw
+# -*- Mode: Perl; perl-indent-level: 2; indent-tabs-mode: nil -*-
+#
+# Makes sure the Wine patches compile.
+# See the bin/build/WineTest.pl script.
+#
+# Copyright 2018 Francois Gouget
+#
+# 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
+
+use strict;
+
+sub BEGIN
+{
+ if ($0 !~ m=^/=)
+ {
+ # Turn $0 into an absolute path so it can safely be used in @INC
+ require Cwd;
+ $0 = Cwd::cwd() . "/$0";
+ }
+ if ($0 =~ m=^(/.*)/[^/]+/[^/]+$=)
+ {
+ $::RootDir = $1;
+ unshift @INC, "$::RootDir/lib";
+ }
+}
+my $Name0 = $0;
+$Name0 =~ s+^.*/++;
+
+
+use WineTestBot::Config;
+use WineTestBot::Jobs;
+use WineTestBot::PatchUtils;
+use WineTestBot::VMs;
+use WineTestBot::Log;
+use WineTestBot::LogUtils;
+use WineTestBot::Engine::Notify;
+
+
+#
+# Logging and error handling helpers
+#
+
+my $Debug;
+sub Debug(@)
+{
+ print STDERR @_ if ($Debug);
+}
+
+my $LogOnly;
+sub Error(@)
+{
+ print STDERR "$Name0:error: ", @_ if (!$LogOnly);
+ LogMsg @_;
+}
+
+
+#
+# Setup and command line processing
+#
+
+$ENV{PATH} = "/usr/bin:/bin";
+delete $ENV{ENV};
+
+my $Usage;
+sub ValidateNumber($$)
+{
+ my ($Name, $Value) = @_;
+
+ # Validate and untaint the value
+ return $1 if ($Value =~ /^(\d+)$/);
+ Error "$Value is not a valid $Name\n";
+ $Usage = 2;
+ return undef;
+}
+
+my ($JobId, $StepNo, $TaskNo);
+while (@ARGV)
+{
+ my $Arg = shift @ARGV;
+ if ($Arg eq "--debug")
+ {
+ $Debug = 1;
+ }
+ elsif ($Arg eq "--log-only")
+ {
+ $LogOnly = 1;
+ }
+ elsif ($Arg =~ /^(?:-\?|-h|--help)$/)
+ {
+ $Usage = 0;
+ last;
+ }
+ elsif ($Arg =~ /^-/)
+ {
+ Error "unknown option '$Arg'\n";
+ $Usage = 2;
+ last;
+ }
+ elsif (!defined $JobId)
+ {
+ $JobId = ValidateNumber('job id', $Arg);
+ }
+ elsif (!defined $StepNo)
+ {
+ $StepNo = ValidateNumber('step number', $Arg);
+ }
+ elsif (!defined $TaskNo)
+ {
+ $TaskNo = ValidateNumber('task number', $Arg);
+ }
+ else
+ {
+ Error "unexpected argument '$Arg'\n";
+ $Usage = 2;
+ last;
+ }
+}
+
+# Check parameters
+if (!defined $Usage)
+{
+ if (!defined $JobId || !defined $StepNo || !defined $TaskNo)
+ {
+ Error "you must specify the job id, step number and task number\n";
+ $Usage = 2;
+ }
+}
+if (defined $Usage)
+{
+ print "Usage: $Name0 [--debug] [--log-only] [--help] JobId StepNo TaskNo\n";
+ exit $Usage;
+}
+
+my $Job = CreateJobs()->GetItem($JobId);
+if (!defined $Job)
+{
+ Error "Job $JobId does not exist\n";
+ exit 1;
+}
+my $Step = $Job->Steps->GetItem($StepNo);
+if (!defined $Step)
+{
+ Error "Step $StepNo of job $JobId does not exist\n";
+ exit 1;
+}
+my $Task = $Step->Tasks->GetItem($TaskNo);
+if (!defined $Task)
+{
+ Error "Step $StepNo task $TaskNo of job $JobId does not exist\n";
+ exit 1;
+}
+my $TaskDir = $Task->CreateDir();
+my $VM = $Task->VM;
+
+
+my $Start = Time();
+LogMsg "Task $JobId/$StepNo/$TaskNo started\n";
+
+
+#
+# Error handling helpers
+#
+
+sub LogTaskError($)
+{
+ my ($ErrMessage) = @_;
+ Debug("$Name0:error: ", $ErrMessage);
+
+ if (open(my $ErrFile, ">>", "$TaskDir/err"))
+ {
+ print $ErrFile $ErrMessage;
+ close($ErrFile);
+ }
+ else
+ {
+ Error "Unable to open 'err' for writing: $!\n";
+ }
+}
+
+sub WrapUpAndExit($;$$)
+{
+ my ($Status, $Retry, $Timeout) = @_;
+ my $NewVMStatus = $Status eq 'queued' ? 'offline' : 'dirty';
+ my $VMResult = $Status eq "boterror" ? "boterror" :
+ $Status eq "queued" ? "error" :
+ $Timeout ? "timeout" : "";
+
+ my $TestFailures;
+ my $Tries = $Task->TestFailures || 0;
+ if ($Retry)
+ {
+ # This may be a transient error (e.g. a network glitch)
+ # so retry a few times to improve robustness
+ $Tries++;
+ if ($Task->CanRetry())
+ {
+ $Status = 'queued';
+ $TestFailures = $Tries;
+ }
+ else
+ {
+ LogTaskError("Giving up after $Tries run(s)\n");
+ }
+ }
+ elsif ($Tries >= 1)
+ {
+ LogTaskError("The previous $Tries run(s) terminated abnormally\n");
+ }
+
+ # Record result details that may be lost or overwritten by a later run
+ if ($VMResult)
+ {
+ $VMResult .= " $Tries $MaxTaskTries" if ($Retry);
+ $VM->RecordResult(undef, $VMResult);
+ }
+
+ # Update the Task and Job
+ $Task->Status($Status);
+ $Task->TestFailures($TestFailures);
+ if ($Status eq 'queued')
+ {
+ $Task->Started(undef);
+ $Task->Ended(undef);
+ # Leave the Task files around so they can be seen until the next run
+ }
+ else
+ {
+ $Task->Ended(time());
+ }
+ $Task->Save();
+ $Job->UpdateStatus();
+
+ # Get the up-to-date VM status and update it if nobody else changed it
+ $VM = CreateVMs()->GetItem($VM->GetKey());
+ if ($VM->Status eq 'running')
+ {
+ $VM->Status($NewVMStatus);
+ $VM->ChildDeadline(undef);
+ $VM->ChildPid(undef);
+ $VM->Save();
+ }
+
+ my $Result = $VM->Name .": ". $VM->Status ." Status: $Status Failures: ". (defined $TestFailures ? $TestFailures : "unset");
+ LogMsg "Task $JobId/$StepNo/$TaskNo done ($Result)\n";
+ Debug(Elapsed($Start), " Done. $Result\n");
+ exit($Status eq 'completed' ? 0 : 1);
+}
+
+sub FatalError($;$)
+{
+ my ($ErrMessage, $Retry) = @_;
+
+ LogMsg "$JobId/$StepNo/$TaskNo $ErrMessage";
+ LogTaskError($ErrMessage);
+
+ WrapUpAndExit('boterror', $Retry);
+}
+
+sub FatalTAError($$)
+{
+ my ($TA, $ErrMessage) = @_;
+ $ErrMessage .= ": ". $TA->GetLastError() if (defined $TA);
+
+ # A TestAgent operation failed, see if the VM is still accessible
+ my $IsPoweredOn = $VM->GetDomain()->IsPoweredOn();
+ if (!defined $IsPoweredOn)
+ {
+ # The VM host is not accessible anymore so mark the VM as offline and
+ # requeue the task. This does not count towards the task's tries limit
+ # since neither the VM nor the task are at fault.
+ Error("$ErrMessage\n");
+ WrapUpAndExit('queued');
+ }
+
+ my $Retry;
+ if ($IsPoweredOn)
+ {
+ LogMsg("$ErrMessage\n");
+ LogTaskError("$ErrMessage\n");
+ $ErrMessage = "The test VM has crashed, rebooted or lost connectivity (or the TestAgent server died)\n";
+ # Retry in case it was a temporary network glitch
+ $Retry = 1;
+ }
+ else
+ {
+ # Ignore the TestAgent error, it's irrelevant
+ $ErrMessage = "The test VM is powered off!\n";
+ }
+ FatalError($ErrMessage, $Retry);
+}
+
+
+#
+# Check the VM and Step
+#
+
+if ($VM->Type ne "wine")
+{
+ FatalError("This is not a Wine VM! (" . $VM->Type . ")\n");
+}
+elsif (!$Debug and $VM->Status ne "running")
+{
+ FatalError("The VM is not ready for use (" . $VM->Status . ")\n");
+}
+elsif (!$VM->GetDomain()->IsPoweredOn())
+{
+ FatalError("The VM is not powered on\n");
+}
+
+if ($Step->FileType ne "patchdlls")
+{
+ FatalError("Unexpected file type '". $Step->FileType ."' found\n");
+}
+
+
+#
+# Run the task
+#
+
+my $FileName = $Step->GetFullFileName();
+my $TA = $VM->GetAgent();
+Debug(Elapsed($Start), " Sending '$FileName'\n");
+if (!$TA->SendFile($FileName, "staging/patch.diff", 0))
+{
+ FatalTAError($TA, "Could not copy the patch to the VM");
+}
+my $Script = "#!/bin/sh\n".
+ "( set -x\n" .
+ " ../bin/build/WineTest.pl ". $Task->CmdLineArg ." build patch.diff\n".
+ ") >Task.log 2>&1\n";
+Debug(Elapsed($Start), " Sending the script: [$Script]\n");
+if (!$TA->SendFileFromString($Script, "task", $TestAgent::SENDFILE_EXE))
+{
+ FatalTAError($TA, "Could not send the task script to the VM");
+}
+
+Debug(Elapsed($Start), " Starting the script\n");
+my $Pid = $TA->Run(["./task"], 0);
+if (!$Pid)
+{
+ FatalTAError($TA, "Failed to start the task");
+}
+
+
+#
+# From that point on we want to at least try to grab the task
+# log before giving up
+#
+
+my ($NewStatus, $ErrMessage, $TAError, $TaskTimedOut);
+Debug(Elapsed($Start), " Waiting for the script (", $Task->Timeout, "s timeout)\n");
+if (!defined $TA->Wait($Pid, $Task->Timeout, 60))
+{
+ $ErrMessage = $TA->GetLastError();
+ if ($ErrMessage =~ /timed out waiting for the child process/)
+ {
+ $ErrMessage = "The task timed out\n";
+ $NewStatus = "badbuild";
+ $TaskTimedOut = 1;
+ }
+ else
+ {
+ $TAError = "An error occurred while waiting for the task to complete: $ErrMessage";
+ $ErrMessage = undef;
+ }
+}
+
+Debug(Elapsed($Start), " Retrieving 'Task.log'\n");
+if ($TA->GetFile("Task.log", "$TaskDir/log"))
+{
+ my $Result = ParseTaskLog("$TaskDir/log", "Task");
+ if ($Result eq "ok")
+ {
+ # We must have gotten the full log and the build did succeed.
+ # So forget any prior error.
+ $NewStatus = "completed";
+ $TAError = $ErrMessage = undef;
+ }
+ elsif ($Result eq "badpatch")
+ {
+ $NewStatus = "badpatch";
+ }
+ elsif ($Result =~ s/^nolog://)
+ {
+ FatalError("$Result\n", "retry");
+ }
+ else
+ {
+ # If the result line is missing we probably already have an error message
+ # that explains why.
+ $NewStatus = "badbuild";
+ }
+}
+elsif (!defined $TAError)
+{
+ $TAError = "An error occurred while retrieving the task log: ". $TA->GetLastError();
+}
+
+# Report the task errors even though they may have been caused by
+# TestAgent trouble.
+LogTaskError($ErrMessage) if (defined $ErrMessage);
+FatalTAError(undef, $TAError) if (defined $TAError);
+$TA->Disconnect();
+
+
+#
+# Wrap up
+#
+
+WrapUpAndExit($NewStatus, undef, $TaskTimedOut);
diff --git a/testbot/bin/build/WineReconfig.pl b/testbot/bin/build/WineReconfig.pl
new file mode 100755
index 000000000..b030051c6
--- /dev/null
+++ b/testbot/bin/build/WineReconfig.pl
@@ -0,0 +1,258 @@
+#!/usr/bin/perl -Tw
+# -*- Mode: Perl; perl-indent-level: 2; indent-tabs-mode: nil -*-
+#
+# Updates the Wine source from Git and rebuilds it.
+#
+# Copyright 2018 Francois Gouget
+#
+# 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
+
+use strict;
+
+sub BEGIN
+{
+ if ($0 !~ m=^/=)
+ {
+ # Turn $0 into an absolute path so it can safely be used in @INC
+ require Cwd;
+ $0 = Cwd::cwd() . "/$0";
+ }
+ if ($0 =~ m=^(/.*)/[^/]+/[^/]+/[^/]+$=)
+ {
+ $::RootDir = $1;
+ unshift @INC, "$::RootDir/lib";
+ }
+ $::BuildEnv = 1;
+}
+my $Name0 = $0;
+$Name0 =~ s+^.*/++;
+
+
+use WineTestBot::Config;
+use WineTestBot::PatchUtils;
+
+
+#
+# Logging and error handling helpers
+#
+
+sub InfoMsg(@)
+{
+ print @_;
+}
+
+sub LogMsg(@)
+{
+ print "Reconfig: ", @_;
+}
+
+sub Error(@)
+{
+ print STDERR "$Name0:error: ", @_;
+}
+
+
+#
+# Build helpers
+#
+
+my $ncpus;
+sub CountCPUs()
+{
+ if (open(my $fh, "<", "/proc/cpuinfo"))
+ {
+ # Linux
+ map { $ncpus++ if (/^processor/); } <$fh>;
+ close($fh);
+ }
+ $ncpus ||= 1;
+}
+
+sub BuildTestAgentd()
+{
+ # If testagentd already exists it's likely already running
+ # so don't rebuild it.
+ if (! -x "$BinDir/build/testagentd")
+ {
+ InfoMsg "\nBuilding the native testagentd\n";
+ system("cd '$::RootDir/src/testagentd' && set -x && ".
+ "time make -j$ncpus build");
+ if ($? != 0)
+ {
+ LogMsg "Build testagentd failed\n";
+ return !1;
+ }
+ }
+
+ return 1;
+}
+
+sub GitPull($)
+{
+ my ($Targets) = @_;
+ return 1 if (!$Targets->{update});
+
+ InfoMsg "\nUpdating the Wine source\n";
+ system("cd '$DataDir/wine' && git pull");
+ if ($? != 0)
+ {
+ LogMsg "Git pull failed\n";
+ return !1;
+ }
+
+ my $ErrMessage = UpdateWineData("$DataDir/wine");
+ if ($ErrMessage)
+ {
+ LogMsg "$ErrMessage\n";
+ return !1;
+ }
+
+ return 1;
+}
+
+sub BuildWine($$$$)
+{
+ my ($Targets, $NoRm, $Build, $Extras) = @_;
+
+ return 1 if (!$Targets->{$Build});
+ mkdir "$DataDir/build-$Build" if (!-d "$DataDir/build-$Build");
+
+ # If $NoRm is not set, rebuild from scratch to make sure cruft will not
+ # accumulate
+ InfoMsg "\nRebuilding the $Build Wine\n";
+ system("cd '$DataDir/build-$Build' && set -x && ".
+ ($NoRm ? "" : "rm -rf * && ") .
+ "time ../wine/configure $Extras --disable-winetest && ".
+ "time make -j$ncpus");
+ if ($? != 0)
+ {
+ LogMsg "The $Build build failed\n";
+ return !1;
+ }
+
+ return 1;
+}
+
+
+#
+# Setup and command line processing
+#
+
+$ENV{PATH} = "/usr/lib/ccache:/usr/bin:/bin";
+delete $ENV{ENV};
+
+my %AllTargets;
+map { $AllTargets{$_} = 1 } qw(update win32 wow32 wow64);
+
+my ($Usage, $TargetList, $NoRm);
+while (@ARGV)
+{
+ my $Arg = shift @ARGV;
+ if ($Arg eq "--no-rm")
+ {
+ $NoRm = 1;
+ }
+ elsif ($Arg =~ /^(?:-\?|-h|--help)$/)
+ {
+ $Usage = 0;
+ last;
+ }
+ elsif ($Arg =~ /^-/)
+ {
+ Error "unknown option '$Arg'\n";
+ $Usage = 2;
+ last;
+ }
+ elsif (!defined $TargetList)
+ {
+ $TargetList = $Arg;
+ }
+ else
+ {
+ Error "unexpected argument '$Arg'\n";
+ $Usage = 2;
+ last;
+ }
+}
+
+# Check and untaint parameters
+my $Targets;
+if (!defined $Usage)
+{
+ $TargetList = join(",", keys %AllTargets) if (!defined $TargetList);
+ foreach my $Target (split /,/, $TargetList)
+ {
+ if (!$AllTargets{$Target})
+ {
+ Error "invalid target name $Target\n";
+ $Usage = 2;
+ last;
+ }
+ $Targets->{$Target} = 1;
+ }
+}
+if (defined $Usage)
+{
+ if ($Usage)
+ {
+ Error "try '$Name0 --help' for more information\n";
+ exit $Usage;
+ }
+ print "Usage: $Name0 [--no-rm] [--help] [TARGETS]\n";
+ print "\n";
+ print "Performs all the tasks needed for the host to be ready to test new patches: update the Wine source and rebuild the Wine binaries.\n";
+ print "\n";
+ print "Where:\n";
+ print " TARGETS Is a comma-separated list of targets to process. By default all\n";
+ print " targets are processed.\n";
+ print " - update: Update Wine's source code.\n";
+ print " - win32: Rebuild the regular 32 bit Wine.\n";
+ print " - wow32: Rebuild the 32 bit WoW Wine.\n";
+ print " - wow64: Rebuild the 64 bit WoW Wine.\n";
+ print " --no-rm Don't rebuild from scratch.\n";
+ print " --help Shows this usage message.\n";
+ exit 0;
+}
+
+if (! -d "$DataDir/staging" and ! mkdir "$DataDir/staging")
+{
+ LogMsg "Unable to create '$DataDir/staging': $!\n";
+ exit(1);
+}
+
+if ($DataDir =~ /'/)
+{
+ LogMsg "The install path contains invalid characters\n";
+ exit(1);
+}
+
+
+#
+# Run the builds
+#
+
+CountCPUs();
+
+if (!BuildTestAgentd() ||
+ !GitPull($Targets) ||
+ !BuildWine($Targets, $NoRm, "win32", "") ||
+ !BuildWine($Targets, $NoRm, "wow64", "--enable-win64") ||
+ !BuildWine($Targets, $NoRm, "wow32", "--with-wine64='$DataDir/build-wow64'"))
+{
+ exit(1);
+}
+
+LogMsg "ok\n";
+exit;
diff --git a/testbot/bin/build/WineTest.pl b/testbot/bin/build/WineTest.pl
new file mode 100755
index 000000000..01396f9bf
--- /dev/null
+++ b/testbot/bin/build/WineTest.pl
@@ -0,0 +1,282 @@
+#!/usr/bin/perl
+# -*- Mode: Perl; perl-indent-level: 2; indent-tabs-mode: nil -*-
+#
+# Applies the patch and rebuilds Wine.
+#
+# This script does not use tainting (-T) because its whole purpose is to run
+# arbitrary user-provided code anyway.
+#
+# Copyright 2018 Francois Gouget
+#
+# 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
+
+use warnings;
+use strict;
+
+sub BEGIN
+{
+ if ($0 !~ m=^/=)
+ {
+ # Turn $0 into an absolute path so it can safely be used in @INC
+ require Cwd;
+ $0 = Cwd::cwd() . "/$0";
+ }
+ if ($0 =~ m=^(/.*)/[^/]+/[^/]+/[^/]+$=)
+ {
+ $::RootDir = $1;
+ unshift @INC, "$::RootDir/lib";
+ }
+ $::BuildEnv = 1;
+}
+my $Name0 = $0;
+$Name0 =~ s+^.*/++;
+
+
+use WineTestBot::Config;
+use WineTestBot::PatchUtils;
+use WineTestBot::Utils;
+
+
+#
+# Logging and error handling helpers
+#
+
+sub InfoMsg(@)
+{
+ print @_;
+}
+
+sub LogMsg(@)
+{
+ print "Task: ", @_;
+}
+
+sub Error(@)
+{
+ print STDERR "$Name0:error: ", @_;
+}
+
+
+#
+# Build helpers
+#
+
+my $ncpus;
+sub CountCPUs()
+{
+ if (open(my $fh, "<", "/proc/cpuinfo"))
+ {
+ # Linux
+ map { $ncpus++ if (/^processor/); } <$fh>;
+ close($fh);
+ }
+ $ncpus ||= 1;
+}
+
+sub ApplyPatch($)
+{
+ my ($PatchFile) = @_;
+
+ InfoMsg "Applying patch\n";
+ system("cd '$DataDir/wine' && set -x && ".
+ "git apply --verbose ". ShQuote($PatchFile) ." && ".
+ "git add -A");
+ if ($? != 0)
+ {
+ LogMsg "Patch failed to apply\n";
+ return undef;
+ }
+
+ my $Impacts = GetPatchImpact($PatchFile, "nounits");
+ if ($Impacts->{Makefiles})
+ {
+ InfoMsg "\nRunning make_makefiles\n";
+ system("cd '$DataDir/wine' && set -x && ./tools/make_makefiles");
+ if ($? != 0)
+ {
+ LogMsg "make_makefiles failed\n";
+ return undef;
+ }
+ }
+
+ if ($Impacts->{Autoconf} && !$Impacts->{HasConfigure})
+ {
+ InfoMsg "\nRunning autoconf\n";
+ system("cd '$DataDir/wine' && set -x && autoconf");
+ if ($? != 0)
+ {
+ LogMsg "Autoconf failed\n";
+ return undef;
+ }
+ }
+
+ return $Impacts;
+}
+
+sub BuildWine($$)
+{
+ my ($Targets, $Build) = @_;
+
+ return 1 if (!$Targets->{$Build});
+
+ InfoMsg "\nRebuilding the $Build Wine\n";
+ system("cd '$DataDir/build-$Build' && set -x && ".
+ "time make -j$ncpus");
+ if ($? != 0)
+ {
+ LogMsg "The $Build build failed\n";
+ return !1;
+ }
+
+ return 1;
+}
+
+
+#
+# Setup and command line processing
+#
+
+$ENV{PATH} = "/usr/lib/ccache:/usr/bin:/bin";
+delete $ENV{ENV};
+
+my %AllTargets;
+map { $AllTargets{$_} = 1 } qw(win32 wow32 wow64);
+
+my ($Usage, $TargetList, $Action, $FileName);
+while (@ARGV)
+{
+ my $Arg = shift @ARGV;
+ if ($Arg =~ /^(?:-\?|-h|--help)$/)
+ {
+ $Usage = 0;
+ last;
+ }
+ elsif ($Arg =~ /^-/)
+ {
+ Error "unknown option '$Arg'\n";
+ $Usage = 2;
+ last;
+ }
+ elsif (!defined $TargetList)
+ {
+ $TargetList = $Arg;
+ }
+ elsif (!defined $Action)
+ {
+ $Action = $Arg;
+ }
+ elsif (!defined $FileName)
+ {
+ if (IsValidFileName($Arg))
+ {
+ $FileName = "$DataDir/staging/$Arg";
+ if (!-r $FileName)
+ {
+ Error "'$Arg' is not readable\n";
+ $Usage = 2;
+ }
+ }
+ else
+ {
+ Error "invalid file name '$Arg'\n";
+ $Usage = 2;
+ last;
+ }
+ }
+ else
+ {
+ Error "unexpected argument '$Arg'\n";
+ $Usage = 2;
+ last;
+ }
+}
+
+# Check and untaint parameters
+my $Targets;
+if (!defined $Usage)
+{
+ if (defined $TargetList)
+ {
+ foreach my $Target (split /,/, $TargetList)
+ {
+ if (!$AllTargets{$Target})
+ {
+ Error "invalid target name $Target\n";
+ $Usage = 2;
+ }
+ $Targets->{$Target} = 1;
+ }
+ }
+ else
+ {
+ Error "specify at least one target\n";
+ $Usage = 2;
+ }
+
+ if (!defined $Action)
+ {
+ Error "you must specify the action to perform\n";
+ $Usage = 2;
+ }
+ elsif ($Action ne "build")
+ {
+ Error "invalid '$Action' action\n";
+ $Usage = 2;
+ }
+
+ if (!defined $FileName and $Action eq "build")
+ {
+ Error "you must provide a patch file\n";
+ $Usage = 2;
+ }
+}
+if (defined $Usage)
+{
+ print "Usage: $Name0 [--help] TARGETS build PATCH\n";
+ print "\n";
+ print "Applies the specified patch and rebuilds Wine.\n";
+ print "\n";
+ print "Where:\n";
+ print " TARGETS Is a comma-separated list of targets for the build.\n";
+ print " - win32: The regular 32 bit Wine build.\n";
+ print " - wow32: The 32 bit WoW Wine build.\n";
+ print " - wow64: The 64 bit WoW Wine build.\n";
+ print " build Verify that the patch compiles.\n";
+ print " PATCH Is the staging file containing the patch to test.\n";
+ print " --help Shows this usage message.\n";
+ exit $Usage;
+}
+
+if ($DataDir =~ /'/)
+{
+ LogMsg "The install path contains invalid characters\n";
+ exit(1);
+}
+
+
+#
+# Run the builds
+#
+
+CountCPUs();
+
+my $Impacts = ApplyPatch($FileName);
+exit(1) if (!$Impacts or
+ !BuildWine($Targets, "win32") or
+ !BuildWine($Targets, "wow64") or
+ !BuildWine($Targets, "wow32"));
+
+LogMsg "ok\n";
+exit;
diff --git a/testbot/ddl/update37.sql b/testbot/ddl/update37.sql
new file mode 100644
index 000000000..cfa50ce2e
--- /dev/null
+++ b/testbot/ddl/update37.sql
@@ -0,0 +1,4 @@
+USE winetestbot;
+
+ALTER TABLE VMs
+ MODIFY Type ENUM('win32', 'win64', 'build', 'wine') NOT NULL;
diff --git a/testbot/ddl/winetestbot.sql b/testbot/ddl/winetestbot.sql
index 1e7b1edd1..12b5dea1a 100644
--- a/testbot/ddl/winetestbot.sql
+++ b/testbot/ddl/winetestbot.sql
@@ -46,7 +46,7 @@ CREATE TABLE VMs
(
Name VARCHAR(20) NOT NULL,
SortOrder INT(3) NOT NULL,
- Type ENUM('win32', 'win64', 'build') NOT NULL,
+ Type ENUM('win32', 'win64', 'build', 'wine') NOT NULL,
Role ENUM('extra', 'base', 'winetest', 'retired', 'deleted') NOT NULL,
Status ENUM('dirty', 'reverting', 'sleeping', 'idle', 'running', 'off', 'offline', 'maintenance') NOT NULL,
Errors INT(2) NULL,
diff --git a/testbot/lib/WineTestBot/Config.pm b/testbot/lib/WineTestBot/Config.pm
index 66cb813ff..1ffff262f 100644
--- a/testbot/lib/WineTestBot/Config.pm
+++ b/testbot/lib/WineTestBot/Config.pm
@@ -31,7 +31,8 @@ use vars qw (@ISA @EXPORT @EXPORT_OK $UseSSL $LogDir $DataDir $BinDir
$MaxVMsWhenIdle $SleepAfterRevert $WaitForToolsInVM
$VMToolTimeout $MaxVMErrors $MaxTaskTries $AdminEMail $RobotEMail
$WinePatchToOverride $WinePatchCc $SuiteTimeout $SingleTimeout
- $BuildTimeout $ReconfigTimeout $TimeoutMargin $TagPrefix
+ $BuildTimeout $ReconfigTimeout $WineReconfigTimeout $TimeoutMargin
+ $TagPrefix
$MaxUnitSize $ProjectName $PatchesMailingList $LDAPServer
$LDAPBindDN $LDAPSearchBase $LDAPSearchFilter
$LDAPRealNameAttribute $LDAPEMailAttribute $AgentPort $Tunnel
@@ -45,7 +46,8 @@ require Exporter;
$MaxRunningVMs $MaxVMsWhenIdle $SleepAfterRevert $WaitForToolsInVM
$VMToolTimeout $MaxVMErrors $MaxTaskTries $AdminEMail
$RobotEMail $WinePatchToOverride $WinePatchCc $SuiteTimeout
- $SingleTimeout $BuildTimeout $ReconfigTimeout $TimeoutMargin
+ $SingleTimeout $BuildTimeout $ReconfigTimeout $WineReconfigTimeout
+ $TimeoutMargin
$TagPrefix $MaxUnitSize $ProjectName $PatchesMailingList
$LDAPServer $LDAPBindDN $LDAPSearchBase $LDAPSearchFilter
$LDAPRealNameAttribute $LDAPEMailAttribute $AgentPort $Tunnel
@@ -101,6 +103,9 @@ $BuildTimeout = 5 * 60;
# (in seconds). Note that this includes building the native Wine build tools,
# and the 32 and 64 bit test executables.
$ReconfigTimeout = (1 + 2 * 5) * 60;
+# How long to let a full Wine recompilation run before forcibly shutting it
+# down (in seconds).
+$WineReconfigTimeout = 20 * 60;
# How much to add to the task timeout to account for file transfers, etc.
$TimeoutMargin = 2 * 60;
# Maximum amount of traces for a test unit.
diff --git a/testbot/lib/WineTestBot/Engine/Scheduler.pm b/testbot/lib/WineTestBot/Engine/Scheduler.pm
index 159b10cf8..3ed86e28d 100644
--- a/testbot/lib/WineTestBot/Engine/Scheduler.pm
+++ b/testbot/lib/WineTestBot/Engine/Scheduler.pm
@@ -392,7 +392,8 @@ sub _CheckAndClassifyVMs()
20) + # extra
($VM->Type eq "build" ? 0 :
$VM->Type eq "win64" ? 1 :
- 2); # win32
+ $VM->Type eq "win32" ? 2 :
+ 3); # wine
}
# If a VM was in an inconsistent state, update the jobs status fields before
diff --git a/testbot/lib/WineTestBot/PatchUtils.pm b/testbot/lib/WineTestBot/PatchUtils.pm
index 3bf172125..1aeed6740 100644
--- a/testbot/lib/WineTestBot/PatchUtils.pm
+++ b/testbot/lib/WineTestBot/PatchUtils.pm
@@ -36,6 +36,22 @@ our @EXPORT = qw(GetPatchImpact UpdateWineData);
use WineTestBot::Config;
+# These paths are too generic to be proof that this is a Wine patch.
+my $AmbiguousPathsRe = join('|',
+ 'Makefile\.in$',
+ # aclocal.m4 gets special treatment
+ # configure gets special treatment
+ # configure.ac gets special treatment
+ 'include/Makefile\.in$',
+ 'include/config\.h\.in$',
+ 'po/',
+ 'tools/Makefile.in',
+ 'tools/config.guess',
+ 'tools/config.sub',
+ 'tools/install-sh',
+ 'tools/makedep.c',
+);
+
# Patches to these paths don't impact the Wine build. So ignore them.
my $IgnoredPathsRe = join('|',
'\.mailmap$',
@@ -53,6 +69,7 @@ my $IgnoredPathsRe = join('|',
'tools/winemaker/',
);
+
=pod
=over 12
@@ -75,6 +92,7 @@ sub UpdateWineData($)
}
my $_TimeStamp;
+my $_WineFiles;
my $_TestList;
=pod
@@ -83,7 +101,7 @@ my $_TestList;
=item C<_LoadWineFiles()>
Reads latest/winefiles.txt to build a per-module hashtable of the test unit
-files.
+files and a hashtable of all the Wine files.
=back
=cut
@@ -101,11 +119,13 @@ sub _LoadWineFiles()
$_TimeStamp = $MTime;
$_TestList = {};
+ $_WineFiles = {};
if (open(my $fh, "<", $FileName))
{
while (my $Line = <$fh>)
{
chomp $Line;
+ $_WineFiles->{$Line} = 1;
if ($Line =~ m~^\w+/([^/]+)/tests/([^/]+)$~)
{
@@ -121,11 +141,23 @@ sub _LoadWineFiles()
sub _HandleFile($$$)
{
- my ($Impacts, $Path, $Change) = @_;
+ my ($Impacts, $FilePath, $Change) = @_;
- if ($Path =~ m~^(dlls|programs)/([^/]+)/tests/([^/\s]+)$~)
+ if ($Change eq "new")
+ {
+ delete $Impacts->{DeletedFiles}->{$FilePath};
+ $Impacts->{NewFiles}->{$FilePath} = 1;
+ }
+ elsif ($Change eq "rm")
+ {
+ delete $Impacts->{NewFiles}->{$FilePath};
+ $Impacts->{DeletedFiles}->{$FilePath} = 1;
+ }
+
+ if ($FilePath =~ m~^(dlls|programs)/([^/]+)/tests/([^/\s]+)$~)
{
my ($Root, $Module, $File) = ($1, $2, $3);
+ $Impacts->{IsWinePatch} = 1;
$Impacts->{TestBuild} = 1;
my $Tests = $Impacts->{Tests};
@@ -163,8 +195,29 @@ sub _HandleFile($$$)
}
else
{
- # Figure out if this patch impacts the Wine build
- $Impacts->{WineBuild} = 1 if ($Path !~ /^(?:$IgnoredPathsRe)/);
+ my $WineFiles = $Impacts->{WineFiles} || $_WineFiles;
+ if ($WineFiles->{$FilePath})
+ {
+ if ($FilePath !~ /^(?:$AmbiguousPathsRe)/)
+ {
+ $Impacts->{IsWinePatch} = 1;
+ }
+ # Else this file exists in Wine but has a very common name so it may just
+ # as well belong to another repository. Still update WineBuild in case
+ # this patch really is for Wine.
+
+ if ($FilePath !~ /^(?:$IgnoredPathsRe)/)
+ {
+ $Impacts->{WineBuild} = 1;
+ }
+ # Else patches to this file don't impact the Wine build.
+ }
+ elsif ($FilePath =~ m~/Makefile.in$~ and $Change eq "new")
+ {
+ # This may or may not be a Wine patch but the new Makefile.in will be
+ # added to the build by make_makefiles.
+ $Impacts->{WineBuild} = $Impacts->{Makefiles} = 1;
+ }
}
}
@@ -195,6 +248,21 @@ sub GetPatchImpact($;$$)
if ($PastImpacts)
{
+ if ($PastImpacts->{WineBuild} or $PastImpacts->{TestBuild})
+ {
+ # Update the list of Wine files so we correctly recognize patchset parts
+ # that modify new Wine files.
+ my $WineFiles = $PastImpacts->{WineFiles} || $_WineFiles;
+ map { $Impacts->{WineFiles}->{$_} = 1 } keys %{$WineFiles};
+ map { $Impacts->{WineFiles}->{$_} = 1 } keys %{$PastImpacts->{NewFiles}};
+ map { delete $Impacts->{WineFiles}->{$_} } keys %{$PastImpacts->{DeletedFiles}};
+ }
+ else
+ {
+ $Impacts->{NewFiles} = $PastImpacts->{NewFiles};
+ $Impacts->{DeletedFiles} = $PastImpacts->{DeletedFiles};
+ }
+
foreach my $PastInfo (values %{$PastImpacts->{Tests}})
{
if ($PastInfo->{Files})
@@ -207,6 +275,7 @@ sub GetPatchImpact($;$$)
}
}
}
+
my ($Path, $Change);
while (my $Line = <$fh>)
{
@@ -221,6 +290,7 @@ sub GetPatchImpact($;$$)
elsif ($Line =~ m=^--- \w+/tools/make_makefiles$=)
{
$Impacts->{WineBuild} = $Impacts->{Makefiles} = 1;
+ $Impacts->{IsWinePatch} = 1;
}
elsif ($Line =~ m=^--- /dev/null$=)
{
diff --git a/testbot/lib/WineTestBot/Patches.pm b/testbot/lib/WineTestBot/Patches.pm
index 258aa2c19..03a0ebf16 100644
--- a/testbot/lib/WineTestBot/Patches.pm
+++ b/testbot/lib/WineTestBot/Patches.pm
@@ -135,32 +135,24 @@ sub Submit($$$)
my $PastImpacts;
$PastImpacts = GetPatchImpact($PatchFileName) if ($IsSet);
my $Impacts = GetPatchImpact("$DataDir/patches/" . $self->Id, undef, $PastImpacts);
- if (!$Impacts->{UnitCount})
- {
- $self->Disposition(($IsSet ? "Part" : "Patch") .
- " doesn't affect tests");
- return undef;
- }
- my $User;
- my $Users = CreateUsers();
- if (defined($self->FromEMail))
+ if (!$Impacts->{WineBuild} and !$Impacts->{TestBuild})
{
- $Users->AddFilter("EMail", [$self->FromEMail]);
- if (! $Users->IsEmpty())
+ if ($Impacts->{IsWinePatch})
{
- $User = @{$Users->GetItems()}[0];
+ $self->Disposition(($IsSet ? "Part does" : "Does")
+ ." not impact the Wine build");
}
- }
- if (! defined($User))
- {
- $User = GetBatchUser();
+ else
+ {
+ $self->Disposition(($IsSet ? "Part is not" : "Not") ." a Wine patch");
+ }
+ return undef;
}
# Create a new job for this patch
my $Jobs = CreateJobs();
my $NewJob = $Jobs->Add();
- $NewJob->User($User);
$NewJob->Priority(6);
my $PropertyDescriptor = $Jobs->GetPropertyDescriptorByName("Remarks");
my $Subject = $self->Subject;
@@ -171,84 +163,137 @@ sub Submit($$$)
$PropertyDescriptor->GetMaxLength()));
$NewJob->Patch($self);
- # Add build step to the job
- my $BuildStep = $NewJob->Steps->Add();
- $BuildStep->FileName("patch.diff");
- $BuildStep->FileType("patchdlls"); # This is irrelevant now
- $BuildStep->InStaging(!1);
- $BuildStep->Type("build");
- $BuildStep->DebugLevel(0);
-
- # Add build task
- my $VMs = CreateVMs();
- $VMs->AddFilter("Type", ["build"]);
- $VMs->AddFilter("Role", ["base"]);
- my $BuildVM = ${$VMs->GetItems()}[0];
- my $Task = $BuildStep->Tasks->Add();
- $Task->VM($BuildVM);
- $Task->Timeout($BuildTimeout);
-
- # Save the build step so the others can reference it.
- my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
- if (defined($ErrMessage))
+ my $User;
+ my $Users = CreateUsers();
+ if (defined $self->FromEMail)
{
- $self->Disposition("Failed to submit build step");
- return $ErrMessage;
+ $Users->AddFilter("EMail", [$self->FromEMail]);
+ $User = @{$Users->GetItems()}[0] if (!$Users->IsEmpty());
}
+ $NewJob->User($User || GetBatchUser());
- # Stage the patch so it can be picked up by the job
- if (!link($PatchFileName, "$DataDir/staging/job". $NewJob->Id ."_patch.diff"))
+ my $BuildVMs = CreateVMs();
+ $BuildVMs->AddFilter("Type", ["build"]);
+ $BuildVMs->AddFilter("Role", ["base"]);
+ if ($Impacts->{UnitCount} and !$BuildVMs->IsEmpty())
{
- $self->Disposition("Failed to stage the patch file");
- return $!;
- }
+ # Create the Build Step
+ my $BuildStep = $NewJob->Steps->Add();
+ $BuildStep->FileName("patch.diff");
+ $BuildStep->FileType("patchdlls");
+ $BuildStep->InStaging(!1);
+ $BuildStep->Type("build");
+ $BuildStep->DebugLevel(0);
+
+ # Add build task
+ my $BuildVM = ${$BuildVMs->GetItems()}[0];
+ my $Task = $BuildStep->Tasks->Add();
+ $Task->VM($BuildVM);
+ $Task->Timeout($BuildTimeout);
+
+ # Save the build step so the others can reference it.
+ my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined($ErrMessage))
+ {
+ $self->Disposition("Failed to submit build step");
+ return $ErrMessage;
+ }
- foreach my $Module (sort keys %{$Impacts->{Tests}})
- {
- my $TestInfo = $Impacts->{Tests}->{$Module};
- foreach my $Unit (sort keys %{$TestInfo->{Units}})
+ # Create steps for the Windows tests
+ foreach my $Module (sort keys %{$Impacts->{Tests}})
{
- # Add 32 and 64-bit tasks
- foreach my $Bits ("32", "64")
+ my $TestInfo = $Impacts->{Tests}->{$Module};
+ foreach my $Unit (sort keys %{$TestInfo->{Units}})
{
- $VMs = CreateVMs();
- $VMs->AddFilter("Type", $Bits eq "32" ? ["win32", "win64"] : ["win64"]);
- $VMs->AddFilter("Role", ["base"]);
- if (@{$VMs->GetKeys()})
+ foreach my $Bits ("32", "64")
{
- # Create the corresponding Step
- my $NewStep = $NewJob->Steps->Add();
- $NewStep->PreviousNo($BuildStep->No);
- my $FileName = $TestInfo->{ExeBase};
- $FileName .= "64" if ($Bits eq "64");
- $NewStep->FileName("$FileName.exe");
- $NewStep->FileType("exe$Bits");
- $NewStep->InStaging(!1);
-
- # And a task for each VM
- my $Tasks = $NewStep->Tasks;
- my $SortedKeys = $VMs->SortKeysBySortOrder($VMs->GetKeys());
- foreach my $VMKey (@$SortedKeys)
+ my $WinVMs = CreateVMs();
+ $WinVMs->AddFilter("Type", $Bits eq "32" ? ["win32", "win64"] : ["win64"]);
+ $WinVMs->AddFilter("Role", ["base"]);
+ if (!$WinVMs->IsEmpty())
{
- my $VM = $VMs->GetItem($VMKey);
- my $Task = $Tasks->Add();
- $Task->VM($VM);
- $Task->Timeout($SingleTimeout);
- $Task->CmdLineArg($Unit);
+ # Create one Step per (module, unit, bitness) combination
+ my $NewStep = $NewJob->Steps->Add();
+ $NewStep->PreviousNo($BuildStep->No);
+ my $FileName = $TestInfo->{ExeBase};
+ $FileName .= "64" if ($Bits eq "64");
+ $NewStep->FileName("$FileName.exe");
+ $NewStep->FileType("exe$Bits");
+ $NewStep->InStaging(!1);
+
+ # And a task for each VM
+ my $Tasks = $NewStep->Tasks;
+ my $SortedKeys = $WinVMs->SortKeysBySortOrder($WinVMs->GetKeys());
+ foreach my $VMKey (@$SortedKeys)
+ {
+ my $VM = $WinVMs->GetItem($VMKey);
+ my $Task = $Tasks->Add();
+ $Task->VM($VM);
+ $Task->Timeout($SingleTimeout);
+ $Task->CmdLineArg($Unit);
+ }
}
}
}
}
}
+ my $WineVMs = CreateVMs();
+ $WineVMs->AddFilter("Type", ["wine"]);
+ $WineVMs->AddFilter("Role", ["base"]);
+ if (!$WineVMs->IsEmpty())
+ {
+ # Add a Wine step to the job
+ my $NewStep = $NewJob->Steps->Add();
+ $NewStep->FileName("patch.diff");
+ $NewStep->FileType("patchdlls");
+ $NewStep->InStaging(!1);
+ $NewStep->DebugLevel(0);
+
+ # And a task for each VM
+ my $Tasks = $NewStep->Tasks;
+ my $SortedKeys = $WineVMs->SortKeysBySortOrder($WineVMs->GetKeys());
+ foreach my $VMKey (@$SortedKeys)
+ {
+ my $VM = $WineVMs->GetItem($VMKey);
+ my $Task = $Tasks->Add();
+ $Task->VM($VM);
+ # Only verify that the win32 version compiles
+ $Task->Timeout($WineReconfigTimeout);
+ $Task->CmdLineArg("win32");
+ }
+ }
+
+ if ($NewJob->Steps->IsEmpty())
+ {
+ # This may be a Wine patch but there is no suitable VM to test it!
+ if ($Impacts->{UnitCount})
+ {
+ $self->Disposition("No build or test VM!");
+ }
+ else
+ {
+ $self->Disposition(($IsSet ? "Part does" : "Does") ." not impact the ".
+ ($WineVMs->IsEmpty() ? "Windows " : "") ."tests");
+ }
+ return undef;
+ }
+
# Save it all
- ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
if (defined $ErrMessage)
{
$self->Disposition("Failed to submit job");
return $ErrMessage;
}
+ # Stage the patch so it can be picked up by the job
+ if (!link($PatchFileName, "$DataDir/staging/job". $NewJob->Id ."_patch.diff"))
+ {
+ $self->Disposition("Failed to stage the patch file");
+ return $!;
+ }
+
# Switch Status to staging to indicate we are done setting up the job
$NewJob->Status("staging");
($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
@@ -348,27 +393,6 @@ sub IsPatch($$)
return !1;
}
-sub IsTestPatch($$)
-{
- my ($self, $Body) = @_;
-
- if (open(BODY, "<" . $Body->path))
- {
- my $Line;
- while (defined($Line = <BODY>))
- {
- if ($Line =~ m/^\+\+\+ .*\/(dlls|programs)\/[^\/]+\/tests\/[^\/\s]+/)
- {
- close BODY;
- return 1;
- }
- }
- close BODY;
- }
-
- return !1;
-}
-
=pod
=over 12
@@ -412,7 +436,6 @@ sub NewPatch($$$)
my $ErrMessage;
if (scalar(@PatchBodies) == 1)
{
- $Patch->AffectsTests($self->IsTestPatch($PatchBodies[0]));
my $Subject = $Patch->Subject;
$Subject =~ s/32\/64//;
$Subject =~ s/64\/32//;
diff --git a/testbot/lib/WineTestBot/PendingPatchSets.pm b/testbot/lib/WineTestBot/PendingPatchSets.pm
index bfd05e4a3..fc22a8687 100644
--- a/testbot/lib/WineTestBot/PendingPatchSets.pm
+++ b/testbot/lib/WineTestBot/PendingPatchSets.pm
@@ -248,8 +248,7 @@ sub NewSubmission($$)
if (! $Set->CheckSubsetComplete($PartNo))
{
- $Patch->Disposition($Patch->AffectsTests ? "Set not complete yet" :
- "Patch doesn't affect tests");
+ $Patch->Disposition("Set not complete yet");
}
else
{
@@ -260,16 +259,11 @@ sub NewSubmission($$)
my $Part = $Parts->GetItem($PartNo);
if (defined($Part))
{
- if ($Part->Patch->AffectsTests)
+ $ErrMessage = $Set->SubmitSubset($PartNo, $Part->Patch);
+ if (!defined $ErrMessage)
{
- $ErrMessage = $Set->SubmitSubset($PartNo, $Part->Patch);
+ (my $ErrProperty, $ErrMessage) = $Part->Patch->Save();
}
- else
- {
- $Part->Patch->Disposition("Patch doesn't affect tests");
- }
- my $ErrProperty;
- ($ErrProperty, $ErrMessage) = $Part->Patch->Save();
}
else
{
diff --git a/testbot/lib/WineTestBot/StepsTasks.pm b/testbot/lib/WineTestBot/StepsTasks.pm
index 84c5a005f..05854bca7 100644
--- a/testbot/lib/WineTestBot/StepsTasks.pm
+++ b/testbot/lib/WineTestBot/StepsTasks.pm
@@ -78,7 +78,7 @@ sub GetTitle($)
{
$Title .= "64 bit ";
}
- $Title .= $self->CmdLineArg;
+ $Title .= $self->CmdLineArg || "";
}
elsif ($self->Type eq "build")
{
diff --git a/testbot/lib/WineTestBot/Tasks.pm b/testbot/lib/WineTestBot/Tasks.pm
index afd4fb4d3..54ebf5360 100644
--- a/testbot/lib/WineTestBot/Tasks.pm
+++ b/testbot/lib/WineTestBot/Tasks.pm
@@ -177,8 +177,9 @@ sub Run($$)
my ($self, $Step) = @_;
my ($JobId, $StepNo, $TaskNo) = @{$self->GetMasterKey()};
- my $Script = $Step->Type eq "build" ? "Build" :
- $Step->Type eq "reconfig" ? "Reconfig" :
+ my $Script = $Step->Type eq "reconfig" ? "Reconfig" :
+ $self->VM->Type eq "wine" ? "WineTest" :
+ $Step->Type eq "build" ? "Build" :
"Task";
my $Args = ["$BinDir/${ProjectName}Run$Script.pl", "--log-only",
$JobId, $StepNo, $TaskNo];
diff --git a/testbot/lib/WineTestBot/VMs.pm b/testbot/lib/WineTestBot/VMs.pm
index 2f494cb81..cf7bd5ab7 100644
--- a/testbot/lib/WineTestBot/VMs.pm
+++ b/testbot/lib/WineTestBot/VMs.pm
@@ -306,10 +306,10 @@ sub Validate($)
{
my ($self) = @_;
- if ($self->Type ne "win32" && $self->Type ne "win64" &&
- ($self->Role eq "winetest" || $self->Role eq "extra"))
+ if ($self->Type !~ /^(?:win32|win64|wine)$/ and
+ $self->Role =~ /^(?:extra|winetest)$/)
{
- return ("Role", "Only win32 and win64 VMs can have a role of '" . $self->Role . "'");
+ return ("Role", "Only win32, win64 and wine VMs can have a role of '" . $self->Role . "'");
}
return $self->SUPER::Validate();
}
@@ -664,7 +664,7 @@ sub CreateItem($)
my @PropertyDescriptors = (
CreateBasicPropertyDescriptor("Name", "VM name", 1, 1, "A", 20),
CreateBasicPropertyDescriptor("SortOrder", "Display order", !1, 1, "N", 3),
- CreateEnumPropertyDescriptor("Type", "Type of VM", !1, 1, ['win32', 'win64', 'build']),
+ CreateEnumPropertyDescriptor("Type", "Type of VM", !1, 1, ['win32', 'win64', 'build', 'wine']),
CreateEnumPropertyDescriptor("Role", "VM Role", !1, 1, ['extra', 'base', 'winetest', 'retired', 'deleted']),
CreateEnumPropertyDescriptor("Status", "Current status", !1, 1, ['dirty', 'reverting', 'sleeping', 'idle', 'running', 'off', 'offline', 'maintenance']),
CreateBasicPropertyDescriptor("Errors", "Errors", !1, !1, "N", 2),
--
2.17.1
June 21, 2018
[PATCH 2/4] testbot/WineRun*: Improve the VM and Step validation.
by Francois Gouget
Check that the VM is of the expected type. Also the VM should always be
powered on (for powered off snapshots LibvirtTool.pl is responsible for
powering on the VM), even when debugging.
Move the Step validation next to the VM one.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
testbot/bin/WineRunBuild.pl | 15 ++++++++++++---
testbot/bin/WineRunReconfig.pl | 15 ++++++++++++---
testbot/bin/WineRunTask.pl | 24 ++++++++++++++----------
3 files changed, 38 insertions(+), 16 deletions(-)
diff --git a/testbot/bin/WineRunBuild.pl b/testbot/bin/WineRunBuild.pl
index 60af32c73..ff04ded0c 100755
--- a/testbot/bin/WineRunBuild.pl
+++ b/testbot/bin/WineRunBuild.pl
@@ -309,18 +309,27 @@ sub FatalTAError($$)
#
-# Check the VM
+# Check the VM and Step
#
-if (!$Debug and $VM->Status ne "running")
+if ($VM->Type ne "build")
+{
+ FatalError("This is not a build VM! (" . $VM->Type . ")\n");
+}
+elsif (!$Debug and $VM->Status ne "running")
{
FatalError("The VM is not ready for use (" . $VM->Status . ")\n");
}
-elsif ($Debug and !$VM->GetDomain()->IsPoweredOn())
+elsif (!$VM->GetDomain()->IsPoweredOn())
{
FatalError("The VM is not powered on\n");
}
+if ($Step->FileType ne "patchdlls")
+{
+ FatalError("Unexpected file type '". $Step->FileType ."' found\n");
+}
+
#
# Figure out what to build
diff --git a/testbot/bin/WineRunReconfig.pl b/testbot/bin/WineRunReconfig.pl
index 2e338c975..23b038a1f 100755
--- a/testbot/bin/WineRunReconfig.pl
+++ b/testbot/bin/WineRunReconfig.pl
@@ -309,18 +309,27 @@ sub FatalTAError($$)
#
-# Check the VM
+# Check the VM and Step
#
-if (!$Debug and $VM->Status ne "running")
+if ($VM->Type ne "build")
+{
+ FatalError("This is not a build VM! (" . $VM->Type . ")\n");
+}
+elsif (!$Debug and $VM->Status ne "running")
{
FatalError("The VM is not ready for use (" . $VM->Status . ")\n");
}
-elsif ($Debug and !$VM->GetDomain()->IsPoweredOn())
+elsif (!$VM->GetDomain()->IsPoweredOn())
{
FatalError("The VM is not powered on\n");
}
+if ($Step->FileType ne "none")
+{
+ FatalError("Unexpected file type '". $Step->FileType ."' found\n");
+}
+
#
# Run the build
diff --git a/testbot/bin/WineRunTask.pl b/testbot/bin/WineRunTask.pl
index 3f5a3ee8d..7604e73ad 100755
--- a/testbot/bin/WineRunTask.pl
+++ b/testbot/bin/WineRunTask.pl
@@ -364,18 +364,27 @@ sub FatalTAError($$;$)
#
-# Check the VM
+# Check the VM and Step
#
-if (!$Debug and $VM->Status ne "running")
+if ($VM->Type ne "win32" and $VM->Type ne "win64")
+{
+ FatalError("This is not a Windows VM! (" . $VM->Type . ")\n");
+}
+elsif (!$Debug and $VM->Status ne "running")
{
FatalError("The VM is not ready for use (" . $VM->Status . ")\n");
}
-elsif ($Debug and !$VM->GetDomain()->IsPoweredOn())
+elsif (!$VM->GetDomain()->IsPoweredOn())
{
FatalError("The VM is not powered on\n");
}
+if ($Step->FileType ne "exe32" and $Step->FileType ne "exe64")
+{
+ FatalError("Unexpected file type '". $Step->FileType ."' found\n");
+}
+
#
# Setup the VM
@@ -395,11 +404,6 @@ if (!$TA->SetTime())
}
}
-my $FileType = $Step->FileType;
-if ($FileType ne "exe32" && $FileType ne "exe64")
-{
- FatalError("Unexpected file type $FileType found\n");
-}
my $FileName = $Step->FileName;
Debug(Elapsed($Start), " Sending '". $Step->GetFullFileName() ."'\n");
if (!$TA->SendFile($Step->GetFullFileName(), $FileName, 0))
@@ -418,7 +422,7 @@ if ($Step->ReportSuccessfulTests)
my $IsWineTest = 1;
if ($Step->Type eq "single")
{
- my $TestLauncher = "TestLauncher" . ($FileType eq "exe64" ? "64" : "32") . ".exe";
+ my $TestLauncher = "TestLauncher" . ($Step->FileType eq "exe64" ? "64" : "32") . ".exe";
Debug(Elapsed($Start), " Sending 'latest/$TestLauncher'\n");
if (!$TA->SendFile("$DataDir/latest/$TestLauncher", $TestLauncher, 0))
{
@@ -450,7 +454,7 @@ elsif ($Step->Type eq "suite")
$Tag =~ s/[^a-zA-Z0-9]/-/g;
if ($VM->Type eq "win64")
{
- $Tag .= "-" . ($FileType eq "exe64" ? "64" : "32");
+ $Tag .= "-" . ($Step->FileType eq "exe64" ? "64" : "32");
}
if (defined($WebHostName))
{
--
2.17.1
June 21, 2018
[PATCH 1/4] testbot: Build the per-module test unit list from the list of Wine files.
by Francois Gouget
This also caches winefiles.txt so it is only reloaded when it changes.
Also stop generating and retrieving testlist.txt since it is not
needed anymore. If desired testlist.txt can be deleted for cleanup.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
testbot/bin/WineRunReconfig.pl | 10 -----
testbot/lib/WineTestBot/PatchUtils.pm | 62 +++++++++++++--------------
2 files changed, 31 insertions(+), 41 deletions(-)
diff --git a/testbot/bin/WineRunReconfig.pl b/testbot/bin/WineRunReconfig.pl
index 2950799d5..2e338c975 100755
--- a/testbot/bin/WineRunReconfig.pl
+++ b/testbot/bin/WineRunReconfig.pl
@@ -414,16 +414,6 @@ if ($NewStatus eq "completed")
}
}
- Debug(Elapsed($Start), " Retrieving the updated test list '$TaskDir/testlist.txt'\n");
- if ($TA->GetFile("testlist.txt", "$TaskDir/testlist.txt"))
- {
- copy "$TaskDir/testlist.txt", "$DataDir/latest/testlist.txt";
- }
- elsif (!defined $TAError)
- {
- $TAError = "An error occurred while retrieving the test list: ". $TA->GetLastError();
- }
-
Debug(Elapsed($Start), " Retrieving the list of Wine files '$TaskDir/winefiles.txt'\n");
if ($TA->GetFile("latest/winefiles.txt", "$TaskDir/winefiles.txt"))
{
diff --git a/testbot/lib/WineTestBot/PatchUtils.pm b/testbot/lib/WineTestBot/PatchUtils.pm
index f83df6087..3bf172125 100644
--- a/testbot/lib/WineTestBot/PatchUtils.pm
+++ b/testbot/lib/WineTestBot/PatchUtils.pm
@@ -58,8 +58,8 @@ my $IgnoredPathsRe = join('|',
=item C<UpdateWineData()>
-Updates the summary information about the Wine source such as the list of
-tests (testlist.txt) and the full list of Wine files.
+Updates information about the Wine source, such as the list of Wine files,
+for use by the TestBot server.
=back
=cut
@@ -72,51 +72,51 @@ sub UpdateWineData($)
my $ErrMessage = `cd '$WineDir' && git ls-tree -r --name-only HEAD 2>&1 >'$DataDir/latest/winefiles.txt'`;
return $ErrMessage if ($? != 0);
-
- if (open(my $fh, ">", "$DataDir/testlist.txt"))
- {
- foreach my $TestFile (glob("$WineDir/*/*/tests/*.c"),
- glob("$WineDir/*/*/tests/*.spec"))
- {
- next if ($TestFile =~ m=/testlist\.c$=);
- $TestFile =~ s=^$WineDir/==;
- print $fh "$TestFile\n";
- }
- close($fh);
- return undef;
- }
-
- return "Could not open 'testlist.txt' for writing: $!";
}
+my $_TimeStamp;
+my $_TestList;
+
=pod
=over 12
-=item C<GetTestList()>
+=item C<_LoadWineFiles()>
-Returns a hashtable containing the list of the source files for a given module.
-This structure is built from the latest/testlist.txt file.
+Reads latest/winefiles.txt to build a per-module hashtable of the test unit
+files.
=back
=cut
-sub GetTestList()
+sub _LoadWineFiles()
{
- my $TestList = {};
- if (open(my $File, "<", "$DataDir/latest/testlist.txt"))
+ my $FileName = "$DataDir/latest/winefiles.txt";
+ my $MTime = (stat($FileName))[9] || 0;
+
+ if ($_TestList and $_TimeStamp == $MTime)
{
- while (my $TestFileName = <$File>)
+ # The file has not changed since we loaded it
+ return;
+ }
+
+ $_TimeStamp = $MTime;
+ $_TestList = {};
+ if (open(my $fh, "<", $FileName))
+ {
+ while (my $Line = <$fh>)
{
- chomp $TestFileName;
- if ($TestFileName =~ m~^\w+/([^/]+)/tests/([^/]+)$~)
+ chomp $Line;
+
+ if ($Line =~ m~^\w+/([^/]+)/tests/([^/]+)$~)
{
my ($Module, $File) = ($1, $2);
- $TestList->{$Module}->{$File} = 1;
+ next if ($File eq "testlist.c");
+ next if ($File !~ /\.(?:c|spec)$/);
+ $_TestList->{$Module}->{$File} = 1;
}
}
- close($File);
+ close($fh);
}
- return $TestList;
}
sub _HandleFile($$$)
@@ -154,8 +154,7 @@ sub _HandleFile($$$)
if (!$Tests->{$Module}->{Files})
{
- my $TestList = ( $Impacts->{TestList} ||= GetTestList() );
- foreach my $File (keys %{$TestList->{$Module}})
+ foreach my $File (keys %{$_TestList->{$Module}})
{
$Tests->{$Module}->{Files}->{$File} = 0; # not modified
}
@@ -192,6 +191,7 @@ sub GetPatchImpact($;$$)
NoUnits => $NoUnits,
Tests => {},
};
+ _LoadWineFiles();
if ($PastImpacts)
{
--
2.17.1
June 21, 2018