2 Unix SMB/CIFS implementation.
3 test suite for spoolss rpc operations as performed by various win versions
5 Copyright (C) Kai Blin 2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program 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
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "torture/torture.h"
23 #include "torture/rpc/rpc.h"
24 #include "librpc/gen_ndr/ndr_spoolss_c.h"
25 #include "rpc_server/dcerpc_server.h"
26 #include "ntvfs/ntvfs.h"
27 #include "param/param.h"
29 struct test_spoolss_win_context
{
31 uint32_t printer_count
;
32 union spoolss_PrinterInfo
*printer_info
;
33 union spoolss_PrinterInfo
*current_info
;
39 /* This is a convenience function for all OpenPrinterEx calls */
40 static bool test_OpenPrinterEx(struct torture_context
*tctx
,
41 struct dcerpc_pipe
*p
,
42 struct policy_handle
*handle
,
43 const char *printer_name
,
47 struct spoolss_OpenPrinterEx op
;
48 struct spoolss_UserLevel1 ul_1
;
50 torture_comment(tctx
, "Opening printer '%s'\n", printer_name
);
52 op
.in
.printername
= talloc_strdup(tctx
, printer_name
);
53 op
.in
.datatype
= NULL
;
54 op
.in
.devmode_ctr
.devmode
= NULL
;
55 op
.in
.access_mask
= access_mask
;
57 op
.in
.userlevel
.level1
= &ul_1
;
58 op
.out
.handle
= handle
;
61 ul_1
.client
= "\\clientname";
62 ul_1
.user
= "username";
66 ul_1
.processor
= 4567;
68 status
= dcerpc_spoolss_OpenPrinterEx(p
, tctx
, &op
);
69 torture_assert_ntstatus_ok(tctx
, status
, "OpenPrinterEx failed");
70 torture_assert_werr_ok(tctx
, op
.out
.result
, "OpenPrinterEx failed");
75 static bool test_OpenPrinterAsAdmin(struct torture_context
*tctx
,
76 struct dcerpc_pipe
*p
,
77 const char *printername
)
80 struct spoolss_OpenPrinterEx op
;
81 struct spoolss_ClosePrinter cp
;
82 struct spoolss_UserLevel1 ul_1
;
83 struct policy_handle handle
;
86 ul_1
.client
= "\\clientname";
87 ul_1
.user
= "username";
91 ul_1
.processor
= 4567;
93 op
.in
.printername
= talloc_strdup(tctx
, printername
);
94 op
.in
.datatype
= NULL
;
95 op
.in
.devmode_ctr
.devmode
= NULL
;
96 op
.in
.access_mask
= SERVER_ALL_ACCESS
;
98 op
.in
.userlevel
.level1
= &ul_1
;
99 op
.out
.handle
= &handle
;
101 cp
.in
.handle
= &handle
;
102 cp
.out
.handle
= &handle
;
104 torture_comment(tctx
, "Testing OpenPrinterEx(%s) with admin rights\n",
107 status
= dcerpc_spoolss_OpenPrinterEx(p
, tctx
, &op
);
109 if (NT_STATUS_IS_OK(status
) && W_ERROR_IS_OK(op
.out
.result
)) {
110 status
= dcerpc_spoolss_ClosePrinter(p
, tctx
, &cp
);
111 torture_assert_ntstatus_ok(tctx
, status
, "ClosePrinter failed");
118 static bool test_ClosePrinter(struct torture_context
*tctx
,
119 struct dcerpc_pipe
*p
,
120 struct policy_handle
*handle
);
122 /* This replicates the opening sequence of OpenPrinterEx calls XP does */
123 static bool test_OpenPrinterSequence(struct torture_context
*tctx
,
124 struct dcerpc_pipe
*p
,
125 struct policy_handle
*handle
)
128 char *printername
= talloc_asprintf(tctx
, "\\\\%s",
129 dcerpc_server_name(p
));
131 /* First, see if we can open the printer read_only */
132 ret
= test_OpenPrinterEx(tctx
, p
, handle
, printername
, 0);
133 torture_assert(tctx
, ret
== true, "OpenPrinterEx failed.");
135 ret
= test_ClosePrinter(tctx
, p
, handle
);
136 torture_assert(tctx
, ret
, "ClosePrinter failed");
138 /* Now let's see if we have admin rights to it. */
139 ret
= test_OpenPrinterAsAdmin(tctx
, p
, printername
);
140 torture_assert(tctx
, ret
== true,
141 "OpenPrinterEx as admin failed unexpectedly.");
143 ret
= test_OpenPrinterEx(tctx
, p
, handle
, printername
, SERVER_EXECUTE
);
144 torture_assert(tctx
, ret
== true, "OpenPrinterEx failed.");
149 static bool test_GetPrinterData(struct torture_context
*tctx
,
150 struct dcerpc_pipe
*p
,
151 struct policy_handle
*handle
,
152 const char *value_name
,
153 WERROR expected_werr
,
154 uint32_t expected_value
)
157 struct spoolss_GetPrinterData gpd
;
159 enum spoolss_PrinterDataType type
;
161 torture_comment(tctx
, "Testing GetPrinterData(%s).\n", value_name
);
162 gpd
.in
.handle
= handle
;
163 gpd
.in
.value_name
= value_name
;
165 gpd
.out
.needed
= &needed
;
166 gpd
.out
.type
= &type
;
168 status
= dcerpc_spoolss_GetPrinterData(p
, tctx
, &gpd
);
169 torture_assert_ntstatus_ok(tctx
, status
, "GetPrinterData failed.");
170 torture_assert_werr_equal(tctx
, gpd
.out
.result
, expected_werr
,
171 "GetPrinterData did not return expected error value.");
173 if (W_ERROR_IS_OK(expected_werr
)) {
174 torture_assert_int_equal(tctx
, gpd
.out
.data
.value
,
176 "GetPrinterData did not return expected value.");
181 static bool test_EnumPrinters(struct torture_context
*tctx
,
182 struct dcerpc_pipe
*p
,
183 struct test_spoolss_win_context
*ctx
,
184 uint32_t initial_blob_size
)
187 struct spoolss_EnumPrinters ep
;
188 DATA_BLOB blob
= data_blob_talloc_zero(ctx
, initial_blob_size
);
192 ep
.in
.flags
= PRINTER_ENUM_NAME
;
193 ep
.in
.server
= talloc_asprintf(tctx
, "\\\\%s", dcerpc_server_name(p
));
195 ep
.in
.buffer
= &blob
;
196 ep
.in
.offered
= initial_blob_size
;
197 ep
.out
.needed
= &needed
;
198 ep
.out
.count
= &count
;
200 status
= dcerpc_spoolss_EnumPrinters(p
, ctx
, &ep
);
201 torture_assert_ntstatus_ok(tctx
, status
, "EnumPrinters failed.");
203 if (W_ERROR_EQUAL(ep
.out
.result
, WERR_INSUFFICIENT_BUFFER
)) {
204 blob
= data_blob_talloc_zero(ctx
, needed
);
205 ep
.in
.buffer
= &blob
;
206 ep
.in
.offered
= needed
;
207 status
= dcerpc_spoolss_EnumPrinters(p
, ctx
, &ep
);
208 torture_assert_ntstatus_ok(tctx
, status
,"EnumPrinters failed.");
211 torture_assert_werr_ok(tctx
, ep
.out
.result
, "EnumPrinters failed.");
213 ctx
->printer_count
= count
;
214 ctx
->printer_info
= ep
.out
.info
;
216 torture_comment(tctx
, "Found %d printer(s).\n", ctx
->printer_count
);
221 static bool test_GetPrinter(struct torture_context
*tctx
,
222 struct dcerpc_pipe
*p
,
223 struct policy_handle
*handle
,
224 struct test_spoolss_win_context
*ctx
,
226 uint32_t initial_blob_size
)
229 struct spoolss_GetPrinter gp
;
230 DATA_BLOB blob
= data_blob_talloc_zero(ctx
, initial_blob_size
);
233 torture_comment(tctx
, "Test GetPrinter level %d\n", level
);
235 gp
.in
.handle
= handle
;
237 gp
.in
.buffer
= (initial_blob_size
== 0)?NULL
:&blob
;
238 gp
.in
.offered
= initial_blob_size
;
239 gp
.out
.needed
= &needed
;
241 status
= dcerpc_spoolss_GetPrinter(p
, tctx
, &gp
);
242 torture_assert_ntstatus_ok(tctx
, status
, "GetPrinter failed");
244 if (W_ERROR_EQUAL(gp
.out
.result
, WERR_INSUFFICIENT_BUFFER
)) {
245 blob
= data_blob_talloc_zero(ctx
, needed
);
246 gp
.in
.buffer
= &blob
;
247 gp
.in
.offered
= needed
;
248 status
= dcerpc_spoolss_GetPrinter(p
, tctx
, &gp
);
249 torture_assert_ntstatus_ok(tctx
, status
, "GetPrinter failed");
252 torture_assert_werr_ok(tctx
, gp
.out
.result
, "GetPrinter failed");
254 ctx
->current_info
= gp
.out
.info
;
258 static bool test_EnumJobs(struct torture_context
*tctx
,
259 struct dcerpc_pipe
*p
,
260 struct policy_handle
*handle
)
263 struct spoolss_EnumJobs ej
;
264 DATA_BLOB blob
= data_blob_talloc_zero(tctx
, 1024);
268 torture_comment(tctx
, "Test EnumJobs\n");
270 ej
.in
.handle
= handle
;
272 ej
.in
.buffer
= &blob
;
273 ej
.in
.offered
= 1024;
274 ej
.out
.needed
= &needed
;
275 ej
.out
.count
= &count
;
277 status
= dcerpc_spoolss_EnumJobs(p
, tctx
, &ej
);
278 torture_assert_ntstatus_ok(tctx
, status
, "EnumJobs failed");
279 torture_assert_werr_ok(tctx
, ej
.out
.result
, "EnumJobs failed");
284 static bool test_GetPrinterDriver2(struct torture_context
*tctx
,
285 struct dcerpc_pipe
*p
,
286 struct policy_handle
*handle
)
289 struct spoolss_GetPrinterDriver2 gpd2
;
290 DATA_BLOB blob
= data_blob_talloc_zero(tctx
, 87424);
292 uint32_t server_major_version
;
293 uint32_t server_minor_version
;
295 torture_comment(tctx
, "Testing GetPrinterDriver2\n");
297 gpd2
.in
.handle
= handle
;
298 gpd2
.in
.architecture
= "Windows NT x86";
300 gpd2
.in
.buffer
= &blob
;
301 gpd2
.in
.offered
= 87424;
302 gpd2
.in
.client_major_version
= 3;
303 gpd2
.in
.client_minor_version
= 0;
304 gpd2
.out
.needed
= &needed
;
305 gpd2
.out
.server_major_version
= &server_major_version
;
306 gpd2
.out
.server_minor_version
= &server_minor_version
;
308 status
= dcerpc_spoolss_GetPrinterDriver2(p
, tctx
, &gpd2
);
309 torture_assert_ntstatus_ok(tctx
, status
, "GetPrinterDriver2 failed");
310 torture_assert_werr_ok(tctx
, gpd2
.out
.result
,
311 "GetPrinterDriver2 failed.");
316 static bool test_EnumForms(struct torture_context
*tctx
,
317 struct dcerpc_pipe
*p
,
318 struct policy_handle
*handle
,
319 uint32_t initial_blob_size
)
322 struct spoolss_EnumForms ef
;
323 DATA_BLOB blob
= data_blob_talloc_zero(tctx
, initial_blob_size
);
327 torture_comment(tctx
, "Testing EnumForms\n");
329 ef
.in
.handle
= handle
;
331 ef
.in
.buffer
= (initial_blob_size
== 0)?NULL
:&blob
;
332 ef
.in
.offered
= initial_blob_size
;
333 ef
.out
.needed
= &needed
;
334 ef
.out
.count
= &count
;
336 status
= dcerpc_spoolss_EnumForms(p
, tctx
, &ef
);
337 torture_assert_ntstatus_ok(tctx
, status
, "EnumForms failed");
339 if (W_ERROR_EQUAL(ef
.out
.result
, WERR_INSUFFICIENT_BUFFER
)) {
340 blob
= data_blob_talloc_zero(tctx
, needed
);
341 ef
.in
.buffer
= &blob
;
342 ef
.in
.offered
= needed
;
343 status
= dcerpc_spoolss_EnumForms(p
, tctx
, &ef
);
344 torture_assert_ntstatus_ok(tctx
, status
, "EnumForms failed");
347 torture_assert_werr_ok(tctx
, ef
.out
.result
, "EnumForms failed");
352 static bool test_EnumPrinterKey(struct torture_context
*tctx
,
353 struct dcerpc_pipe
*p
,
354 struct policy_handle
*handle
,
356 struct test_spoolss_win_context
*ctx
)
359 struct spoolss_EnumPrinterKey epk
;
362 torture_comment(tctx
, "Testing EnumPrinterKey(%s)\n", key
);
364 epk
.in
.handle
= handle
;
365 epk
.in
.key_name
= talloc_strdup(tctx
, key
);
366 epk
.in
.key_buffer_size
= 0;
367 epk
.out
.needed
= &needed
;
368 epk
.out
.key_buffer
= talloc_array(tctx
, uint16_t, 0);
370 status
= dcerpc_spoolss_EnumPrinterKey(p
, tctx
, &epk
);
371 torture_assert_ntstatus_ok(tctx
, status
, "EnumPrinterKey failed");
374 if (W_ERROR_EQUAL(epk
.out
.result
, WERR_MORE_DATA
)) {
375 epk
.in
.key_buffer_size
= needed
;
376 epk
.out
.key_buffer
= talloc_array(tctx
, uint16_t, needed
/2);
377 status
= dcerpc_spoolss_EnumPrinterKey(p
, tctx
, &epk
);
378 torture_assert_ntstatus_ok(tctx
, status
,
379 "EnumPrinterKey failed");
382 torture_assert_werr_ok(tctx
, epk
.out
.result
, "EnumPrinterKey failed");
384 convert_string_talloc_convenience(ctx
, lp_iconv_convenience(tctx
->lp_ctx
), CH_UTF16
,
385 CH_UNIX
, epk
.out
.key_buffer
, *epk
.out
.needed
,
386 (void**)&ctx
->printer_keys
, false);
391 static bool test_EnumPrinterDataEx(struct torture_context
*tctx
,
392 struct dcerpc_pipe
*p
,
393 struct policy_handle
*handle
,
395 uint32_t initial_blob_size
,
396 WERROR expected_error
)
399 struct spoolss_EnumPrinterDataEx epde
;
403 torture_comment(tctx
, "Testing EnumPrinterDataEx(%s)\n", key
);
405 epde
.in
.handle
= handle
;
406 epde
.in
.key_name
= talloc_strdup(tctx
, key
);
408 epde
.out
.needed
= &needed
;
409 epde
.out
.count
= &count
;
410 epde
.out
.buffer
= talloc_array(tctx
, uint8_t, 0);
412 status
= dcerpc_spoolss_EnumPrinterDataEx(p
, tctx
, &epde
);
413 torture_assert_ntstatus_ok(tctx
, status
, "EnumPrinterDataEx failed.");
414 if (W_ERROR_EQUAL(epde
.out
.result
, WERR_MORE_DATA
)) {
415 epde
.in
.offered
= needed
;
416 epde
.out
.buffer
= talloc_array(tctx
, uint8_t, needed
);
417 status
= dcerpc_spoolss_EnumPrinterDataEx(p
, tctx
, &epde
);
418 torture_assert_ntstatus_ok(tctx
, status
,
419 "EnumPrinterDataEx failed.");
422 torture_assert_werr_equal(tctx
, epde
.out
.result
, expected_error
,
423 "EnumPrinterDataEx failed.");
428 static bool test_ClosePrinter(struct torture_context
*tctx
,
429 struct dcerpc_pipe
*p
,
430 struct policy_handle
*handle
)
433 struct spoolss_ClosePrinter cp
;
435 cp
.in
.handle
= handle
;
436 cp
.out
.handle
= handle
;
438 status
= dcerpc_spoolss_ClosePrinter(p
, tctx
, &cp
);
439 torture_assert_ntstatus_ok(tctx
, status
, "ClosePrinter failed");
444 static bool test_WinXP(struct torture_context
*tctx
, struct dcerpc_pipe
*p
)
447 struct test_spoolss_win_context
*ctx
, *tmp_ctx
;
448 struct policy_handle handle01
, handle02
, handle03
, handle04
;
449 /* Sometimes a handle stays unused. In order to make this clear in the
450 * code, the unused_handle structures are used for that. */
451 struct policy_handle unused_handle1
, unused_handle2
;
455 ntvfs_init(tctx
->lp_ctx
);
457 ctx
= talloc_zero(tctx
, struct test_spoolss_win_context
);
458 tmp_ctx
= talloc_zero(tctx
, struct test_spoolss_win_context
);
460 ret
&= test_OpenPrinterSequence(tctx
, p
, &handle01
);
461 ret
&= test_GetPrinterData(tctx
, p
, &handle01
,"UISingleJobStatusString",
462 WERR_INVALID_PARAM
, 0);
463 torture_comment(tctx
, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
465 server_name
= talloc_asprintf(ctx
, "\\\\%s", dcerpc_server_name(p
));
466 ret
&= test_OpenPrinterEx(tctx
, p
, &unused_handle1
, server_name
, 0);
468 ret
&= test_EnumPrinters(tctx
, p
, ctx
, 1024);
470 ret
&= test_OpenPrinterEx(tctx
, p
, &handle02
, server_name
, 0);
471 ret
&= test_GetPrinterData(tctx
, p
, &handle02
, "MajorVersion", WERR_OK
,
473 ret
&= test_ClosePrinter(tctx
, p
, &handle02
);
475 /* If no printers were found, skip all tests that need a printer */
476 if (ctx
->printer_count
== 0) {
480 ret
&= test_OpenPrinterEx(tctx
, p
, &handle02
,
481 ctx
->printer_info
[0].info2
.printername
,
483 ret
&= test_GetPrinter(tctx
, p
, &handle02
, ctx
, 2, 0);
485 torture_assert_str_equal(tctx
, ctx
->current_info
->info2
.printername
,
486 ctx
->printer_info
[0].info2
.printername
,
487 "GetPrinter returned unexpected printername");
488 /*FIXME: Test more components of the PrinterInfo2 struct */
490 ret
&= test_OpenPrinterEx(tctx
, p
, &handle03
,
491 ctx
->printer_info
[0].info2
.printername
, 0);
492 ret
&= test_GetPrinter(tctx
, p
, &handle03
, ctx
, 0, 1164);
493 ret
&= test_GetPrinter(tctx
, p
, &handle03
, ctx
, 2, 0);
495 ret
&= test_OpenPrinterEx(tctx
, p
, &handle04
,
496 ctx
->printer_info
[0].info2
.printername
, 0);
497 ret
&= test_GetPrinter(tctx
, p
, &handle04
, ctx
, 2, 0);
498 ret
&= test_ClosePrinter(tctx
, p
, &handle04
);
500 ret
&= test_OpenPrinterEx(tctx
, p
, &handle04
,
501 ctx
->printer_info
[0].info2
.printername
, 0);
502 ret
&= test_GetPrinter(tctx
, p
, &handle04
, ctx
, 2, 4096);
503 ret
&= test_ClosePrinter(tctx
, p
, &handle04
);
505 ret
&= test_OpenPrinterAsAdmin(tctx
, p
,
506 ctx
->printer_info
[0].info2
.printername
);
508 ret
&= test_OpenPrinterEx(tctx
, p
, &handle04
,
509 ctx
->printer_info
[0].info2
.printername
, PRINTER_READ
);
510 ret
&= test_GetPrinterData(tctx
, p
, &handle04
,"UISingleJobStatusString",
512 torture_comment(tctx
, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
514 ret
&= test_OpenPrinterEx(tctx
, p
, &unused_handle2
,
515 ctx
->printer_info
[0].info2
.printername
, 0);
517 ret
&= test_EnumJobs(tctx
, p
, &handle04
);
518 ret
&= test_GetPrinter(tctx
, p
, &handle04
, ctx
, 2, 4096);
520 ret
&= test_ClosePrinter(tctx
, p
, &unused_handle2
);
521 ret
&= test_ClosePrinter(tctx
, p
, &handle04
);
523 ret
&= test_EnumPrinters(tctx
, p
, ctx
, 1556);
524 ret
&= test_GetPrinterDriver2(tctx
, p
, &handle03
);
525 ret
&= test_EnumForms(tctx
, p
, &handle03
, 0);
527 ret
&= test_EnumPrinterKey(tctx
, p
, &handle03
, "", ctx
);
528 key_pointer
= ctx
->printer_keys
;
529 while(*key_pointer
!= '\0') {
533 for(end_pointer
= key_pointer
; *end_pointer
!= '\0';
535 /* Do nothing, just move the pointer */
537 key_name
= talloc_strndup(tctx
, key_pointer
,
538 end_pointer
- key_pointer
);
540 ret
&= test_EnumPrinterKey(tctx
, p
, &handle03
, key_name
,
542 ret
&= test_EnumPrinterDataEx(tctx
, p
, &handle03
, key_name
, 0,
545 key_pointer
= ++end_pointer
;
548 ret
&= test_EnumPrinterDataEx(tctx
, p
, &handle03
, "", 0,
551 ret
&= test_GetPrinter(tctx
, p
, &handle03
, tmp_ctx
, 2, 0);
553 ret
&= test_OpenPrinterEx(tctx
, p
, &unused_handle2
,
554 ctx
->printer_info
[0].info2
.printername
, 0);
555 ret
&= test_ClosePrinter(tctx
, p
, &unused_handle2
);
557 ret
&= test_GetPrinter(tctx
, p
, &handle03
, tmp_ctx
, 2, 2556);
559 ret
&= test_OpenPrinterEx(tctx
, p
, &unused_handle2
,
560 ctx
->printer_info
[0].info2
.printername
, 0);
561 ret
&= test_ClosePrinter(tctx
, p
, &unused_handle2
);
563 ret
&= test_OpenPrinterEx(tctx
, p
, &unused_handle2
,
564 ctx
->printer_info
[0].info2
.printername
, 0);
565 ret
&= test_ClosePrinter(tctx
, p
, &unused_handle2
);
567 ret
&= test_GetPrinter(tctx
, p
, &handle03
, tmp_ctx
, 7, 0);
569 ret
&= test_OpenPrinterEx(tctx
, p
, &unused_handle2
,
570 ctx
->printer_info
[0].info2
.printername
, 0);
571 ret
&= test_ClosePrinter(tctx
, p
, &unused_handle2
);
573 ret
&= test_ClosePrinter(tctx
, p
, &handle03
);
575 ret
&= test_OpenPrinterEx(tctx
, p
, &unused_handle2
,
576 ctx
->printer_info
[0].info2
.printername
, 0);
577 ret
&= test_ClosePrinter(tctx
, p
, &unused_handle2
);
579 ret
&= test_OpenPrinterEx(tctx
, p
, &handle03
, server_name
, 0);
580 ret
&= test_GetPrinterData(tctx
, p
, &handle03
, "W3SvcInstalled",
582 ret
&= test_ClosePrinter(tctx
, p
, &handle03
);
584 ret
&= test_ClosePrinter(tctx
, p
, &unused_handle1
);
585 ret
&= test_ClosePrinter(tctx
, p
, &handle02
);
587 ret
&= test_OpenPrinterEx(tctx
, p
, &handle02
,
588 ctx
->printer_info
[0].info2
.sharename
, 0);
589 ret
&= test_GetPrinter(tctx
, p
, &handle02
, tmp_ctx
, 2, 0);
590 ret
&= test_ClosePrinter(tctx
, p
, &handle02
);
593 ret
&= test_ClosePrinter(tctx
, p
, &handle01
);
595 talloc_free(tmp_ctx
);
600 struct torture_suite
*torture_rpc_spoolss_win(TALLOC_CTX
*mem_ctx
)
602 struct torture_suite
*suite
= torture_suite_create(mem_ctx
, "SPOOLSS-WIN");
604 struct torture_rpc_tcase
*tcase
= torture_suite_add_rpc_iface_tcase(suite
,
605 "win", &ndr_table_spoolss
);
607 torture_rpc_tcase_add_test(tcase
, "testWinXP", test_WinXP
);