kernel32: FindExSearchLimitToDirectories has no effect on FindFirstFileEx.
[wine] / dlls / kernel32 / tests / console.c
1 /*
2  * Unit tests for console API
3  *
4  * Copyright (c) 2003,2004 Eric Pouech
5  * Copyright (c) 2007 Kirill K. Smirnov
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "wine/test.h"
23 #include <windows.h>
24 #include <stdio.h>
25
26 /* DEFAULT_ATTRIB is used for all initial filling of the console.
27  * all modifications are made with TEST_ATTRIB so that we could check
28  * what has to be modified or not
29  */
30 #define TEST_ATTRIB    (BACKGROUND_BLUE | FOREGROUND_GREEN)
31 #define DEFAULT_ATTRIB (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED)
32 /* when filling the screen with non-blank chars, this macro defines
33  * what character should be at position 'c'
34  */
35 #define CONTENT(c)    ('A' + (((c).Y * 17 + (c).X) % 23))
36
37 #define okCURSOR(hCon, c) do { \
38   CONSOLE_SCREEN_BUFFER_INFO __sbi; \
39   BOOL expect = GetConsoleScreenBufferInfo((hCon), &__sbi) && \
40                 __sbi.dwCursorPosition.X == (c).X && __sbi.dwCursorPosition.Y == (c).Y; \
41   ok(expect, "Expected cursor at (%d,%d), got (%d,%d)\n", \
42      (c).X, (c).Y, __sbi.dwCursorPosition.X, __sbi.dwCursorPosition.Y); \
43 } while (0)
44
45 #define okCHAR(hCon, c, ch, attr) do { \
46   char __ch; WORD __attr; DWORD __len; BOOL expect; \
47   expect = ReadConsoleOutputCharacter((hCon), &__ch, 1, (c), &__len) == 1 && __len == 1 && __ch == (ch); \
48   ok(expect, "At (%d,%d): expecting char '%c'/%02x got '%c'/%02x\n", (c).X, (c).Y, (ch), (ch), __ch, __ch); \
49   expect = ReadConsoleOutputAttribute((hCon), &__attr, 1, (c), &__len) == 1 && __len == 1 && __attr == (attr); \
50   ok(expect, "At (%d,%d): expecting attr %04x got %04x\n", (c).X, (c).Y, (attr), __attr); \
51 } while (0)
52
53 /* FIXME: this could be optimized on a speed point of view */
54 static void resetContent(HANDLE hCon, COORD sbSize, BOOL content)
55 {
56     COORD       c;
57     WORD        attr = DEFAULT_ATTRIB;
58     char        ch;
59     DWORD       len;
60
61     for (c.X = 0; c.X < sbSize.X; c.X++)
62     {
63         for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
64         {
65             ch = (content) ? CONTENT(c) : ' ';
66             WriteConsoleOutputAttribute(hCon, &attr, 1, c, &len);
67             WriteConsoleOutputCharacterA(hCon, &ch, 1, c, &len);
68         }
69     }
70 }
71
72 static void testCursor(HANDLE hCon, COORD sbSize)
73 {
74     COORD               c;
75
76     c.X = c.Y = 0;
77     ok(SetConsoleCursorPosition(0, c) == 0, "No handle\n");
78     ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError: expecting %u got %u\n",
79        ERROR_INVALID_HANDLE, GetLastError());
80
81     c.X = c.Y = 0;
82     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
83     okCURSOR(hCon, c);
84
85     c.X = sbSize.X - 1;
86     c.Y = sbSize.Y - 1;
87     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in lower-right\n");
88     okCURSOR(hCon, c);
89
90     c.X = sbSize.X;
91     c.Y = sbSize.Y - 1;
92     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
93     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
94        ERROR_INVALID_PARAMETER, GetLastError());
95
96     c.X = sbSize.X - 1;
97     c.Y = sbSize.Y;
98     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
99     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
100        ERROR_INVALID_PARAMETER, GetLastError());
101
102     c.X = -1;
103     c.Y = 0;
104     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
105     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
106        ERROR_INVALID_PARAMETER, GetLastError());
107
108     c.X = 0;
109     c.Y = -1;
110     ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
111     ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %u\n",
112        ERROR_INVALID_PARAMETER, GetLastError());
113 }
114
115 static void testWriteSimple(HANDLE hCon, COORD sbSize)
116 {
117     COORD               c;
118     DWORD               len;
119     const char*         mytest = "abcdefg";
120     const int   mylen = strlen(mytest);
121
122     /* single line write */
123     c.X = c.Y = 0;
124     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
125
126     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
127     c.Y = 0;
128     for (c.X = 0; c.X < mylen; c.X++)
129     {
130         okCHAR(hCon, c, mytest[c.X], TEST_ATTRIB);
131     }
132
133     okCURSOR(hCon, c);
134     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
135 }
136
137 static void testWriteNotWrappedNotProcessed(HANDLE hCon, COORD sbSize)
138 {
139     COORD               c;
140     DWORD               len, mode;
141     const char*         mytest = "123";
142     const int           mylen = strlen(mytest);
143     int                 ret;
144     int                 p;
145
146     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode & ~(ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT)),
147        "clearing wrap at EOL & processed output\n");
148
149     /* write line, wrapping disabled, buffer exceeds sb width */
150     c.X = sbSize.X - 3; c.Y = 0;
151     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
152
153     ret = WriteConsole(hCon, mytest, mylen, &len, NULL);
154     ok(ret != 0 && len == mylen, "Couldn't write, ret = %d, len = %d\n", ret, len);
155     c.Y = 0;
156     for (p = mylen - 3; p < mylen; p++)
157     {
158         c.X = sbSize.X - 3 + p % 3;
159         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
160     }
161
162     c.X = 0; c.Y = 1;
163     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
164
165     p = sbSize.X - 3 + mylen % 3;
166     c.X = p; c.Y = 0;
167
168     /* write line, wrapping disabled, strings end on end of line */
169     c.X = sbSize.X - mylen; c.Y = 0;
170     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
171
172     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
173 }
174
175 static void testWriteNotWrappedProcessed(HANDLE hCon, COORD sbSize)
176 {
177     COORD               c;
178     DWORD               len, mode;
179     const char*         mytest = "abcd\nf\tg";
180     const int   mylen = strlen(mytest);
181     const int   mylen2 = strchr(mytest, '\n') - mytest;
182     int                 p;
183
184     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, (mode | ENABLE_PROCESSED_OUTPUT) & ~ENABLE_WRAP_AT_EOL_OUTPUT),
185        "clearing wrap at EOL & setting processed output\n");
186
187     /* write line, wrapping disabled, buffer exceeds sb width */
188     c.X = sbSize.X - 5; c.Y = 0;
189     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-5\n");
190
191     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
192     c.Y = 0;
193     for (c.X = sbSize.X - 5; c.X < sbSize.X - 1; c.X++)
194     {
195         okCHAR(hCon, c, mytest[c.X - sbSize.X + 5], TEST_ATTRIB);
196     }
197     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
198
199     c.X = 0; c.Y++;
200     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
201     for (c.X = 1; c.X < 8; c.X++)
202         okCHAR(hCon, c, ' ', TEST_ATTRIB);
203     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
204     c.X++;
205     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
206
207     okCURSOR(hCon, c);
208
209     /* write line, wrapping disabled, strings end on end of line */
210     c.X = sbSize.X - 4; c.Y = 0;
211     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
212
213     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
214     c.Y = 0;
215     for (c.X = sbSize.X - 4; c.X < sbSize.X; c.X++)
216     {
217         okCHAR(hCon, c, mytest[c.X - sbSize.X + 4], TEST_ATTRIB);
218     }
219     c.X = 0; c.Y++;
220     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
221     for (c.X = 1; c.X < 8; c.X++)
222         okCHAR(hCon, c, ' ', TEST_ATTRIB);
223     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
224     c.X++;
225     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
226
227     okCURSOR(hCon, c);
228
229     /* write line, wrapping disabled, strings end after end of line */
230     c.X = sbSize.X - 3; c.Y = 0;
231     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
232
233     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
234     c.Y = 0;
235     for (p = mylen2 - 3; p < mylen2; p++)
236     {
237         c.X = sbSize.X - 3 + p % 3;
238         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
239     }
240     c.X = 0; c.Y = 1;
241     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
242     for (c.X = 1; c.X < 8; c.X++)
243         okCHAR(hCon, c, ' ', TEST_ATTRIB);
244     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
245     c.X++;
246     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
247
248     okCURSOR(hCon, c);
249 }
250
251 static void testWriteWrappedNotProcessed(HANDLE hCon, COORD sbSize)
252 {
253     COORD               c;
254     DWORD               len, mode;
255     const char*         mytest = "abcd\nf\tg";
256     const int   mylen = strlen(mytest);
257     int                 p;
258
259     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon,(mode | ENABLE_WRAP_AT_EOL_OUTPUT) & ~(ENABLE_PROCESSED_OUTPUT)),
260        "setting wrap at EOL & clearing processed output\n");
261
262     /* write line, wrapping enabled, buffer doesn't exceed sb width */
263     c.X = sbSize.X - 9; c.Y = 0;
264     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
265
266     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
267     c.Y = 0;
268     for (p = 0; p < mylen; p++)
269     {
270         c.X = sbSize.X - 9 + p;
271         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
272     }
273     c.X = sbSize.X - 9 + mylen;
274     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
275     c.X = 0; c.Y = 1;
276     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
277
278     /* write line, wrapping enabled, buffer does exceed sb width */
279     c.X = sbSize.X - 3; c.Y = 0;
280     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
281
282     c.Y = 1;
283     c.X = mylen - 3;
284     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
285 }
286
287 static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
288 {
289     COORD               c;
290     DWORD               len, mode;
291     const char*         mytest = "abcd\nf\tg";
292     const int   mylen = strlen(mytest);
293     int                 p;
294
295     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode | (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)),
296        "setting wrap at EOL & processed output\n");
297
298     /* write line, wrapping enabled, buffer doesn't exceed sb width */
299     c.X = sbSize.X - 9; c.Y = 0;
300     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
301
302     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
303     for (p = 0; p < 4; p++)
304     {
305         c.X = sbSize.X - 9 + p;
306         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
307     }
308     c.X = sbSize.X - 9 + p;
309     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
310     c.X = 0; c.Y++;
311     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
312     for (c.X = 1; c.X < 8; c.X++)
313         okCHAR(hCon, c, ' ', TEST_ATTRIB);
314     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
315     c.X++;
316     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
317     okCURSOR(hCon, c);
318
319     /* write line, wrapping enabled, buffer does exceed sb width */
320     c.X = sbSize.X - 3; c.Y = 2;
321     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
322
323     ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
324     for (p = 0; p < 3; p++)
325     {
326         c.X = sbSize.X - 3 + p;
327         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
328     }
329     c.X = 0; c.Y++;
330     okCHAR(hCon, c, mytest[3], TEST_ATTRIB);
331     c.X++;
332     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
333
334     c.X = 0; c.Y++;
335     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
336     for (c.X = 1; c.X < 8; c.X++)
337         okCHAR(hCon, c, ' ', TEST_ATTRIB);
338     okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
339     c.X++;
340     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
341     okCURSOR(hCon, c);
342 }
343
344 static void testWrite(HANDLE hCon, COORD sbSize)
345 {
346     /* FIXME: should in fact insure that the sb is at least 10 character wide */
347     ok(SetConsoleTextAttribute(hCon, TEST_ATTRIB), "Setting default text color\n");
348     resetContent(hCon, sbSize, FALSE);
349     testWriteSimple(hCon, sbSize);
350     resetContent(hCon, sbSize, FALSE);
351     testWriteNotWrappedNotProcessed(hCon, sbSize);
352     resetContent(hCon, sbSize, FALSE);
353     testWriteNotWrappedProcessed(hCon, sbSize);
354     resetContent(hCon, sbSize, FALSE);
355     testWriteWrappedNotProcessed(hCon, sbSize);
356     resetContent(hCon, sbSize, FALSE);
357     testWriteWrappedProcessed(hCon, sbSize);
358 }
359
360 static void testScroll(HANDLE hCon, COORD sbSize)
361 {
362     SMALL_RECT  scroll, clip;
363     COORD       dst, c, tc;
364     CHAR_INFO   ci;
365
366 #define W 11
367 #define H 7
368
369 #define IN_SRECT(r,c) ((r).Left <= (c).X && (c).X <= (r).Right && (r).Top <= (c).Y && (c).Y <= (r).Bottom)
370 #define IN_SRECT2(r,d,c) ((d).X <= (c).X && (c).X <= (d).X + (r).Right - (r).Left && (d).Y <= (c).Y && (c).Y <= (d).Y + (r).Bottom - (r).Top)
371
372     /* no clipping, src & dst rect don't overlap */
373     resetContent(hCon, sbSize, TRUE);
374
375     scroll.Left = 0;
376     scroll.Right = W - 1;
377     scroll.Top = 0;
378     scroll.Bottom = H - 1;
379     dst.X = W + 3;
380     dst.Y = H + 3;
381     ci.Char.UnicodeChar = '#';
382     ci.Attributes = TEST_ATTRIB;
383
384     clip.Left = 0;
385     clip.Right = sbSize.X - 1;
386     clip.Top = 0;
387     clip.Bottom = sbSize.Y - 1;
388
389     ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
390
391     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
392     {
393         for (c.X = 0; c.X < sbSize.X; c.X++)
394         {
395             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
396             {
397                 tc.X = c.X - dst.X;
398                 tc.Y = c.Y - dst.Y;
399                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
400             }
401             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
402                 okCHAR(hCon, c, '#', TEST_ATTRIB);
403             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
404         }
405     }
406
407     /* no clipping, src & dst rect do overlap */
408     resetContent(hCon, sbSize, TRUE);
409
410     scroll.Left = 0;
411     scroll.Right = W - 1;
412     scroll.Top = 0;
413     scroll.Bottom = H - 1;
414     dst.X = W /2;
415     dst.Y = H / 2;
416     ci.Char.UnicodeChar = '#';
417     ci.Attributes = TEST_ATTRIB;
418
419     clip.Left = 0;
420     clip.Right = sbSize.X - 1;
421     clip.Top = 0;
422     clip.Bottom = sbSize.Y - 1;
423
424     ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
425
426     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
427     {
428         for (c.X = 0; c.X < sbSize.X; c.X++)
429         {
430             if (dst.X <= c.X && c.X < dst.X + W && dst.Y <= c.Y && c.Y < dst.Y + H)
431             {
432                 tc.X = c.X - dst.X;
433                 tc.Y = c.Y - dst.Y;
434                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
435             }
436             else if (c.X < W && c.Y < H) okCHAR(hCon, c, '#', TEST_ATTRIB);
437             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
438         }
439     }
440
441     /* clipping, src & dst rect don't overlap */
442     resetContent(hCon, sbSize, TRUE);
443
444     scroll.Left = 0;
445     scroll.Right = W - 1;
446     scroll.Top = 0;
447     scroll.Bottom = H - 1;
448     dst.X = W + 3;
449     dst.Y = H + 3;
450     ci.Char.UnicodeChar = '#';
451     ci.Attributes = TEST_ATTRIB;
452
453     clip.Left = W / 2;
454     clip.Right = min(W + W / 2, sbSize.X - 1);
455     clip.Top = H / 2;
456     clip.Bottom = min(H + H / 2, sbSize.Y - 1);
457
458     ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
459
460     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
461     {
462         for (c.X = 0; c.X < sbSize.X; c.X++)
463         {
464             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
465             {
466                 tc.X = c.X - dst.X;
467                 tc.Y = c.Y - dst.Y;
468                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
469             }
470             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
471                 okCHAR(hCon, c, '#', TEST_ATTRIB);
472             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
473         }
474     }
475
476     /* clipping, src & dst rect do overlap */
477     resetContent(hCon, sbSize, TRUE);
478
479     scroll.Left = 0;
480     scroll.Right = W - 1;
481     scroll.Top = 0;
482     scroll.Bottom = H - 1;
483     dst.X = W / 2 - 3;
484     dst.Y = H / 2 - 3;
485     ci.Char.UnicodeChar = '#';
486     ci.Attributes = TEST_ATTRIB;
487
488     clip.Left = W / 2;
489     clip.Right = min(W + W / 2, sbSize.X - 1);
490     clip.Top = H / 2;
491     clip.Bottom = min(H + H / 2, sbSize.Y - 1);
492
493     ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
494
495     for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
496     {
497         for (c.X = 0; c.X < sbSize.X; c.X++)
498         {
499             if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
500             {
501                 tc.X = c.X - dst.X;
502                 tc.Y = c.Y - dst.Y;
503                 okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
504             }
505             else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
506                 okCHAR(hCon, c, '#', TEST_ATTRIB);
507             else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
508         }
509     }
510 }
511
512 static int mch_count;
513 /* we need the event as Wine console event generation isn't synchronous
514  * (ie GenerateConsoleCtrlEvent returns before all ctrl-handlers in all
515  * processes have been called).
516  */
517 static HANDLE mch_event;
518 static BOOL WINAPI mch(DWORD event)
519 {
520     mch_count++;
521     SetEvent(mch_event);
522     return TRUE;
523 }
524
525 static void testCtrlHandler(void)
526 {
527     ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
528     ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %u\n", GetLastError());
529     ok(SetConsoleCtrlHandler(mch, TRUE), "Couldn't set handler\n");
530     /* wine requires the event for the test, as we cannot insure, so far, that event
531      * are processed synchronously in GenerateConsoleCtrlEvent()
532      */
533     mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
534     mch_count = 0;
535     ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
536     /* FIXME: it isn't synchronous on wine but it can still happen before we test */
537     if (0) ok(mch_count == 1, "Event isn't synchronous\n");
538     ok(WaitForSingleObject(mch_event, 3000) == WAIT_OBJECT_0, "event sending didn't work\n");
539     CloseHandle(mch_event);
540
541     /* Turning off ctrl-c handling doesn't work on win9x such way ... */
542     ok(SetConsoleCtrlHandler(NULL, TRUE), "Couldn't turn off ctrl-c handling\n");
543     mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
544     mch_count = 0;
545     if(!(GetVersion() & 0x80000000))
546         /* ... and next line leads to an unhandled exception on 9x.  Avoid it on 9x. */
547         ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
548     ok(WaitForSingleObject(mch_event, 3000) == WAIT_TIMEOUT && mch_count == 0, "Event shouldn't have been sent\n");
549     CloseHandle(mch_event);
550     ok(SetConsoleCtrlHandler(mch, FALSE), "Couldn't remove handler\n");
551     ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
552     ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %u\n", GetLastError());
553 }
554
555 /*
556  * Test console screen buffer:
557  * 1) Try to set invalid handle.
558  * 2) Try to set non-console handles.
559  * 3) Use CONOUT$ file as active SB.
560  * 4) Test cursor.
561  * 5) Test output codepage to show it is not a property of SB.
562  * 6) Test switching to old SB if we close all handles to current SB - works
563  * in Windows, TODO in wine.
564  *
565  * What is not tested but should be:
566  * 1) ScreenBufferInfo
567  */
568 static void testScreenBuffer(HANDLE hConOut)
569 {
570     HANDLE hConOutRW, hConOutRO, hConOutWT;
571     HANDLE hFileOutRW, hFileOutRO, hFileOutWT;
572     HANDLE hConOutNew;
573     char test_str1[] = "Test for SB1";
574     char test_str2[] = "Test for SB2";
575     char test_cp866[] = {0xe2, 0xa5, 0xe1, 0xe2, 0};
576     char test_cp1251[] = {0xf2, 0xe5, 0xf1, 0xf2, 0};
577     WCHAR test_unicode[] = {0x0442, 0x0435, 0x0441, 0x0442, 0};
578     WCHAR str_wbuf[20];
579     char str_buf[20];
580     DWORD len;
581     COORD c;
582     BOOL ret;
583     DWORD oldcp;
584
585     /* In the beginning set output codepage to 866 */
586     oldcp = GetConsoleOutputCP();
587     ok(SetConsoleOutputCP(866), "Cannot set output codepage to 866\n");
588
589     hConOutRW = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
590                          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
591                          CONSOLE_TEXTMODE_BUFFER, NULL);
592     ok(hConOutRW != INVALID_HANDLE_VALUE,
593        "Cannot create a new screen buffer for ReadWrite\n");
594     hConOutRO = CreateConsoleScreenBuffer(GENERIC_READ,
595                          FILE_SHARE_READ, NULL,
596                          CONSOLE_TEXTMODE_BUFFER, NULL);
597     ok(hConOutRO != INVALID_HANDLE_VALUE,
598        "Cannot create a new screen buffer for ReadOnly\n");
599     hConOutWT = CreateConsoleScreenBuffer(GENERIC_WRITE,
600                          FILE_SHARE_WRITE, NULL,
601                          CONSOLE_TEXTMODE_BUFFER, NULL);
602     ok(hConOutWT != INVALID_HANDLE_VALUE,
603        "Cannot create a new screen buffer for WriteOnly\n");
604
605     hFileOutRW = CreateFileA("NUL", GENERIC_READ | GENERIC_WRITE,
606                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
607                              OPEN_EXISTING, 0, NULL);
608     ok(hFileOutRW != INVALID_HANDLE_VALUE, "Cannot open NUL for ReadWrite\n");
609     hFileOutRO = CreateFileA("NUL", GENERIC_READ, FILE_SHARE_READ,
610                              NULL, OPEN_EXISTING, 0, NULL);
611     ok(hFileOutRO != INVALID_HANDLE_VALUE, "Cannot open NUL for ReadOnly\n");
612     hFileOutWT = CreateFileA("NUL", GENERIC_WRITE, FILE_SHARE_WRITE,
613                              NULL, OPEN_EXISTING, 0, NULL);
614     ok(hFileOutWT != INVALID_HANDLE_VALUE, "Cannot open NUL for WriteOnly\n");
615
616     /* Trying to set invalid handle */
617     SetLastError(0);
618     ok(!SetConsoleActiveScreenBuffer(INVALID_HANDLE_VALUE),
619        "Shouldn't succeed\n");
620     ok(GetLastError() == ERROR_INVALID_HANDLE,
621        "GetLastError: expecting %u got %u\n",
622        ERROR_INVALID_HANDLE, GetLastError());
623
624     /* Trying to set non-console handles */
625     SetLastError(0);
626     ok(!SetConsoleActiveScreenBuffer(hFileOutRW), "Shouldn't succeed\n");
627     ok(GetLastError() == ERROR_INVALID_HANDLE,
628        "GetLastError: expecting %u got %u\n",
629        ERROR_INVALID_HANDLE, GetLastError());
630
631     SetLastError(0);
632     ok(!SetConsoleActiveScreenBuffer(hFileOutRO), "Shouldn't succeed\n");
633     ok(GetLastError() == ERROR_INVALID_HANDLE,
634        "GetLastError: expecting %u got %u\n",
635        ERROR_INVALID_HANDLE, GetLastError());
636
637     SetLastError(0);
638     ok(!SetConsoleActiveScreenBuffer(hFileOutWT), "Shouldn't succeed\n");
639     ok(GetLastError() == ERROR_INVALID_HANDLE,
640        "GetLastError: expecting %u got %u\n",
641        ERROR_INVALID_HANDLE, GetLastError());
642
643     CloseHandle(hFileOutRW);
644     CloseHandle(hFileOutRO);
645     CloseHandle(hFileOutWT);
646
647     /* Trying to set SB handles with various access modes */
648     SetLastError(0);
649     ok(!SetConsoleActiveScreenBuffer(hConOutRO), "Shouldn't succeed\n");
650     ok(GetLastError() == ERROR_INVALID_HANDLE,
651        "GetLastError: expecting %u got %u\n",
652        ERROR_INVALID_HANDLE, GetLastError());
653
654     ok(SetConsoleActiveScreenBuffer(hConOutWT), "Couldn't set new WriteOnly SB\n");
655
656     ok(SetConsoleActiveScreenBuffer(hConOutRW), "Couldn't set new ReadWrite SB\n");
657
658     CloseHandle(hConOutWT);
659     CloseHandle(hConOutRO);
660
661     /* Now we have two ReadWrite SB, active must be hConOutRW */
662     /* Open current SB via CONOUT$ */
663     hConOutNew = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0,
664                              NULL, OPEN_EXISTING, 0, 0);
665     ok(hConOutNew != INVALID_HANDLE_VALUE, "CONOUT$ is not opened\n");
666
667
668     /* test cursor */
669     c.X = c.Y = 10;
670     SetConsoleCursorPosition(hConOut, c);
671     c.X = c.Y = 5;
672     SetConsoleCursorPosition(hConOutRW, c);
673     okCURSOR(hConOutNew, c);
674     c.X = c.Y = 10;
675     okCURSOR(hConOut, c);
676
677
678     c.X = c.Y = 0;
679
680     /* Write using hConOutNew... */
681     SetConsoleCursorPosition(hConOutNew, c);
682     ret = WriteConsoleA(hConOutNew, test_str2, lstrlenA(test_str2), &len, NULL);
683     ok (ret && len == lstrlenA(test_str2), "WriteConsoleA failed\n");
684     /* ... and read it back via hConOutRW */
685     ret = ReadConsoleOutputCharacterA(hConOutRW, str_buf, lstrlenA(test_str2), c, &len);
686     ok(ret && len == lstrlenA(test_str2), "ReadConsoleOutputCharacterA failed\n");
687     str_buf[lstrlenA(test_str2)] = 0;
688     ok(!lstrcmpA(str_buf, test_str2), "got '%s' expected '%s'\n", str_buf, test_str2);
689
690
691     /* Now test output codepage handling. Current is 866 as we set earlier. */
692     SetConsoleCursorPosition(hConOutRW, c);
693     ret = WriteConsoleA(hConOutRW, test_cp866, lstrlenA(test_cp866), &len, NULL);
694     ok(ret && len == lstrlenA(test_cp866), "WriteConsoleA failed\n");
695     ret = ReadConsoleOutputCharacterW(hConOutRW, str_wbuf, lstrlenA(test_cp866), c, &len);
696     ok(ret && len == lstrlenA(test_cp866), "ReadConsoleOutputCharacterW failed\n");
697     str_wbuf[lstrlenA(test_cp866)] = 0;
698     ok(!lstrcmpW(str_wbuf, test_unicode), "string does not match the pattern\n");
699
700     /*
701      * cp866 is OK, let's switch to cp1251.
702      * We expect that this codepage will be used in every SB - active and not.
703      */
704     ok(SetConsoleOutputCP(1251), "Cannot set output cp to 1251\n");
705     SetConsoleCursorPosition(hConOutRW, c);
706     ret = WriteConsoleA(hConOutRW, test_cp1251, lstrlenA(test_cp1251), &len, NULL);
707     ok(ret && len == lstrlenA(test_cp1251), "WriteConsoleA failed\n");
708     ret = ReadConsoleOutputCharacterW(hConOutRW, str_wbuf, lstrlenA(test_cp1251), c, &len);
709     ok(ret && len == lstrlenA(test_cp1251), "ReadConsoleOutputCharacterW failed\n");
710     str_wbuf[lstrlenA(test_cp1251)] = 0;
711     ok(!lstrcmpW(str_wbuf, test_unicode), "string does not match the pattern\n");
712
713     /* Check what has happened to hConOut. */
714     SetConsoleCursorPosition(hConOut, c);
715     ret = WriteConsoleA(hConOut, test_cp1251, lstrlenA(test_cp1251), &len, NULL);
716     ok(ret && len == lstrlenA(test_cp1251), "WriteConsoleA failed\n");
717     ret = ReadConsoleOutputCharacterW(hConOut, str_wbuf, lstrlenA(test_cp1251), c, &len);
718     ok(ret && len == lstrlenA(test_cp1251), "ReadConsoleOutputCharacterW failed\n");
719     str_wbuf[lstrlenA(test_cp1251)] = 0;
720     ok(!lstrcmpW(str_wbuf, test_unicode), "string does not match the pattern\n");
721
722     /* Close all handles of current console SB */
723     CloseHandle(hConOutNew);
724     CloseHandle(hConOutRW);
725
726     /* Now active SB should be hConOut */
727     hConOutNew = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0,
728                              NULL, OPEN_EXISTING, 0, 0);
729     ok(hConOutNew != INVALID_HANDLE_VALUE, "CONOUT$ is not opened\n");
730
731     /* Write using hConOutNew... */
732     SetConsoleCursorPosition(hConOutNew, c);
733     ret = WriteConsoleA(hConOutNew, test_str1, lstrlenA(test_str1), &len, NULL);
734     ok (ret && len == lstrlenA(test_str1), "WriteConsoleA failed\n");
735     /* ... and read it back via hConOut */
736     ret = ReadConsoleOutputCharacterA(hConOut, str_buf, lstrlenA(test_str1), c, &len);
737     ok(ret && len == lstrlenA(test_str1), "ReadConsoleOutputCharacterA failed\n");
738     str_buf[lstrlenA(test_str1)] = 0;
739     todo_wine ok(!lstrcmpA(str_buf, test_str1), "got '%s' expected '%s'\n", str_buf, test_str1);
740     CloseHandle(hConOutNew);
741
742     /* This is not really needed under Windows */
743     SetConsoleActiveScreenBuffer(hConOut);
744
745     /* restore codepage */
746     SetConsoleOutputCP(oldcp);
747 }
748
749 START_TEST(console)
750 {
751     HANDLE hConIn, hConOut;
752     BOOL ret;
753     CONSOLE_SCREEN_BUFFER_INFO  sbi;
754
755     /* be sure we have a clean console (and that's our own)
756      * FIXME: this will make the test fail (currently) if we don't run
757      * under X11
758      * Another solution would be to rerun the test under wineconsole with
759      * the curses backend
760      */
761
762     /* first, we detach and open a fresh console to play with */
763     FreeConsole();
764     ok(AllocConsole(), "Couldn't alloc console\n");
765     hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
766     hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
767
768     /* now verify everything's ok */
769     ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
770     ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
771
772     ok(ret = GetConsoleScreenBufferInfo(hConOut, &sbi), "Getting sb info\n");
773     if (!ret) return;
774
775     /* Non interactive tests */
776     testCursor(hConOut, sbi.dwSize);
777     /* will test wrapped (on/off) & processed (on/off) strings output */
778     testWrite(hConOut, sbi.dwSize);
779     /* will test line scrolling at the bottom of the screen */
780     /* testBottomScroll(); */
781     /* will test all the scrolling operations */
782     testScroll(hConOut, sbi.dwSize);
783     /* will test sb creation / modification / codepage handling */
784     testScreenBuffer(hConOut);
785     testCtrlHandler();
786     /* still to be done: access rights & access on objects */
787 }