3#include <winpr/print.h>
4#include <winpr/image.h>
5#include <winpr/clipboard.h>
6#include <winpr/stream.h>
9#include <winpr/shell.h>
10#include <winpr/path.h>
12#define test_log(fmt, ...) test_log_fn(__FILE__, __func__, __LINE__, (fmt), ##__VA_ARGS__)
13static void test_log_fn(
const char* file,
const char* func,
size_t line,
const char* fmt, ...)
20 winpr_vasprintf(&fstr, &fslen, fmt, ap);
21 (void)fprintf(stderr,
"[%" PRIu32
":%s]: %s\n", line, func, fstr);
26#define test_ClipboardSetData(clipboard, formatId, data, size) \
27 test_ClipboardSetDataFn(__FILE__, __func__, __LINE__, (clipboard), (formatId), (data), (size))
30static BOOL test_ClipboardSetDataFn(
const char* file,
const char* func,
size_t line,
31 wClipboard* clipboard, UINT32 formatId,
const void* data,
34 const BOOL rc = ClipboardSetData(clipboard, formatId, data, size);
37 test_log_fn(file, func, line,
"ClipboardSetData %s[0x%04" PRIx32
"][%" PRIu32
"] failed",
38 ClipboardGetFormatName(clipboard, formatId), formatId, size);
43#define test_ClipboardGetData(clipboard, formatId, size) \
44 test_ClipboardGetDataFn(__FILE__, __func__, __LINE__, (clipboard), (formatId), (size))
47static void* test_ClipboardGetDataFn(
const char* file,
const char* func,
size_t line,
48 wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
50 void* rc = ClipboardGetData(clipboard, formatId, pSize);
53 test_log_fn(file, func, line,
"ClipboardGetData %s[0x%04" PRIx32
"] failed",
54 ClipboardGetFormatName(clipboard, formatId), formatId);
60static BOOL test_dib_to_bmp(
const BYTE* dib,
size_t dibSize,
size_t expectedOffset)
64 wClipboard* clipboard = ClipboardCreate();
67 test_log(
"ClipboardCreate failed");
71 const UINT32 bmpId = ClipboardRegisterFormat(clipboard,
"image/bmp");
74 test_log(
"bmpId == 0");
77 if (dibSize > UINT32_MAX)
79 test_log(
"dibSize %" PRIuz
" > UINT32_MAX failed");
82 if (!test_ClipboardSetData(clipboard, CF_DIB, dib, (UINT32)dibSize))
86 BYTE* bmp = test_ClipboardGetData(clipboard, bmpId, &bmpSize);
91 wImage* image = winpr_image_new();
94 test_log(
"winpr_image_new failed");
97 rc = (winpr_image_read_buffer(image, bmp, bmpSize) > 0);
99 test_log(
"winpr_image_read_buffer failed");
100 winpr_image_free(image, TRUE);
105 ClipboardDestroy(clipboard);
109static void write_dib_info_header(
wStream* s, UINT32 size, UINT16 bpp, UINT32 compression)
113 Stream_Write_UINT32(s, size);
114 Stream_Write_INT32(s, 1);
115 Stream_Write_INT32(s, 1);
116 Stream_Write_UINT16(s, 1);
117 Stream_Write_UINT16(s, bpp);
118 Stream_Write_UINT32(s, compression);
119 Stream_Write_UINT32(s, 4);
124static BOOL test_dib_offsets(
void)
127 wStream sbuffer = WINPR_C_ARRAY_INIT;
128 wStream* s = Stream_StaticInit(&sbuffer, v4,
sizeof(v4));
131 write_dib_info_header(s,
sizeof(
BITMAPV4HEADER), 32, BI_BITFIELDS);
133 Stream_Write_UINT32(s, 0x00FF0000);
134 Stream_Write_UINT32(s, 0x0000FF00);
135 Stream_Write_UINT32(s, 0x000000FF);
136 Stream_Write_UINT32(s, 0xFF000000);
138 Stream_Write_UINT32(s, 0xFF123456);
142 BYTE bitfields[
sizeof(
BITMAPINFOHEADER) + 3 *
sizeof(DWORD) + 4] = WINPR_C_ARRAY_INIT;
143 s = Stream_StaticInit(&sbuffer, bitfields,
sizeof(bitfields));
147 Stream_Write_UINT32(s, 0x00FF0000);
148 Stream_Write_UINT32(s, 0x0000FF00);
149 Stream_Write_UINT32(s, 0x000000FF);
150 Stream_Write_UINT32(s, 0xFF123456);
151 if (!test_dib_to_bmp(bitfields,
sizeof(bitfields),
157 s = Stream_StaticInit(&sbuffer, paletted,
sizeof(paletted));
161 Stream_Zero(s, 256 *
sizeof(
RGBQUAD));
162 Stream_Write_UINT32(s, 0);
163 return test_dib_to_bmp(paletted,
sizeof(paletted),
169static BOOL test_text_leading_newline(
void)
171 const char* tests[][2] = { {
"\nfoo",
"\r\nfoo" }, {
"\n",
"\r\n" } };
174 wClipboard* clipboard = ClipboardCreate();
177 test_log(
"ClipboardCreate failed");
181 const UINT32 textId = ClipboardRegisterFormat(clipboard,
"text/plain");
182 for (
size_t x = 0; x < ARRAYSIZE(tests); x++)
184 const char** cur = tests[x];
186 if (!test_ClipboardSetData(clipboard, textId, cur[0], (UINT32)strlen(cur[0])))
190 WCHAR* wstr = test_ClipboardGetData(clipboard, CF_UNICODETEXT, &size);
191 char* str = ConvertWCharNToUtf8Alloc(wstr, size /
sizeof(WCHAR),
nullptr);
192 const BOOL match = str && (strcmp(str, cur[1]) == 0);
195 test_log(
"text/plain to CF_UNICODETEXT failed for case %" PRIuz
"[%s (%" PRIu32
196 ") %p : expect %s (%" PRIuz
") : from %s (" PRIuz
")]",
197 x, str, size, wstr, cur[1], strlen(cur[1]), cur[0], strlen(cur[0]));
207 ClipboardDestroy(clipboard);
212static BOOL test_text_conversion(
void)
214 wClipboard* clipboard = ClipboardCreate();
229 const test_case_t tests[] = {
231 { ClipboardRegisterFormat(clipboard,
"text/plain"),
232 ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
"a\nb\ncሴ՞", 10,
234 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
235 ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\r\nb\nc՞՞", 10,
"a\r\nb\nc՞՞", 11 },
236 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_UNICODETEXT,
"a\nb\ncሴ՞", 10,
237 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00",
239 { CF_UNICODETEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
240 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00", 18,
242 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_TEXT,
"a\nb\ncሴ՞", 10,
243 "a\nb\nc\\u1234\\u055e", 18 },
244 { CF_TEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\nc\\u1234\\u055e", 17,
246 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_OEMTEXT,
"a\nb\ncሴ՞", 10,
247 "a\nb\nc\\u1234\\u055e", 18 },
248 { CF_OEMTEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\rc\\u1234\\u055e", 17,
251 { ClipboardRegisterFormat(clipboard,
"text/plain"),
252 ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
"a\nb\rc\\u1234\\u055e",
253 17,
"a\nb\ncሴ՞", 11 },
254 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
255 ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\r\nb\nc՞՞", 10,
256 "a\r\nb\nc\\u055e\\u055e", 19 },
257 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_UNICODETEXT,
"a\nb\rc\\u1234\\u055e",
258 17,
"\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00",
260 { CF_UNICODETEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
261 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00", 18,
262 "a\nb\nc\\u1234\\u055e", 18 },
263 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_TEXT,
"a\nb\rc\\u1234\\u055e", 17,
264 "a\nb\nc\\u1234\\u055e", 18 },
265 { CF_TEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\nc\\u1234\\u055e", 17,
266 "a\nb\nc\\u1234\\u055e", 18 },
267 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_OEMTEXT,
"a\nb\rc\\u1234\\u055e", 17,
268 "a\nb\nc\\u1234\\u055e", 18 },
269 { CF_OEMTEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\rc\\u1234\\u055e", 17,
270 "a\nb\nc\\u1234\\u055e", 18 },
273 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
274 ClipboardRegisterFormat(clipboard,
"UTF8_STRING"),
"ab", 3,
"ab", 3 },
275 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"), CF_UNICODETEXT,
"ab", 3,
276 "\x61\x00\x62\x00\x00\x00", 6 }
281 for (
size_t x = 0; x < ARRAYSIZE(tests); x++)
283 const test_case_t* cur = &tests[x];
286 if (!test_ClipboardSetData(clipboard, cur->srcFormat, cur->src, cur->srcLen))
290 BYTE* data = test_ClipboardGetData(clipboard, cur->dstFormat, &dstSize);
291 if (dstSize != cur->expectLen)
293 winpr_znfree(data, dstSize);
300 winpr_znfree(data, dstSize);
303 const int rc = memcmp(cur->expect, data, cur->expectLen);
304 winpr_znfree(data, dstSize);
309 winpr_znfree(data, dstSize);
314 ClipboardDestroy(clipboard);
319static BOOL set_descriptor_name(
FILEDESCRIPTORW* dsc,
const char* name,
const char* trailing)
323 memset(dsc->cFileName, 0xff,
sizeof(dsc->cFileName));
324 const size_t len = strlen(name);
325 if (ConvertUtf8NToWChar(name, len + 1, dsc->cFileName, ARRAYSIZE(dsc->cFileName)) < 0)
327 else if (trailing && (ConvertUtf8NToWChar(trailing, strlen(trailing), &dsc->cFileName[len],
328 ARRAYSIZE(dsc->cFileName) - len - 1) < 0))
331 test_log(
"set_descriptor_name(%s, %s) failed", name, trailing);
336static BOOL test_file_descriptors_to_uri_list(
void)
339 char* basePath = GetKnownSubPath(KNOWN_PATH_TEMP,
"frdp-test");
342 char* uris =
nullptr;
348 } data = WINPR_C_ARRAY_INIT;
350 char* cmpdir =
nullptr;
351 char* cmpa =
nullptr;
352 wClipboard* clipboard = ClipboardCreate();
356 HRESULT hr = PathCchConvertStyleA(basePath, strlen(basePath), PATH_STYLE_UNIX);
360 wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard);
363 delegate->basePath = basePath;
366 winpr_asprintf(&cmpdir, &len,
"file://%s/dir\r\n", basePath);
367 winpr_asprintf(&cmpa, &len,
"file://%s/a.txt", basePath);
368 if (!cmpdir || !cmpa)
372 winpr_Data_Write_UINT32(&data.count, ARRAYSIZE(data.dsc));
373 winpr_Data_Write_UINT32(&data.dsc[0].dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
374 if (!set_descriptor_name(&data.dsc[0],
"dir",
nullptr))
376 if (!set_descriptor_name(&data.dsc[1],
"dir\\b.txt",
nullptr))
379 if (!set_descriptor_name(&data.dsc[2],
"a.txt",
"old\\name"))
381 if (!set_descriptor_name(&data.dsc[3],
"a.txt",
"old\\"))
385 const UINT32 srcId = ClipboardRegisterFormat(clipboard,
"FileGroupDescriptorW");
386 const UINT32 dstId = ClipboardRegisterFormat(clipboard,
"text/uri-list");
387 if (!test_ClipboardSetData(clipboard, srcId, &data,
sizeof(data)))
391 uris = test_ClipboardGetData(clipboard, dstId, &size);
394 test_log(
"text/uri-list: '%s'", uris);
396 if (!strstr(uris, cmpdir))
398 test_log(
"strstr(%s, %s) failed", uris, cmpdir);
401 if (strstr(uris,
"b.txt"))
403 test_log(
"strstr(%s, %s) failed", uris,
"b.txt");
411 test_log(
"FileGroupDescriptorW -> text/uri-list conversion failed");
416 ClipboardDestroy(clipboard);
422static BOOL test_is_plain_bmp(
const BYTE* bmp,
size_t size)
424 if ((size < 54) || (bmp[0] !=
'B') || (bmp[1] !=
'M'))
429 UINT32 biCompression = 0;
430 memcpy(&biSize, &bmp[14],
sizeof(biSize));
431 memcpy(&biHeight, &bmp[22],
sizeof(biHeight));
432 memcpy(&biCompression, &bmp[30],
sizeof(biCompression));
433 if ((biSize != 40) || (biHeight <= 0) || (biCompression != BI_RGB))
435 test_log(
"not a plain bottom-up BMP: biSize=%" PRIu32
" biHeight=%" PRId32
436 " biCompression=%" PRIu32,
437 biSize, biHeight, biCompression);
445static BOOL test_convert_image(wClipboard* clipboard,
const char* dstMime,
const wImage* expected)
449 wImage* img = winpr_image_new();
451 test_ClipboardGetData(clipboard, ClipboardRegisterFormat(clipboard, dstMime), &size);
455 if (winpr_image_read_buffer(img, data, size) <= 0)
457 test_log(
"can not read %s", dstMime);
461 if (!winpr_image_equal(expected, img,
462 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
463 WINPR_IMAGE_CMP_FUZZY))
465 test_log(
"%s differs from the source image", dstMime);
469 if ((strcmp(dstMime,
"image/bmp") == 0) && !test_is_plain_bmp(data, size))
475 winpr_image_free(img, TRUE);
481static BOOL test_image_to_image(
void)
484 void* data =
nullptr;
486 wClipboard* clipboard = ClipboardCreate();
487 wImage* img = winpr_image_new();
488 if (!clipboard || !img)
491 if (winpr_image_read(img, TEST_CLIP_BMP) <= 0)
494 data = winpr_image_write_buffer(img, WINPR_IMAGE_BITMAP, &size);
495 if (!data || !test_ClipboardSetData(clipboard, ClipboardRegisterFormat(clipboard,
"image/bmp"),
501 if (!test_convert_image(clipboard,
"image/x-bmp", img))
503#if defined(WINPR_UTILS_IMAGE_PNG)
504 if (!test_convert_image(clipboard,
"image/png", img))
507 data = winpr_image_write_buffer(img, WINPR_IMAGE_PNG, &size);
508 if (!data || !test_ClipboardSetData(clipboard, ClipboardRegisterFormat(clipboard,
"image/png"),
512 if (!test_convert_image(clipboard,
"image/bmp", img))
519 winpr_image_free(img, TRUE);
520 ClipboardDestroy(clipboard);
526static BOOL test_text_plain(
void)
529 const char utf8[] =
"A\xe4\xb8\xad\xe6\x96\x87\xc3\xa9";
530 const size_t utf8len = strlen(utf8);
531 char* plain =
nullptr;
532 WCHAR* wstr =
nullptr;
533 char* back =
nullptr;
536 wClipboard* clipboard = ClipboardCreate();
540 const UINT32 idUtf8 = ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8");
541 const UINT32 idPlain = ClipboardRegisterFormat(clipboard,
"text/plain");
542 if (!test_ClipboardSetData(clipboard, idUtf8, utf8, (UINT32)utf8len))
545 plain = test_ClipboardGetData(clipboard, idPlain, &size);
550 if ((size < utf8len) || (memcmp(plain, utf8, utf8len) != 0))
552 test_log(
"text/plain is not UTF-8");
556 for (UINT32 x = 0; x < size; x++)
558 if ((BYTE)plain[x] > 0x7f)
560 test_log(
"text/plain is not ASCII");
566 if (!test_ClipboardSetData(clipboard, idPlain, plain, size))
569 wstr = test_ClipboardGetData(clipboard, CF_UNICODETEXT, &size);
575 back = ConvertWCharNToUtf8Alloc(wstr, size /
sizeof(WCHAR), &backlen);
576 if (!back || (backlen < utf8len) || (memcmp(back, utf8, utf8len) != 0))
578 test_log(
"text/plain does not convert back to the original text");
588 ClipboardDestroy(clipboard);
592int TestClipboardFormats(
int argc,
char* argv[])
596 UINT32* pFormatIds =
nullptr;
597 const char* formatName =
nullptr;
598 UINT32 utf8StringFormatId = 0;
603 if (!test_text_leading_newline())
606 if (!test_text_conversion())
609 if (!test_file_descriptors_to_uri_list())
612 wClipboard* clipboard = ClipboardCreate();
616 const char* mime_types[] = {
"text/plain",
"text/plain;charset=utf-8",
617 "text/html",
"image/bmp",
618 "image/png",
"image/webp",
620 for (
size_t x = 0; x < ARRAYSIZE(mime_types); x++)
622 const char* mime = mime_types[x];
623 UINT32
id = ClipboardRegisterFormat(clipboard, mime);
624 test_log(
"ClipboardRegisterFormat(%s) -> 0x%08" PRIx32
"", mime,
id);
629 utf8StringFormatId = ClipboardRegisterFormat(clipboard,
"UTF8_STRING");
630 pFormatIds =
nullptr;
631 count = ClipboardGetRegisteredFormatIds(clipboard, &pFormatIds);
633 for (UINT32 index = 0; index < count; index++)
635 UINT32 formatId = pFormatIds[index];
636 formatName = ClipboardGetFormatName(clipboard, formatId);
637 test_log(
"Format: 0x%08" PRIX32
" %s", formatId, formatName);
644 const char pSrcData[] =
"this is a test string";
645 char* pDstData =
nullptr;
647 UINT32 SrcSize = (UINT32)(strnlen(pSrcData, ARRAYSIZE(pSrcData)) + 1);
648 const BOOL bSuccess =
649 test_ClipboardSetData(clipboard, utf8StringFormatId, pSrcData, SrcSize);
654 pDstData = (
char*)test_ClipboardGetData(clipboard, utf8StringFormatId, &DstSize);
655 const BOOL equal = (SrcSize == DstSize) && (strncmp(pSrcData, pDstData, SrcSize) == 0);
661 WCHAR* pDstData = (WCHAR*)test_ClipboardGetData(clipboard, CF_UNICODETEXT, &WDstSize);
663 size_t cmpSrcSize = 0;
665 ConvertWCharNToUtf8Alloc(pDstData, WDstSize /
sizeof(WCHAR), &cmpSrcSize);
667 (SrcSize == cmpSrcSize + 1ull) && (strncmp(pCmpData, pSrcData, SrcSize) == 0);
675 pFormatIds =
nullptr;
676 count = ClipboardGetFormatIds(clipboard, &pFormatIds);
678 for (UINT32 index = 0; index < count; index++)
680 UINT32 formatId = pFormatIds[index];
681 formatName = ClipboardGetFormatName(clipboard, formatId);
682 test_log(
"Format: 0x%08" PRIX32
" %s", formatId, formatName);
687 const char* name = TEST_CLIP_BMP;
688 BOOL bSuccess = FALSE;
689 UINT32 idBmp = ClipboardRegisterFormat(clipboard,
"image/bmp");
691 wImage* img = winpr_image_new();
695 if (winpr_image_read(img, name) <= 0)
697 winpr_image_free(img, TRUE);
702 void* data = winpr_image_write_buffer(img, WINPR_IMAGE_BITMAP, &bmpsize);
703 bSuccess = test_ClipboardSetData(clipboard, idBmp, data, bmpsize);
706 winpr_image_free(img, TRUE);
714 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
717 bSuccess = test_ClipboardSetData(clipboard,
id, pDstData, DstSize);
723 const uint32_t
id = ClipboardGetFormatId(clipboard,
"HTML Format");
725 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
729 FILE* fp = fopen(
"test.html",
"w");
732 (void)fwrite(pDstData, 1, DstSize, fp);
739 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/bmp");
742 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
746 if (DstSize != bmpsize)
750#if defined(WINPR_UTILS_IMAGE_PNG)
752 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/png");
755 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
761 const char* name = TEST_CLIP_PNG;
762 BOOL bSuccess = FALSE;
763 UINT32 idBmp = ClipboardRegisterFormat(clipboard,
"image/png");
765 wImage* img = winpr_image_new();
769 if (winpr_image_read(img, name) <= 0)
771 winpr_image_free(img, TRUE);
776 void* data = winpr_image_write_buffer(img, WINPR_IMAGE_PNG, &bmpsize);
777 bSuccess = test_ClipboardSetData(clipboard, idBmp, data, bmpsize);
780 winpr_image_free(img, TRUE);
788 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
791 bSuccess = test_ClipboardSetData(clipboard,
id, pDstData, DstSize);
798#if defined(WINPR_UTILS_IMAGE_WEBP)
800 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/webp");
803 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
810#if defined(WINPR_UTILS_IMAGE_JPEG)
812 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/jpeg");
815 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
823 if (!test_dib_offsets())
826 if (!test_image_to_image())
829 if (!test_text_plain())
836 ClipboardDestroy(clipboard);