torture: support printer publish pending responses
[obnox/samba/samba-obnox.git] / source4 / torture / rpc / spoolss.c
index 674682b6ab1fc41896fd8fb43abd7af3ac438bcc..4c84bc0d1386e586c2b3c3ddb9e695649b1df200 100644 (file)
@@ -5,7 +5,7 @@
    Copyright (C) Tim Potter 2003
    Copyright (C) Stefan Metzmacher 2005
    Copyright (C) Jelmer Vernooij 2007
-   Copyright (C) Guenther Deschner 2009-2010
+   Copyright (C) Guenther Deschner 2009-2011,2013
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include "libcli/libcli.h"
 #include "libcli/raw/raw_proto.h"
 #include "libcli/resolve/resolve.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
 #include "lib/cmdline/popt_common.h"
 #include "system/filesys.h"
+#include "torture/ndr/ndr.h"
+#include "torture/smb2/proto.h"
 
 #define TORTURE_WELLKNOWN_PRINTER      "torture_wkn_printer"
 #define TORTURE_PRINTER                        "torture_printer"
 #define TORTURE_PRINTER_EX             "torture_printer_ex"
 #define TORTURE_DRIVER                 "torture_driver"
 #define TORTURE_DRIVER_EX              "torture_driver_ex"
+#define TORTURE_DRIVER_ADOBE           "torture_driver_adobe"
+#define TORTURE_DRIVER_EX_ADOBE                "torture_driver_ex_adobe"
+#define TORTURE_DRIVER_ADOBE_CUPSADDSMB        "torture_driver_adobe_cupsaddsmb"
+#define TORTURE_DRIVER_TIMESTAMPS      "torture_driver_timestamps"
+#define TORTURE_DRIVER_DELETER         "torture_driver_deleter"
+#define TORTURE_DRIVER_DELETERIN       "torture_driver_deleterin"
+#define TORTURE_PRINTER_STATIC1                "print1"
 
 #define TOP_LEVEL_PRINT_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print"
 #define TOP_LEVEL_PRINT_PRINTERS_KEY TOP_LEVEL_PRINT_KEY "\\Printers"
 #define TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY TOP_LEVEL_CONTROL_KEY "\\Environments"
 
 struct test_spoolss_context {
+       struct dcerpc_pipe *spoolss_pipe;
+
+       /* server environment */
+       const char *environment;
+
        /* print server handle */
        struct policy_handle server_handle;
 
@@ -91,12 +107,15 @@ struct torture_driver_context {
 };
 
 struct torture_printer_context {
+       struct dcerpc_pipe *spoolss_pipe;
        struct spoolss_SetPrinterInfo2 info2;
        struct torture_driver_context driver;
        bool ex;
        bool wellknown;
        bool added_driver;
        bool have_driver;
+       struct spoolss_DeviceMode *devmode;
+       struct policy_handle handle;
 };
 
 static bool upload_printer_driver(struct torture_context *tctx,
@@ -113,7 +132,8 @@ static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
                                               const char *server_name,
                                               struct spoolss_AddDriverInfo8 *r,
                                               uint32_t flags,
-                                              bool ex);
+                                              bool ex,
+                                              const char *remote_driver_dir);
 
 #define COMPARE_STRING(tctx, c,r,e) \
        torture_assert_str_equal(tctx, c.e, r.e, "invalid value")
@@ -173,9 +193,9 @@ static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
 
 #define DO_ROUND(size, n) (((size)+((n)-1)) & ~((n)-1))
 
-#define CHECK_NEEDED_SIZE_ENUM_LEVEL(fn, info, level, count, ic, needed, align) do { \
+#define CHECK_NEEDED_SIZE_ENUM_LEVEL(fn, info, level, count, needed, align) do { \
        if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
-       uint32_t size = ndr_size_##fn##_info(tctx, ic, level, count, info);\
+       uint32_t size = ndr_size_##fn##_info(tctx, level, count, info);\
        uint32_t round_size = DO_ROUND(size, align);\
        if (round_size != needed) {\
                torture_warning(tctx, __location__": "#fn" level %d (count: %d) got unexpected needed size: %d, we calculated: %d", level, count, needed, round_size);\
@@ -184,9 +204,9 @@ static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
        }\
 } while(0)
 
-#define CHECK_NEEDED_SIZE_ENUM(fn, info, count, ic, needed, align) do { \
+#define CHECK_NEEDED_SIZE_ENUM(fn, info, count, needed, align) do { \
        if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
-       uint32_t size = ndr_size_##fn##_info(tctx, ic, count, info);\
+       uint32_t size = ndr_size_##fn##_info(tctx, count, info);\
        uint32_t round_size = DO_ROUND(size, align);\
        if (round_size != needed) {\
                torture_warning(tctx, __location__": "#fn" (count: %d) got unexpected needed size: %d, we calculated: %d", count, needed, round_size);\
@@ -195,9 +215,9 @@ static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
        }\
 } while(0)
 
-#define CHECK_NEEDED_SIZE_LEVEL(fn, info, level, ic, needed, align) do { \
+#define CHECK_NEEDED_SIZE_LEVEL(fn, info, level, needed, align) do { \
        if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
-       uint32_t size = ndr_size_##fn(info, level, ic, 0);\
+       uint32_t size = ndr_size_##fn(info, level, 0);\
        uint32_t round_size = DO_ROUND(size, align);\
        if (round_size != needed) {\
                torture_warning(tctx, __location__": "#fn" level %d got unexpected needed size: %d, we calculated: %d", level, needed, round_size);\
@@ -224,12 +244,12 @@ static bool PrinterInfo_to_SetPrinterInfo(struct torture_context *tctx,
                s->info2->drivername            = i->info2.drivername;
                s->info2->comment               = i->info2.comment;
                s->info2->location              = i->info2.location;
-               s->info2->devmode_ptr           = 0;
+               s->info2->devmode_ptr           = NULL;
                s->info2->sepfile               = i->info2.sepfile;
                s->info2->printprocessor        = i->info2.printprocessor;
                s->info2->datatype              = i->info2.datatype;
                s->info2->parameters            = i->info2.parameters;
-               s->info2->secdesc_ptr           = 0;
+               s->info2->secdesc_ptr           = NULL;
                s->info2->attributes            = i->info2.attributes;
                s->info2->priority              = i->info2.priority;
                s->info2->defaultpriority       = i->info2.defaultpriority;
@@ -277,9 +297,12 @@ static bool test_OpenPrinter_server(struct torture_context *tctx,
 }
 
 static bool test_EnumPorts(struct torture_context *tctx,
-                          struct dcerpc_binding_handle *b,
-                          struct test_spoolss_context *ctx)
+                          void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
        NTSTATUS status;
        struct spoolss_EnumPorts r;
        uint16_t levels[] = { 1, 2 };
@@ -322,7 +345,7 @@ static bool test_EnumPorts(struct torture_context *tctx,
 
                torture_assert(tctx, info, "EnumPorts returned no info");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, r.in.level, count, needed, 4);
 
                ctx->port_count[level]  = count;
                ctx->ports[level]       = info;
@@ -356,10 +379,13 @@ static bool test_EnumPorts(struct torture_context *tctx,
 }
 
 static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
-                                           struct dcerpc_pipe *p,
-                                           const char *environment)
+                                           void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
        NTSTATUS status;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
        struct spoolss_GetPrintProcessorDirectory r;
        struct {
@@ -390,7 +416,7 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
                DATA_BLOB blob;
 
                r.in.server             = levels[i].server;
-               r.in.environment        = environment;
+               r.in.environment        = ctx->environment;
                r.in.level              = level;
                r.in.buffer             = NULL;
                r.in.offered            = 0;
@@ -413,7 +439,7 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "GetPrintProcessorDirectory failed");
 
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_PrintProcessorDirectoryInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 2);
+               CHECK_NEEDED_SIZE_LEVEL(spoolss_PrintProcessorDirectoryInfo, r.out.info, r.in.level, needed, 2);
        }
 
        return true;
@@ -421,10 +447,13 @@ static bool test_GetPrintProcessorDirectory(struct torture_context *tctx,
 
 
 static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
-                                          struct dcerpc_pipe *p,
-                                          const char *environment)
+                                          void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
        NTSTATUS status;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
        struct spoolss_GetPrinterDriverDirectory r;
        struct {
@@ -455,7 +484,7 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
                DATA_BLOB blob;
 
                r.in.server             = levels[i].server;
-               r.in.environment        = environment;
+               r.in.environment        = ctx->environment;
                r.in.level              = level;
                r.in.buffer             = NULL;
                r.in.offered            = 0;
@@ -478,7 +507,7 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed");
 
-               CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverDirectoryInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 2);
+               CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverDirectoryInfo, r.out.info, r.in.level, needed, 2);
        }
 
        return true;
@@ -532,7 +561,7 @@ static bool test_EnumPrinterDrivers_args(struct torture_context *tctx,
                *info_p = info;
        }
 
-       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, needed, 4);
 
        return true;
 
@@ -543,11 +572,13 @@ static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx,
                                            const char *server_name,
                                            const char *environment,
                                            uint32_t level,
-                                           const char *driver_name)
+                                           const char *driver_name,
+                                           union spoolss_DriverInfo *info_p)
 {
        uint32_t count;
        union spoolss_DriverInfo *info;
        int i;
+       const char *environment_ret = NULL;
 
        torture_assert(tctx,
                test_EnumPrinterDrivers_args(tctx, b, server_name, environment, level, &count, &info),
@@ -561,29 +592,41 @@ static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx,
                        break;
                case 2:
                        driver_name_ret = info[i].info2.driver_name;
+                       environment_ret = info[i].info2.architecture;
                        break;
                case 3:
                        driver_name_ret = info[i].info3.driver_name;
+                       environment_ret = info[i].info3.architecture;
                        break;
                case 4:
                        driver_name_ret = info[i].info4.driver_name;
+                       environment_ret = info[i].info4.architecture;
                        break;
                case 5:
                        driver_name_ret = info[i].info5.driver_name;
+                       environment_ret = info[i].info5.architecture;
                        break;
                case 6:
                        driver_name_ret = info[i].info6.driver_name;
+                       environment_ret = info[i].info6.architecture;
                        break;
                case 7:
                        driver_name_ret = info[i].info7.driver_name;
                        break;
                case 8:
                        driver_name_ret = info[i].info8.driver_name;
+                       environment_ret = info[i].info8.architecture;
                        break;
                default:
                        break;
                }
+               if (environment_ret) {
+                       torture_assert_str_equal(tctx, environment, environment_ret, "architecture mismatch");
+               }
                if (strequal(driver_name, driver_name_ret)) {
+                       if (info_p) {
+                               *info_p = info[i];
+                       }
                        return true;
                }
        }
@@ -592,18 +635,25 @@ static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx,
 }
 
 static bool test_EnumPrinterDrivers(struct torture_context *tctx,
-                                   struct dcerpc_pipe *p,
-                                   struct test_spoolss_context *ctx,
-                                   const char *architecture)
+                                   void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
        uint16_t levels[] = { 1, 2, 3, 4, 5, 6, 8 };
-       int i, j;
+       int i, j, a;
 
        /* FIXME: gd, come back and fix "" as server, and handle
         * priority of returned error codes in torture test and samba 3
         * server */
        const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+       const char *environments[2];
+
+       environments[0] = SPOOLSS_ARCHITECTURE_ALL;
+       environments[1] = ctx->environment;
+
+       for (a=0;a<ARRAY_SIZE(environments);a++) {
 
        for (i=0;i<ARRAY_SIZE(levels);i++) {
                int level = levels[i];
@@ -611,7 +661,7 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
                union spoolss_DriverInfo *info;
 
                torture_assert(tctx,
-                       test_EnumPrinterDrivers_args(tctx, b, server_name, architecture, level, &count, &info),
+                       test_EnumPrinterDrivers_args(tctx, b, server_name, environments[a], level, &count, &info),
                        "failed to enumerate drivers");
 
                ctx->driver_count[level]        = count;
@@ -629,8 +679,8 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
        for (i=0;i<ARRAY_SIZE(levels);i++) {
                int level = levels[i];
 
-               for (j=0;j<ctx->driver_count[level];j++) {
-                       union spoolss_DriverInfo *cur = &ctx->drivers[level][j];
+               for (j=0;j<ctx->driver_count[level - 1];j++) {
+                       union spoolss_DriverInfo *cur = &ctx->drivers[level - 1][j];
                        union spoolss_DriverInfo *ref = &ctx->drivers[8][j];
 
                        switch (level) {
@@ -706,14 +756,18 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
                        }
                }
        }
+       }
 
        return true;
 }
 
 static bool test_EnumMonitors(struct torture_context *tctx,
-                             struct dcerpc_binding_handle *b,
-                             struct test_spoolss_context *ctx)
+                             void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
        NTSTATUS status;
        struct spoolss_EnumMonitors r;
        uint16_t levels[] = { 1, 2 };
@@ -754,7 +808,7 @@ static bool test_EnumMonitors(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumMonitors failed");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumMonitors, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumMonitors, info, r.in.level, count, needed, 4);
 
                ctx->monitor_count[level]       = count;
                ctx->monitors[level]            = info;
@@ -786,139 +840,196 @@ static bool test_EnumMonitors(struct torture_context *tctx,
        return true;
 }
 
-static bool test_EnumPrintProcessors(struct torture_context *tctx,
-                                    struct dcerpc_binding_handle *b,
-                                    struct test_spoolss_context *ctx,
-                                    const char *environment)
+static bool test_EnumPrintProcessors_level(struct torture_context *tctx,
+                                          struct dcerpc_binding_handle *b,
+                                          const char *environment,
+                                          uint32_t level,
+                                          uint32_t *count_p,
+                                          union spoolss_PrintProcessorInfo **info_p,
+                                          WERROR expected_result)
 {
-       NTSTATUS status;
        struct spoolss_EnumPrintProcessors r;
-       uint16_t levels[] = { 1 };
-       int i, j;
-
-       for (i=0;i<ARRAY_SIZE(levels);i++) {
-               int level = levels[i];
-               DATA_BLOB blob;
-               uint32_t needed;
-               uint32_t count;
-               union spoolss_PrintProcessorInfo *info;
-
-               r.in.servername = "";
-               r.in.environment = environment;
-               r.in.level = level;
-               r.in.buffer = NULL;
-               r.in.offered = 0;
-               r.out.needed = &needed;
-               r.out.count = &count;
-               r.out.info = &info;
+       DATA_BLOB blob;
+       uint32_t needed;
+       uint32_t count;
+       union spoolss_PrintProcessorInfo *info;
 
-               torture_comment(tctx, "Testing EnumPrintProcessors level %u\n", r.in.level);
+       r.in.servername = "";
+       r.in.environment = environment;
+       r.in.level = level;
+       r.in.buffer = NULL;
+       r.in.offered = 0;
+       r.out.needed = &needed;
+       r.out.count = &count;
+       r.out.info = &info;
 
-               status = dcerpc_spoolss_EnumPrintProcessors_r(b, ctx, &r);
-               torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
-               if (W_ERROR_IS_OK(r.out.result)) {
-                       /* TODO: do some more checks here */
-                       continue;
-               }
-               torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
-                       "EnumPrintProcessors unexpected return code");
+       torture_comment(tctx, "Testing EnumPrintProcessors(%s) level %u\n",
+               r.in.environment, r.in.level);
 
-               blob = data_blob_talloc_zero(ctx, needed);
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r),
+               "EnumPrintProcessors failed");
+       if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+               blob = data_blob_talloc_zero(tctx, needed);
                r.in.buffer = &blob;
                r.in.offered = needed;
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r),
+                       "EnumPrintProcessors failed");
+       }
+       torture_assert_werr_equal(tctx, r.out.result, expected_result,
+               "EnumPrintProcessors failed");
+
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessors, info, level, count, needed, 4);
+
+       if (count_p) {
+               *count_p = count;
+       }
+       if (info_p) {
+               *info_p = info;
+       }
+
+       return true;
+}
 
-               status = dcerpc_spoolss_EnumPrintProcessors_r(b, ctx, &r);
-               torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
+static bool test_EnumPrintProcessors(struct torture_context *tctx,
+                                    void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+       uint16_t levels[] = {0, 1, 2, 3, 32, 256 };
+       uint16_t     ok[] = {0, 1, 0, 0, 0, 0 };
+       int i;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcessors failed");
+       torture_assert(tctx,
+               test_EnumPrintProcessors_level(tctx, b, "phantasy", 1, NULL, NULL, WERR_INVALID_ENVIRONMENT),
+               "test_EnumPrintProcessors_level failed");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessors, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       for (i=0;i<ARRAY_SIZE(levels);i++) {
+               union spoolss_PrintProcessorInfo *info;
+               uint32_t count;
+               WERROR expected_result = ok[i] ? WERR_OK : WERR_INVALID_LEVEL;
 
-               ctx->print_processor_count[level]       = count;
-               ctx->print_processors[level]            = info;
+               torture_assert(tctx,
+                       test_EnumPrintProcessors_level(tctx, b, ctx->environment, levels[i], &count, &info, expected_result),
+                       "test_EnumPrintProcessors_level failed");
        }
 
-       for (i=1;i<ARRAY_SIZE(levels);i++) {
-               int level = levels[i];
-               int old_level = levels[i-1];
-               torture_assert_int_equal(tctx, ctx->print_processor_count[level], ctx->print_processor_count[old_level],
-                       "EnumPrintProcessors failed");
+       return true;
+}
+
+static bool test_EnumPrintProcDataTypes_level(struct torture_context *tctx,
+                                             struct dcerpc_binding_handle *b,
+                                             const char *print_processor_name,
+                                             uint32_t level,
+                                             uint32_t *count_p,
+                                             union spoolss_PrintProcDataTypesInfo **info_p,
+                                             WERROR expected_result)
+{
+       struct spoolss_EnumPrintProcDataTypes r;
+       DATA_BLOB blob;
+       uint32_t needed;
+       uint32_t count;
+       union spoolss_PrintProcDataTypesInfo *info;
+
+       r.in.servername = "";
+       r.in.print_processor_name = print_processor_name;
+       r.in.level = level;
+       r.in.buffer = NULL;
+       r.in.offered = 0;
+       r.out.needed = &needed;
+       r.out.count = &count;
+       r.out.info = &info;
+
+       torture_comment(tctx, "Testing EnumPrintProcDataTypes(%s) level %u\n",
+               r.in.print_processor_name, r.in.level);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_EnumPrintProcDataTypes_r(b, tctx, &r),
+               "EnumPrintProcDataTypes failed");
+       if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+               blob = data_blob_talloc_zero(tctx, needed);
+               r.in.buffer = &blob;
+               r.in.offered = needed;
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_EnumPrintProcDataTypes_r(b, tctx, &r),
+                       "EnumPrintProcDataTypes failed");
        }
+       torture_assert_werr_equal(tctx, r.out.result, expected_result,
+               "EnumPrintProcDataTypes failed");
 
-       for (i=0;i<ARRAY_SIZE(levels);i++) {
-               int level = levels[i];
-               for (j=0;j<ctx->print_processor_count[level];j++) {
-#if 0
-                       union spoolss_PrintProcessorInfo *cur = &ctx->print_processors[level][j];
-                       union spoolss_PrintProcessorInfo *ref = &ctx->print_processors[1][j];
-#endif
-                       switch (level) {
-                       case 1:
-                               /* level 1 is our reference, and it makes no sense to compare it to itself */
-                               break;
-                       }
-               }
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcDataTypes, info, level, count, needed, 4);
+
+       if (count_p) {
+               *count_p = count;
+       }
+       if (info_p) {
+               *info_p = info;
        }
 
        return true;
 }
 
 static bool test_EnumPrintProcDataTypes(struct torture_context *tctx,
-                                       struct dcerpc_binding_handle *b)
+                                       void *private_data)
 {
-       NTSTATUS status;
-       struct spoolss_EnumPrintProcDataTypes r;
-       uint16_t levels[] = { 1 };
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+       uint16_t levels[] = {0, 1, 2, 3, 32, 256 };
+       uint16_t     ok[] = {0, 1, 0, 0, 0, 0 };
        int i;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       torture_assert(tctx,
+               test_EnumPrintProcDataTypes_level(tctx, b, NULL, 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR),
+               "test_EnumPrintProcDataTypes_level failed");
+
+       torture_assert(tctx,
+               test_EnumPrintProcDataTypes_level(tctx, b, "nonexisting", 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR),
+               "test_EnumPrintProcDataTypes_level failed");
 
        for (i=0;i<ARRAY_SIZE(levels);i++) {
                int level = levels[i];
-               DATA_BLOB blob;
-               uint32_t needed;
                uint32_t count;
                union spoolss_PrintProcDataTypesInfo *info;
+               WERROR expected_result = ok[i] ? WERR_OK : WERR_INVALID_LEVEL;
 
-               r.in.servername = "";
-               r.in.print_processor_name = "winprint";
-               r.in.level = level;
-               r.in.buffer = NULL;
-               r.in.offered = 0;
-               r.out.needed = &needed;
-               r.out.count = &count;
-               r.out.info = &info;
-
-               torture_comment(tctx, "Testing EnumPrintProcDataTypes level %u\n", r.in.level);
-
-               status = dcerpc_spoolss_EnumPrintProcDataTypes_r(b, tctx, &r);
-               torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcDataType failed");
-               if (W_ERROR_IS_OK(r.out.result)) {
-                       /* TODO: do some more checks here */
-                       continue;
-               }
-               torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
-                       "EnumPrintProcDataTypes unexpected return code");
-
-               blob = data_blob_talloc_zero(tctx, needed);
-               r.in.buffer = &blob;
-               r.in.offered = needed;
-
-               status = dcerpc_spoolss_EnumPrintProcDataTypes_r(b, tctx, &r);
-               torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcDataTypes failed");
+               torture_assert(tctx,
+                       test_EnumPrintProcDataTypes_level(tctx, b, "winprint", level, &count, &info, expected_result),
+                       "test_EnumPrintProcDataTypes_level failed");
+       }
 
-               torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcDataTypes failed");
+       {
+               union spoolss_PrintProcessorInfo *info;
+               uint32_t count;
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcDataTypes, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               torture_assert(tctx,
+                       test_EnumPrintProcessors_level(tctx, b, ctx->environment, 1, &count, &info, WERR_OK),
+                       "test_EnumPrintProcessors_level failed");
 
+               for (i=0; i < count; i++) {
+                       torture_assert(tctx,
+                               test_EnumPrintProcDataTypes_level(tctx, b, info[i].info1.print_processor_name, 1, NULL, NULL, WERR_OK),
+                               "test_EnumPrintProcDataTypes_level failed");
+               }
        }
 
+
        return true;
 }
 
-
 static bool test_EnumPrinters(struct torture_context *tctx,
-                             struct dcerpc_binding_handle *b,
-                             struct test_spoolss_context *ctx)
+                             void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
        struct spoolss_EnumPrinters r;
        NTSTATUS status;
        uint16_t levels[] = { 0, 1, 2, 4, 5 };
@@ -960,7 +1071,7 @@ static bool test_EnumPrinters(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
 
                ctx->printer_count[level]       = count;
                ctx->printers[level]            = info;
@@ -1079,7 +1190,7 @@ bool test_GetPrinter_level(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "GetPrinter failed");
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterInfo, r.out.info, r.in.level, needed, 4);
 
        if (info && r.out.info) {
                *info = *r.out.info;
@@ -1136,8 +1247,9 @@ static bool test_SetPrinter(struct torture_context *tctx,
 
        torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
                "failed to call SetPrinter");
-       torture_assert_werr_ok(tctx, r.out.result,
-               "failed to call SetPrinter");
+       torture_assert(tctx, (W_ERROR_EQUAL(r.out.result, WERR_OK)
+                          || W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)),
+                      "SetPrinter failed");
 
        return true;
 }
@@ -1306,8 +1418,8 @@ static bool test_SetPrinter_errors(struct torture_context *tctx,
 static void clear_info2(struct spoolss_SetPrinterInfoCtr *r)
 {
        if ((r->level == 2) && (r->info.info2)) {
-               r->info.info2->secdesc_ptr = 0;
-               r->info.info2->devmode_ptr = 0;
+               r->info.info2->secdesc_ptr = NULL;
+               r->info.info2->devmode_ptr = NULL;
        }
 }
 
@@ -1327,6 +1439,8 @@ static bool test_PrinterInfo(struct torture_context *tctx,
        bool ret = true;
        int i;
 
+       torture_skip(tctx, "Printer Info test is currently broken, skipping");
+
        uint32_t status_list[] = {
                /* these do not stick
                PRINTER_STATUS_PAUSED,
@@ -1707,16 +1821,6 @@ static bool test_PrinterInfo(struct torture_context *tctx,
        return ret;
 }
 
-#define torture_assert_sid_equal(torture_ctx,got,expected,cmt)\
-       do { struct dom_sid *__got = (got), *__expected = (expected); \
-       if (!dom_sid_equal(__got, __expected)) { \
-               torture_result(torture_ctx, TORTURE_FAIL, \
-                                          __location__": "#got" was %s, expected %s: %s", \
-                                          dom_sid_string(torture_ctx, __got), dom_sid_string(torture_ctx, __expected), cmt); \
-               return false; \
-       } \
-       } while(0)
-
 static bool test_security_descriptor_equal(struct torture_context *tctx,
                                           const struct security_descriptor *sd1,
                                           const struct security_descriptor *sd2)
@@ -1780,7 +1884,7 @@ static bool test_sd_set_level(struct torture_context *tctx,
        case 3: {
                struct spoolss_SetPrinterInfo3 info3;
 
-               info3.sec_desc_ptr = 0;
+               info3.sec_desc_ptr = NULL;
 
                info_ctr.level = 3;
                info_ctr.info.info3 = &info3;
@@ -1951,7 +2055,7 @@ static bool test_devmode_set_level(struct torture_context *tctx,
        case 8: {
                struct spoolss_SetPrinterInfo8 info8;
 
-               info8.devmode_ptr = 0;
+               info8.devmode_ptr = NULL;
 
                info_ctr.level = 8;
                info_ctr.info.info8 = &info8;
@@ -2038,7 +2142,7 @@ static bool test_devicemode_full(struct torture_context *tctx,
        bool ret = true;
        NTSTATUS status;
 
-#define TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value) do { \
+#define TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, exp_value, expected_result) do { \
                torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
                q.in.level = lvl1; \
                TESTGETCALL(GetPrinter, q) \
@@ -2050,16 +2154,22 @@ static bool test_devicemode_full(struct torture_context *tctx,
                }\
                devmode_ctr.devmode = q.out.info->info ## lvl1.devmode; \
                devmode_ctr.devmode->field1 = value; \
-               TESTSETCALL(SetPrinter, s) \
-               TESTGETCALL(GetPrinter, q) \
-               INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \
-               q.in.level = lvl2; \
-               TESTGETCALL(GetPrinter, q) \
-               INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \
+               TESTSETCALL_EXP(SetPrinter, s, expected_result) \
+               if (W_ERROR_IS_OK(expected_result)) { \
+                       TESTGETCALL(GetPrinter, q) \
+                       INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \
+                       q.in.level = lvl2; \
+                       TESTGETCALL(GetPrinter, q) \
+                       INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \
+               }\
        } while (0)
 
+#define TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, expected_result) do { \
+        TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, value, expected_result); \
+        } while (0)
+
 #define TEST_DEVMODE_INT(lvl1, field1, lvl2, field2, value) do { \
-        TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, value); \
+        TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, value, WERR_OK); \
         } while (0)
 
        ZERO_STRUCT(devmode_ctr);
@@ -2080,10 +2190,22 @@ static bool test_devicemode_full(struct torture_context *tctx,
        const char *devicename;/* [charset(UTF16)] */
        enum spoolss_DeviceModeSpecVersion specversion;
        uint16_t driverversion;
-       uint16_t size;
        uint16_t __driverextra_length;/* [value(r->driverextra_data.length)] */
        uint32_t fields;
 #endif
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, __LINE__, WERR_INVALID_PARAM);
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, 0, WERR_INVALID_PARAM);
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, 0xffff, WERR_INVALID_PARAM);
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAM : WERR_OK);
+       TEST_DEVMODE_INT(8, size,               8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
+
+       devmode_ctr.devmode->driverextra_data = data_blob_string_const("foobar");
+       torture_assert(tctx,
+               test_devmode_set_level(tctx, b, handle, 8, devmode_ctr.devmode),
+               "failed to set devmode");
+
+       TEST_DEVMODE_INT_EXP(8, size,           8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAM : WERR_OK);
+       TEST_DEVMODE_INT(8, size,               8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
 
        TEST_DEVMODE_INT(8, orientation,        8, orientation, __LINE__);
        TEST_DEVMODE_INT(8, papersize,          8, papersize, __LINE__);
@@ -2125,10 +2247,6 @@ static bool call_OpenPrinterEx(struct torture_context *tctx,
                               struct spoolss_DeviceMode *devmode,
                               struct policy_handle *handle);
 
-static bool test_ClosePrinter(struct torture_context *tctx,
-                             struct dcerpc_binding_handle *b,
-                             struct policy_handle *handle);
-
 static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
                                      struct dcerpc_pipe *p,
                                      struct policy_handle *handle,
@@ -2146,10 +2264,20 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
 
        devmode = info.info8.devmode;
 
+       if (devmode && devmode->size == 0) {
+               torture_fail(tctx,
+                       "devmode of zero size!");
+       }
+
        torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
 
        devmode2 = info.info2.devmode;
 
+       if (devmode2 && devmode2->size == 0) {
+               torture_fail(tctx,
+                       "devmode of zero size!");
+       }
+
        torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
                "DM level 8 != DM level 2");
 
@@ -2274,7 +2402,8 @@ static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
 static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
                                     struct dcerpc_pipe *p,
                                     struct policy_handle *handle,
-                                    const char *name)
+                                    const char *name,
+                                    struct spoolss_DeviceMode *addprinter_devmode)
 {
        union spoolss_PrinterInfo info;
        struct spoolss_DeviceMode *devmode;
@@ -2290,6 +2419,17 @@ static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
 
        devmode = info.info8.devmode;
 
+       if (devmode && devmode->size == 0) {
+               torture_fail(tctx,
+                       "devmode of zero size!");
+       }
+
+       if (addprinter_devmode) {
+               if (!test_devicemode_equal(tctx, devmode, addprinter_devmode)) {
+                       torture_warning(tctx, "current global DM is != DM provided in addprinter");
+               }
+       }
+
        /* run tests */
 
        ret = test_PrinterInfo_DevModes(tctx, p, handle, name);
@@ -2306,9 +2446,9 @@ static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
        return ret;
 }
 
-static bool test_ClosePrinter(struct torture_context *tctx,
-                             struct dcerpc_binding_handle *b,
-                             struct policy_handle *handle)
+bool test_ClosePrinter(struct torture_context *tctx,
+                      struct dcerpc_binding_handle *b,
+                      struct policy_handle *handle)
 {
        NTSTATUS status;
        struct spoolss_ClosePrinter r;
@@ -2362,7 +2502,7 @@ static bool test_GetForm_args(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "GetForm failed");
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, needed, 4);
 
        if (info_p) {
                *info_p = *r.out.info;
@@ -2429,7 +2569,7 @@ static bool test_EnumForms(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
 
-       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, needed, 4);
 
        if (info_p) {
                *info_p = info;
@@ -2532,18 +2672,21 @@ static bool test_AddForm(struct torture_context *tctx,
                         WERROR expected_result)
 {
        struct spoolss_AddForm r;
+       struct spoolss_AddFormInfoCtr info_ctr;
+
+       info_ctr.level = level;
+       info_ctr.info = *info;
 
        if (level != 1) {
                torture_skip(tctx, "only level 1 supported");
        }
 
        r.in.handle     = handle;
-       r.in.level      = level;
-       r.in.info       = *info;
+       r.in.info_ctr   = &info_ctr;
 
        torture_comment(tctx, "Testing AddForm(%s) level %d, type %d\n",
-               r.in.info.info1->form_name, r.in.level,
-               r.in.info.info1->flags);
+               r.in.info_ctr->info.info1->form_name, level,
+               r.in.info_ctr->info.info1->flags);
 
        torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
                "AddForm failed");
@@ -2571,14 +2714,17 @@ static bool test_SetForm(struct torture_context *tctx,
                         union spoolss_AddFormInfo *info)
 {
        struct spoolss_SetForm r;
+       struct spoolss_AddFormInfoCtr info_ctr;
+
+       info_ctr.level  = level;
+       info_ctr.info   = *info;
 
        r.in.handle     = handle;
        r.in.form_name  = form_name;
-       r.in.level      = level;
-       r.in.info       = *info;
+       r.in.info_ctr   = &info_ctr;
 
        torture_comment(tctx, "Testing SetForm(%s) level %d\n",
-               form_name, r.in.level);
+               form_name, level);
 
        torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetForm_r(b, tctx, &r),
                "SetForm failed");
@@ -2627,21 +2773,31 @@ static bool test_Forms_args(struct torture_context *tctx,
 
        if (winreg_handle && hive_handle && W_ERROR_IS_OK(expected_add_result)) {
 
+               struct spoolss_FormInfo1 i1;
+
                torture_assert(tctx,
                        test_GetForm_winreg(tctx, winreg_handle, hive_handle, TOP_LEVEL_CONTROL_FORMS_KEY, form_name, &w_type, &w_size, &w_length, &w_data),
                        "failed to get form via winreg");
 
+               i1.size.width   = IVAL(w_data, 0);
+               i1.size.height  = IVAL(w_data, 4);
+               i1.area.left    = IVAL(w_data, 8);
+               i1.area.top     = IVAL(w_data, 12);
+               i1.area.right   = IVAL(w_data, 16);
+               i1.area.bottom  = IVAL(w_data, 20);
+               /* skip index here */
+               i1.flags        = IVAL(w_data, 28);
+
                torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");
                torture_assert_int_equal(tctx, w_size, 0x20, "unexpected size");
                torture_assert_int_equal(tctx, w_length, 0x20, "unexpected length");
-               torture_assert_mem_equal(tctx, &w_data[0], &add_info.info1->size.width, 4, "width mismatch");
-               torture_assert_mem_equal(tctx, &w_data[4], &add_info.info1->size.height, 4, "height mismatch");
-               torture_assert_mem_equal(tctx, &w_data[8], &add_info.info1->area.left, 4, "left mismatch");
-               torture_assert_mem_equal(tctx, &w_data[12], &add_info.info1->area.top, 4, "top mismatch");
-               torture_assert_mem_equal(tctx, &w_data[16], &add_info.info1->area.right, 4, "right mismatch");
-               torture_assert_mem_equal(tctx, &w_data[20], &add_info.info1->area.bottom, 4, "bottom mismatch");
-               /* skip index here */
-               torture_assert_mem_equal(tctx, &w_data[28], &add_info.info1->flags, 4, "flags mismatch");
+               torture_assert_int_equal(tctx, i1.size.width, add_info.info1->size.width, "width mismatch");
+               torture_assert_int_equal(tctx, i1.size.height, add_info.info1->size.height, "height mismatch");
+               torture_assert_int_equal(tctx, i1.area.left, add_info.info1->area.left, "left mismatch");
+               torture_assert_int_equal(tctx, i1.area.top, add_info.info1->area.top, "top mismatch");
+               torture_assert_int_equal(tctx, i1.area.right, add_info.info1->area.right, "right mismatch");
+               torture_assert_int_equal(tctx, i1.area.bottom, add_info.info1->area.bottom, "bottom mismatch");
+               torture_assert_int_equal(tctx, i1.flags, add_info.info1->flags, "flags mismatch");
        }
 
        if (!print_server && W_ERROR_IS_OK(expected_add_result)) {
@@ -2658,14 +2814,25 @@ static bool test_Forms_args(struct torture_context *tctx,
                torture_assert_int_equal(tctx, info.info1.flags, add_info.info1->flags, "flags mismatch");
 
                if (winreg_handle && hive_handle) {
-                       torture_assert_mem_equal(tctx, &w_data[0], &info.info1.size.width, 4, "width mismatch");
-                       torture_assert_mem_equal(tctx, &w_data[4], &info.info1.size.height, 4, "height mismatch");
-                       torture_assert_mem_equal(tctx, &w_data[8], &info.info1.area.left, 4, "left mismatch");
-                       torture_assert_mem_equal(tctx, &w_data[12], &info.info1.area.top, 4, "top mismatch");
-                       torture_assert_mem_equal(tctx, &w_data[16], &info.info1.area.right, 4, "right mismatch");
-                       torture_assert_mem_equal(tctx, &w_data[20], &info.info1.area.bottom, 4, "bottom mismatch");
+
+                       struct spoolss_FormInfo1 i1;
+
+                       i1.size.width   = IVAL(w_data, 0);
+                       i1.size.height  = IVAL(w_data, 4);
+                       i1.area.left    = IVAL(w_data, 8);
+                       i1.area.top     = IVAL(w_data, 12);
+                       i1.area.right   = IVAL(w_data, 16);
+                       i1.area.bottom  = IVAL(w_data, 20);
                        /* skip index here */
-                       torture_assert_mem_equal(tctx, &w_data[28], &info.info1.flags, 4, "flags mismatch");
+                       i1.flags        = IVAL(w_data, 28);
+
+                       torture_assert_int_equal(tctx, i1.size.width, info.info1.size.width, "width mismatch");
+                       torture_assert_int_equal(tctx, i1.size.height, info.info1.size.height, "height mismatch");
+                       torture_assert_int_equal(tctx, i1.area.left, info.info1.area.left, "left mismatch");
+                       torture_assert_int_equal(tctx, i1.area.top, info.info1.area.top, "top mismatch");
+                       torture_assert_int_equal(tctx, i1.area.right, info.info1.area.right, "right mismatch");
+                       torture_assert_int_equal(tctx, i1.area.bottom, info.info1.area.bottom, "bottom mismatch");
+                       torture_assert_int_equal(tctx, i1.flags, info.info1.flags, "flags mismatch");
                }
 
                add_info.info1->size.width = 1234;
@@ -2811,13 +2978,17 @@ static bool test_Forms(struct torture_context *tctx,
 }
 
 static bool test_EnumPorts_old(struct torture_context *tctx,
-                              struct dcerpc_pipe *p)
+                              void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
        NTSTATUS status;
        struct spoolss_EnumPorts r;
        uint32_t needed;
        uint32_t count;
        union spoolss_PortInfo *info;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
        r.in.servername = talloc_asprintf(tctx, "\\\\%s",
@@ -2849,16 +3020,20 @@ static bool test_EnumPorts_old(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
 
-       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, 2, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, 2, count, needed, 4);
 
        return true;
 }
 
 static bool test_AddPort(struct torture_context *tctx,
-                        struct dcerpc_pipe *p)
+                        void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
        NTSTATUS status;
        struct spoolss_AddPort r;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
        r.in.server_name = talloc_asprintf(tctx, "\\\\%s",
@@ -2926,7 +3101,7 @@ static bool test_GetJob_args(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
        torture_assert(tctx, r.out.info, "No job info returned");
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, needed, 4);
 
        if (info_p) {
                *info_p = *r.out.info;
@@ -3024,11 +3199,13 @@ static bool test_AddJob(struct torture_context *tctx,
        torture_comment(tctx, "Testing AddJob\n");
 
        status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
+       torture_assert_ntstatus_ok(tctx, status, "AddJob failed");
        torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_LEVEL, "AddJob failed");
 
        r.in.level = 1;
 
        status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
+       torture_assert_ntstatus_ok(tctx, status, "AddJob failed");
        torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAM, "AddJob failed");
 
        return true;
@@ -3075,7 +3252,7 @@ static bool test_EnumJobs_args(struct torture_context *tctx,
                torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
                torture_assert(tctx, info, "No jobs returned");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, needed, 4);
 
        } else {
                torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
@@ -3091,50 +3268,156 @@ static bool test_EnumJobs_args(struct torture_context *tctx,
        return true;
 }
 
-static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
-                                        struct dcerpc_binding_handle *b,
-                                        struct policy_handle *handle,
-                                        uint32_t *job_id)
+static bool test_JobPropertiesEnum(struct torture_context *tctx,
+                                  struct dcerpc_binding_handle *b,
+                                  struct policy_handle *handle,
+                                  uint32_t job_id)
 {
-       NTSTATUS status;
-       struct spoolss_StartDocPrinter s;
-       struct spoolss_DocumentInfo1 info1;
-       struct spoolss_StartPagePrinter sp;
-       struct spoolss_WritePrinter w;
-       struct spoolss_EndPagePrinter ep;
-       struct spoolss_EndDocPrinter e;
-       int i;
-       uint32_t num_written;
-
-       torture_comment(tctx, "Testing StartDocPrinter\n");
+       struct spoolss_RpcEnumJobNamedProperties r;
+       uint32_t pcProperties = 0;
+       struct RPC_PrintNamedProperty *ppProperties = NULL;
 
-       s.in.handle             = handle;
-       s.in.level              = 1;
-       s.in.info.info1         = &info1;
-       s.out.job_id            = job_id;
-       info1.document_name     = "TorturePrintJob";
-       info1.output_file       = NULL;
-       info1.datatype          = "RAW";
+       r.in.hPrinter = handle;
+       r.in.JobId = job_id;
+       r.out.pcProperties = &pcProperties;
+       r.out.ppProperties = &ppProperties;
 
-       status = dcerpc_spoolss_StartDocPrinter_r(b, tctx, &s);
-       torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_StartDocPrinter failed");
-       torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed");
+       torture_comment(tctx, "Testing RpcEnumJobNamedProperties(%d)\n", job_id);
 
-       for (i=1; i < 4; i++) {
-               torture_comment(tctx, "Testing StartPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_RpcEnumJobNamedProperties_r(b, tctx, &r),
+               "spoolss_RpcEnumJobNamedProperties failed");
+       torture_assert_werr_ok(tctx, r.out.result,
+               "spoolss_RpcEnumJobNamedProperties failed");
 
-               sp.in.handle            = handle;
+       return true;
+}
 
-               status = dcerpc_spoolss_StartPagePrinter_r(b, tctx, &sp);
-               torture_assert_ntstatus_ok(tctx, status,
-                                          "dcerpc_spoolss_StartPagePrinter failed");
-               torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed");
+static bool test_JobPropertySet(struct torture_context *tctx,
+                               struct dcerpc_binding_handle *b,
+                               struct policy_handle *handle,
+                               uint32_t job_id,
+                               struct RPC_PrintNamedProperty *property)
+{
+       struct spoolss_RpcSetJobNamedProperty r;
 
-               torture_comment(tctx, "Testing WritePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+       r.in.hPrinter = handle;
+       r.in.JobId = job_id;
+       r.in.pProperty = property;
 
-               w.in.handle             = handle;
-               w.in.data               = data_blob_string_const(talloc_asprintf(tctx,"TortureTestPage: %d\nData\n",i));
-               w.out.num_written       = &num_written;
+       torture_comment(tctx, "Testing RpcSetJobNamedProperty(%d) %s - %d\n",
+               job_id, property->propertyName,
+               property->propertyValue.ePropertyType);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_RpcSetJobNamedProperty_r(b, tctx, &r),
+               "spoolss_RpcSetJobNamedProperty failed");
+       torture_assert_werr_ok(tctx, r.out.result,
+               "spoolss_RpcSetJobNamedProperty failed");
+
+       return true;
+}
+
+static bool test_JobPropertyGetValue(struct torture_context *tctx,
+                                    struct dcerpc_binding_handle *b,
+                                    struct policy_handle *handle,
+                                    uint32_t job_id,
+                                    const char *property_name,
+                                    struct RPC_PrintPropertyValue *value)
+{
+       struct spoolss_RpcGetJobNamedPropertyValue r;
+
+       r.in.hPrinter = handle;
+       r.in.JobId = job_id;
+       r.in.pszName = property_name;
+       r.out.pValue = value;
+
+       torture_comment(tctx, "Testing RpcGetJobNamedPropertyValue(%d) %s\n",
+               job_id, property_name);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_RpcGetJobNamedPropertyValue_r(b, tctx, &r),
+               "spoolss_RpcGetJobNamedPropertyValue failed");
+       torture_assert_werr_ok(tctx, r.out.result,
+               "spoolss_RpcGetJobNamedPropertyValue failed");
+
+       return true;
+}
+
+static bool test_JobPropertyDelete(struct torture_context *tctx,
+                                  struct dcerpc_binding_handle *b,
+                                  struct policy_handle *handle,
+                                  uint32_t job_id,
+                                  const char *property_name)
+{
+       struct spoolss_RpcDeleteJobNamedProperty r;
+
+       r.in.hPrinter = handle;
+       r.in.JobId = job_id;
+       r.in.pszName = property_name;
+
+       torture_comment(tctx, "Testing RpcDeleteJobNamedProperty(%d) %s\n",
+               job_id, property_name);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_RpcDeleteJobNamedProperty_r(b, tctx, &r),
+               "spoolss_RpcDeleteJobNamedProperty failed");
+       torture_assert_werr_ok(tctx, r.out.result,
+               "spoolss_RpcDeleteJobNamedProperty failed");
+
+       return true;
+}
+
+
+static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        struct policy_handle *handle,
+                                        const char *document_name,
+                                        uint32_t *job_id)
+{
+       NTSTATUS status;
+       struct spoolss_StartDocPrinter s;
+       struct spoolss_DocumentInfoCtr info_ctr;
+       struct spoolss_DocumentInfo1 info1;
+       struct spoolss_StartPagePrinter sp;
+       struct spoolss_WritePrinter w;
+       struct spoolss_EndPagePrinter ep;
+       struct spoolss_EndDocPrinter e;
+       int i;
+       uint32_t num_written;
+
+       torture_comment(tctx, "Testing StartDocPrinter\n");
+
+       s.in.handle             = handle;
+       s.in.info_ctr           = &info_ctr;
+       s.out.job_id            = job_id;
+
+       info1.document_name     = document_name;
+       info1.output_file       = NULL;
+       info1.datatype          = "RAW";
+
+       info_ctr.level          = 1;
+       info_ctr.info.info1     = &info1;
+
+       status = dcerpc_spoolss_StartDocPrinter_r(b, tctx, &s);
+       torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_StartDocPrinter failed");
+       torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed");
+
+       for (i=1; i < 4; i++) {
+               torture_comment(tctx, "Testing StartPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+
+               sp.in.handle            = handle;
+
+               status = dcerpc_spoolss_StartPagePrinter_r(b, tctx, &sp);
+               torture_assert_ntstatus_ok(tctx, status,
+                                          "dcerpc_spoolss_StartPagePrinter failed");
+               torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed");
+
+               torture_comment(tctx, "Testing WritePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+
+               w.in.handle             = handle;
+               w.in.data               = data_blob_string_const(talloc_asprintf(tctx,"TortureTestPage: %d\nData\n",i));
+               w.out.num_written       = &num_written;
 
                status = dcerpc_spoolss_WritePrinter_r(b, tctx, &w);
                torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_WritePrinter failed");
@@ -3224,8 +3507,8 @@ static bool test_DoPrintTest_check_jobs(struct torture_context *tctx,
 
                if (strequal(ginfo.info1.document_name, document_name)) {
                        torture_warning(tctx,
-                               talloc_asprintf(tctx, "document_name did *NOT* change from '%s' to '%s'\n",
-                                       document_name, new_document_name));
+                                       "document_name did *NOT* change from '%s' to '%s'\n",
+                                       document_name, new_document_name);
                }
        }
 
@@ -3250,10 +3533,39 @@ static bool test_DoPrintTest(struct torture_context *tctx,
        uint32_t *job_ids;
        int i;
 
+       torture_comment(tctx, "Testing real print operations\n");
+
+       job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+       for (i=0; i < num_jobs; i++) {
+               ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
+       }
+
+       for (i=0; i < num_jobs; i++) {
+               ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
+       }
+
+       if (ret == true) {
+               torture_comment(tctx, "real print operations test succeeded\n\n");
+       }
+
+       return ret;
+}
+
+static bool test_DoPrintTest_extended(struct torture_context *tctx,
+                                     struct dcerpc_binding_handle *b,
+                                     struct policy_handle *handle)
+{
+       bool ret = true;
+       uint32_t num_jobs = 8;
+       uint32_t *job_ids;
+       int i;
+       torture_comment(tctx, "Testing real print operations (extended)\n");
+
        job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
 
        for (i=0; i < num_jobs; i++) {
-               ret &= test_DoPrintTest_add_one_job(tctx, b, handle, &job_ids[i]);
+               ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
        }
 
        ret &= test_DoPrintTest_check_jobs(tctx, b, handle, num_jobs, job_ids);
@@ -3262,9 +3574,177 @@ static bool test_DoPrintTest(struct torture_context *tctx,
                ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
        }
 
+       if (ret == true) {
+               torture_comment(tctx, "real print operations (extended) test succeeded\n\n");
+       }
+
        return ret;
 }
 
+static bool test_JobPrintProperties_equal(struct torture_context *tctx,
+                                         struct RPC_PrintPropertyValue *got,
+                                         struct RPC_PrintNamedProperty *exp)
+{
+       torture_assert_int_equal(tctx,
+                                got->ePropertyType,
+                                exp->propertyValue.ePropertyType,
+                                "ePropertyType");
+
+       switch (exp->propertyValue.ePropertyType) {
+       case kRpcPropertyTypeString:
+               torture_assert_str_equal(tctx,
+                                        got->value.propertyString,
+                                        exp->propertyValue.value.propertyString,
+                                        "propertyString");
+               break;
+       case kRpcPropertyTypeInt32:
+               torture_assert_int_equal(tctx,
+                                        got->value.propertyInt32,
+                                        exp->propertyValue.value.propertyInt32,
+                                        "propertyInt32");
+               break;
+       case kRpcPropertyTypeInt64:
+               torture_assert_u64_equal(tctx,
+                                        got->value.propertyInt64,
+                                        exp->propertyValue.value.propertyInt64,
+                                        "propertyInt64");
+               break;
+       case kRpcPropertyTypeByte:
+               torture_assert_int_equal(tctx,
+                                        got->value.propertyByte,
+                                        exp->propertyValue.value.propertyByte,
+                                        "propertyByte");
+               break;
+       case kRpcPropertyTypeBuffer:
+               torture_assert_int_equal(tctx,
+                                        got->value.propertyBlob.cbBuf,
+                                        exp->propertyValue.value.propertyBlob.cbBuf,
+                                        "propertyBlob.cbBuf");
+               torture_assert_mem_equal(tctx,
+                                        got->value.propertyBlob.pBuf,
+                                        exp->propertyValue.value.propertyBlob.pBuf,
+                                        exp->propertyValue.value.propertyBlob.cbBuf,
+                                        "propertyBlob.pBuf");
+
+               break;
+
+       }
+
+       return true;
+}
+
+static bool test_JobPrintProperties(struct torture_context *tctx,
+                                   struct dcerpc_binding_handle *b,
+                                   struct policy_handle *handle,
+                                   uint32_t job_id)
+{
+       struct RPC_PrintNamedProperty in;
+       struct RPC_PrintPropertyValue out;
+       int i;
+       DATA_BLOB blob = data_blob_string_const("blob");
+       struct {
+               const char *property_name;
+               enum RPC_EPrintPropertyType type;
+               union RPC_PrintPropertyValueUnion value;
+               WERROR expected_result;
+       } tests[] = {
+               {
+                       .property_name                  = "torture_property_string",
+                       .type                           = kRpcPropertyTypeString,
+                       .value.propertyString           = "torture_property_value_string",
+               },{
+                       .property_name                  = "torture_property_int32",
+                       .type                           = kRpcPropertyTypeInt32,
+                       .value.propertyInt32            = 42,
+               },{
+                       .property_name                  = "torture_property_int64",
+                       .type                           = kRpcPropertyTypeInt64,
+                       .value.propertyInt64            = 0xaffe,
+               },{
+                       .property_name                  = "torture_property_byte",
+                       .type                           = kRpcPropertyTypeByte,
+                       .value.propertyByte             = 0xab,
+               },{
+                       .property_name                  = "torture_property_buffer",
+                       .type                           = kRpcPropertyTypeBuffer,
+                       .value.propertyBlob.cbBuf       = blob.length,
+                       .value.propertyBlob.pBuf        = blob.data,
+               }
+       };
+
+       torture_assert(tctx,
+               test_JobPropertiesEnum(tctx, b, handle, job_id),
+               "failed to enum properties");
+
+       for (i=0; i <ARRAY_SIZE(tests); i++) {
+
+               in.propertyName                 = tests[i].property_name;
+               in.propertyValue.ePropertyType  = tests[i].type;
+               in.propertyValue.value          = tests[i].value;
+
+               torture_assert(tctx,
+                       test_JobPropertySet(tctx, b, handle, job_id, &in),
+                       "failed to set property");
+
+               torture_assert(tctx,
+                       test_JobPropertyGetValue(tctx, b, handle, job_id, in.propertyName, &out),
+                       "failed to get property");
+
+               torture_assert(tctx,
+                       test_JobPrintProperties_equal(tctx, &out, &in),
+                       "property unequal");
+
+               torture_assert(tctx,
+                       test_JobPropertiesEnum(tctx, b, handle, job_id),
+                       "failed to enum properties");
+
+               torture_assert(tctx,
+                       test_JobPropertyDelete(tctx, b, handle, job_id, in.propertyName),
+                       "failed to delete job property");
+       }
+
+       torture_assert(tctx,
+               test_JobPropertiesEnum(tctx, b, handle, job_id),
+               "failed to enum properties");
+
+       return true;
+}
+
+static bool test_DoPrintTest_properties(struct torture_context *tctx,
+                                       struct dcerpc_binding_handle *b,
+                                       struct policy_handle *handle)
+{
+       uint32_t num_jobs = 8;
+       uint32_t *job_ids;
+       int i;
+       torture_comment(tctx, "Testing real print operations (properties)\n");
+
+       job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+       for (i=0; i < num_jobs; i++) {
+               torture_assert(tctx,
+                       test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]),
+                       "failed to create print job");
+       }
+
+       for (i=0; i < num_jobs; i++) {
+               torture_assert(tctx,
+                       test_JobPrintProperties(tctx, b, handle, job_ids[i]),
+                       "failed to test job properties");
+       }
+
+
+       for (i=0; i < num_jobs; i++) {
+               torture_assert(tctx,
+                       test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE),
+                       "failed to delete printjob");
+       }
+
+       torture_comment(tctx, "real print operations (properties) test succeeded\n\n");
+
+       return true;
+}
+
 static bool test_PausePrinter(struct torture_context *tctx,
                              struct dcerpc_binding_handle *b,
                              struct policy_handle *handle)
@@ -3331,13 +3811,14 @@ static bool test_ResumePrinter(struct torture_context *tctx,
        return true;
 }
 
-static bool test_GetPrinterData(struct torture_context *tctx,
-                               struct dcerpc_binding_handle *b,
-                               struct policy_handle *handle,
-                               const char *value_name,
-                               enum winreg_Type *type_p,
-                               uint8_t **data_p,
-                               uint32_t *needed_p)
+static bool test_GetPrinterData_checktype(struct torture_context *tctx,
+                                         struct dcerpc_binding_handle *b,
+                                         struct policy_handle *handle,
+                                         const char *value_name,
+                                         enum winreg_Type *expected_type,
+                                         enum winreg_Type *type_p,
+                                         uint8_t **data_p,
+                                         uint32_t *needed_p)
 {
        NTSTATUS status;
        struct spoolss_GetPrinterData r;
@@ -3358,6 +3839,9 @@ static bool test_GetPrinterData(struct torture_context *tctx,
        torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
 
        if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+               if (expected_type) {
+                       torture_assert_int_equal(tctx, type, *expected_type, "unexpected type");
+               }
                r.in.offered = needed;
                r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
                status = dcerpc_spoolss_GetPrinterData_r(b, tctx, &r);
@@ -3367,7 +3851,7 @@ static bool test_GetPrinterData(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, r.out.result,
                talloc_asprintf(tctx, "GetPrinterData(%s) failed", r.in.value_name));
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, lp_iconv_convenience(tctx->lp_ctx), needed, 1);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
 
        if (type_p) {
                *type_p = type;
@@ -3384,14 +3868,27 @@ static bool test_GetPrinterData(struct torture_context *tctx,
        return true;
 }
 
-static bool test_GetPrinterDataEx(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
-                                 struct policy_handle *handle,
-                                 const char *key_name,
-                                 const char *value_name,
-                                 enum winreg_Type *type_p,
-                                 uint8_t **data_p,
-                                 uint32_t *needed_p)
+static bool test_GetPrinterData(struct torture_context *tctx,
+                               struct dcerpc_binding_handle *b,
+                               struct policy_handle *handle,
+                               const char *value_name,
+                               enum winreg_Type *type_p,
+                               uint8_t **data_p,
+                               uint32_t *needed_p)
+{
+       return test_GetPrinterData_checktype(tctx, b, handle, value_name,
+                                            NULL, type_p, data_p, needed_p);
+}
+
+static bool test_GetPrinterDataEx_checktype(struct torture_context *tctx,
+                                           struct dcerpc_pipe *p,
+                                           struct policy_handle *handle,
+                                           const char *key_name,
+                                           const char *value_name,
+                                           enum winreg_Type *expected_type,
+                                           enum winreg_Type *type_p,
+                                           uint8_t **data_p,
+                                           uint32_t *needed_p)
 {
        NTSTATUS status;
        struct spoolss_GetPrinterDataEx r;
@@ -3420,6 +3917,9 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
        }
 
        if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+               if (expected_type) {
+                       torture_assert_int_equal(tctx, type, *expected_type, "unexpected type");
+               }
                r.in.offered = needed;
                r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
                status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r);
@@ -3429,7 +3929,7 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, r.out.result,
                talloc_asprintf(tctx, "GetPrinterDataEx(%s - %s) failed", r.in.key_name, r.in.value_name));
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, lp_iconv_convenience(tctx->lp_ctx), needed, 1);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
 
        if (type_p) {
                *type_p = type;
@@ -3446,6 +3946,19 @@ static bool test_GetPrinterDataEx(struct torture_context *tctx,
        return true;
 }
 
+static bool test_GetPrinterDataEx(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p,
+                                 struct policy_handle *handle,
+                                 const char *key_name,
+                                 const char *value_name,
+                                 enum winreg_Type *type_p,
+                                 uint8_t **data_p,
+                                 uint32_t *needed_p)
+{
+       return test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name,
+                                              NULL, type_p, data_p, needed_p);
+}
+
 static bool test_get_environment(struct torture_context *tctx,
                                 struct dcerpc_binding_handle *b,
                                 struct policy_handle *handle,
@@ -3463,16 +3976,17 @@ static bool test_get_environment(struct torture_context *tctx,
        torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");
 
        blob = data_blob_const(data, needed);
-       *architecture = reg_val_data_string(tctx, lp_iconv_convenience(tctx->lp_ctx), REG_SZ, blob);
+       *architecture = reg_val_data_string(tctx, REG_SZ, blob);
 
        return true;
 }
 
 static bool test_GetPrinterData_list(struct torture_context *tctx,
-                                    struct dcerpc_pipe *p,
-                                    struct policy_handle *handle,
-                                    const char **architecture)
+                                    void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
        const char *list[] = {
                "W3SvcInstalled",
@@ -3496,20 +4010,13 @@ static bool test_GetPrinterData_list(struct torture_context *tctx,
                uint8_t *data, *data_ex;
                uint32_t needed, needed_ex;
 
-               torture_assert(tctx, test_GetPrinterData(tctx, b, handle, list[i], &type, &data, &needed),
+               torture_assert(tctx, test_GetPrinterData(tctx, b, &ctx->server_handle, list[i], &type, &data, &needed),
                        talloc_asprintf(tctx, "GetPrinterData failed on %s\n", list[i]));
-               torture_assert(tctx, test_GetPrinterDataEx(tctx, p, handle, "random_string", list[i], &type_ex, &data_ex, &needed_ex),
+               torture_assert(tctx, test_GetPrinterDataEx(tctx, p, &ctx->server_handle, "random_string", list[i], &type_ex, &data_ex, &needed_ex),
                        talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i]));
                torture_assert_int_equal(tctx, type, type_ex, "type mismatch");
                torture_assert_int_equal(tctx, needed, needed_ex, "needed mismatch");
                torture_assert_mem_equal(tctx, data, data_ex, needed, "data mismatch");
-
-               if (strequal(list[i], "Architecture")) {
-                       if (architecture) {
-                               DATA_BLOB blob = data_blob_const(data, needed);
-                               *architecture = reg_val_data_string(tctx, lp_iconv_convenience(tctx->lp_ctx), REG_SZ, blob);
-                       }
-               }
        }
 
        return true;
@@ -3648,7 +4155,7 @@ static bool test_EnumPrinterDataEx(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDataEx failed");
 
-       CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, lp_iconv_convenience(tctx->lp_ctx), needed, 1);
+       CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, needed, 1);
 
        if (count_p) {
                *count_p = count;
@@ -3689,9 +4196,8 @@ static bool test_EnumPrinterData_consistency(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing EnumPrinterData vs EnumPrinterDataEx consistency\n");
 
-       torture_assert(tctx,
-               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
-                                 "REG_SZ", "torture_data1", &type, &blob), "");
+       torture_assert(tctx, push_reg_sz(tctx, &blob, "torture_data1"), "");
+       type = REG_SZ;
 
        torture_assert(tctx,
                test_SetPrinterData(tctx, b, handle, "torture_value1", type, blob.data, blob.length),
@@ -4085,7 +4591,7 @@ static bool test_winreg_symbolic_link(struct torture_context *tctx,
        torture_assert_int_equal(tctx, w_type, REG_LINK, "unexpected type");
 
        blob = data_blob(w_data, w_size);
-       str = reg_val_data_string(tctx, lp_iconv_convenience(tctx->lp_ctx), REG_SZ, blob);
+       str = reg_val_data_string(tctx, REG_SZ, blob);
 
        torture_assert_str_equal(tctx, str, symlink_destination, "unexpected symlink target string");
 
@@ -4154,7 +4660,7 @@ do {\
                "failed to query winreg");\
        torture_assert_int_equal(tctx, w_type, REG_SZ, "unexpected type");\
        blob = data_blob(w_data, w_size);\
-       str = reg_val_data_string(tctx, lp_iconv_convenience(tctx->lp_ctx), REG_SZ, blob);\
+       str = reg_val_data_string(tctx, REG_SZ, blob);\
        if (w_size == 2 && iname == NULL) {\
                /*torture_comment(tctx, "%s: \"\", %s: (null)\n", #wname, #iname);\ */\
        } else {\
@@ -4182,6 +4688,23 @@ do {\
                talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\
 } while(0);
 
+#define test_binary(wname, iname) \
+do {\
+       enum winreg_Type w_type;\
+       uint32_t w_size;\
+       uint32_t w_length;\
+       uint8_t *w_data;\
+       torture_assert(tctx,\
+               test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+                                      &w_type, &w_size, &w_length, &w_data),\
+               "failed to query winreg");\
+       torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+       torture_assert_int_equal(tctx, w_size, iname.length, "unexpected length");\
+       torture_assert_mem_equal(tctx, w_data, iname.data, w_size, \
+               "binary unequal");\
+} while(0);
+
+
 #define test_dm(wname, iname) \
 do {\
        DATA_BLOB blob;\
@@ -4197,7 +4720,7 @@ do {\
                "failed to query winreg");\
        torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
        blob = data_blob(w_data, w_size);\
-       ndr_err = ndr_pull_struct_blob(&blob, tctx, lp_iconv_convenience(tctx->lp_ctx), &dm,\
+       ndr_err = ndr_pull_struct_blob(&blob, tctx, &dm,\
                (ndr_pull_flags_fn_t)ndr_pull_spoolss_DeviceMode);\
        torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall dm");\
        torture_assert(tctx, test_devicemode_equal(tctx, &dm, iname),\
@@ -4219,7 +4742,7 @@ do {\
                "failed to query winreg");\
        torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
        blob = data_blob(w_data, w_size);\
-       ndr_err = ndr_pull_struct_blob(&blob, tctx, lp_iconv_convenience(tctx->lp_ctx), &sd,\
+       ndr_err = ndr_pull_struct_blob(&blob, tctx, &sd,\
                (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);\
        torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall sd");\
        torture_assert(tctx, test_security_descriptor_equal(tctx, &sd, iname),\
@@ -4242,7 +4765,7 @@ do {\
        torture_assert_int_equal(tctx, w_type, REG_MULTI_SZ, "unexpected type");\
        blob = data_blob(w_data, w_size);\
        torture_assert(tctx, \
-               pull_reg_multi_sz(tctx, lp_iconv_convenience(tctx->lp_ctx), &blob, &array),\
+               pull_reg_multi_sz(tctx, &blob, &array),\
                "failed to pull multi sz");\
        for (i=0; array[i] != NULL; i++) {\
                torture_assert_str_equal(tctx, array[i], iname[i],\
@@ -4250,7 +4773,6 @@ do {\
        }\
 } while(0);
 
-
        if (!test_winreg_symbolic_link(tctx, winreg_handle, hive_handle,
                                       TOP_LEVEL_CONTROL_PRINTERS_KEY,
                                       "\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers"))
@@ -4258,7 +4780,6 @@ do {\
                torture_warning(tctx, "failed to check for winreg symlink");
        }
 
-
        for (i=0; i < ARRAY_SIZE(keys); i++) {
 
                const char *printer_key;
@@ -4304,28 +4825,68 @@ do {\
        return true;
 }
 
-static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
-                                        struct dcerpc_binding_handle *b,
-                                        struct policy_handle *handle,
-                                        const char *driver_name,
-                                        const char *architecture,
-                                        uint32_t level,
-                                        uint32_t client_major_version,
-                                        uint32_t client_minor_version,
-                                        union spoolss_DriverInfo *info_p,
-                                        WERROR *result);
-
-static const char *strip_path(const char *path)
+static bool test_PrintProcessors(struct torture_context *tctx,
+                                struct dcerpc_binding_handle *b,
+                                const char *environment,
+                                struct dcerpc_binding_handle *winreg_handle,
+                                struct policy_handle *hive_handle)
 {
-       char *p;
+       union spoolss_PrintProcessorInfo *info;
+       uint32_t count;
+       int i;
 
-       if (path == NULL) {
-               return NULL;
-       }
+       torture_comment(tctx, "Testing Print Processor Info and winreg consistency\n");
 
-       p = strrchr(path, '\\');
-       if (p) {
-               return p+1;
+       torture_assert(tctx,
+               test_EnumPrintProcessors_level(tctx, b, environment, 1, &count, &info, WERR_OK),
+               "failed to enum print processors level 1");
+
+       for (i=0; i < count; i++) {
+
+               const char *processor_key;
+               struct policy_handle key_handle;
+
+               processor_key = talloc_asprintf(tctx, "%s\\%s\\Print Processors\\%s",
+                                               TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
+                                               environment,
+                                               info[i].info1.print_processor_name);
+
+               torture_assert(tctx,
+                       test_winreg_OpenKey(tctx, winreg_handle, hive_handle, processor_key, &key_handle), "");
+
+               /* nothing to check in there so far */
+
+               torture_assert(tctx,
+                       test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+       }
+
+       torture_comment(tctx, "Print Processor Info and winreg consistency test succeeded\n\n");
+
+       return true;
+}
+
+static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        struct policy_handle *handle,
+                                        const char *driver_name,
+                                        const char *architecture,
+                                        uint32_t level,
+                                        uint32_t client_major_version,
+                                        uint32_t client_minor_version,
+                                        union spoolss_DriverInfo *info_p,
+                                        WERROR *result);
+
+static const char *strip_path(const char *path)
+{
+       char *p;
+
+       if (path == NULL) {
+               return NULL;
+       }
+
+       p = strrchr(path, '\\');
+       if (p) {
+               return p+1;
        }
 
        return path;
@@ -4350,6 +4911,11 @@ static const char *driver_winreg_date(TALLOC_CTX *mem_ctx, NTTIME nt)
 {
        time_t t;
        struct tm *tm;
+
+       if (nt == 0) {
+               return talloc_strdup(mem_ctx, "01/01/1601");
+       }
+
        t = nt_time_to_unix(nt);
        tm = localtime(&t);
 
@@ -4372,8 +4938,10 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
                                      const char *printer_name,
                                      const char *driver_name,
                                      const char *environment,
+                                     enum spoolss_DriverOSVersion version,
                                      struct dcerpc_binding_handle *winreg_handle,
-                                     struct policy_handle *hive_handle)
+                                     struct policy_handle *hive_handle,
+                                     const char *server_name_slash)
 {
        WERROR result;
        union spoolss_DriverInfo info;
@@ -4397,20 +4965,27 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
        driver_key = talloc_asprintf(tctx, "%s\\%s\\Drivers\\Version-%d\\%s",
                                     TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
                                     environment,
-                                    3,
+                                    version,
                                     driver_name);
 
        torture_assert(tctx,
                test_winreg_OpenKey(tctx, winreg_handle, hive_handle, driver_key, &key_handle),
                "failed to open driver key");
 
-       if (torture_setting_bool(tctx, "samba3", false)) {
-               goto try_level3;
+       if (torture_setting_bool(tctx, "samba3", false) ||
+           torture_setting_bool(tctx, "w2k3", false)) {
+               goto try_level6;
        }
 
-       torture_assert(tctx,
-               test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 8, 3, 0, &info, &result),
-               "failed to get driver info level 8");
+       if (handle) {
+               torture_assert(tctx,
+                       test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 8, version, 0, &info, &result),
+                       "failed to get driver info level 8");
+       } else {
+               torture_assert(tctx,
+                       test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 8, driver_name, &info),
+                       "failed to get driver info level 8");
+       }
 
        if (W_ERROR_EQUAL(result, WERR_INVALID_LEVEL)) {
                goto try_level6;
@@ -4456,9 +5031,15 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
 
  try_level6:
 
-       torture_assert(tctx,
-               test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 6, 3, 0, &info, &result),
-               "failed to get driver info level 6");
+       if (handle) {
+               torture_assert(tctx,
+                       test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 6, version, 0, &info, &result),
+                       "failed to get driver info level 6");
+       } else {
+               torture_assert(tctx,
+                       test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 6, driver_name, &info),
+                       "failed to get driver info level 6");
+       }
 
        driver_path     = strip_path(info.info6.driver_path);
        data_file       = strip_path(info.info6.data_file);
@@ -4474,8 +5055,16 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
        test_sz("Data File",                    data_file);
        test_sz("Datatype",                     info.info6.default_datatype);
        test_sz("Driver",                       driver_path);
-       test_sz("DriverDate",                   driver_date);
-       test_sz("DriverVersion",                driver_version);
+       if (torture_setting_bool(tctx, "w2k3", false)) {
+               DATA_BLOB blob = data_blob_talloc_zero(tctx, 8);
+               push_nttime(blob.data, 0, info.info6.driver_date);
+               test_binary("DriverDate",       blob);
+               SBVAL(blob.data, 0, info.info6.driver_version);
+               test_binary("DriverVersion",    blob);
+       } else {
+               test_sz("DriverDate",           driver_date);
+               test_sz("DriverVersion",        driver_version);
+       }
        test_sz("HardwareID",                   info.info6.hardware_id);
        test_sz("Help File",                    help_file);
        test_sz("Manufacturer",                 info.info6.manufacturer_name);
@@ -4488,11 +5077,15 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
        test_dword("Version",                   info.info6.version);
 /*     test_dword("TempDir",                   ?); */
 
- try_level3:
-
-       torture_assert(tctx,
-               test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 3, 3, 0, &info, &result),
-               "failed to get driver info level 3");
+       if (handle) {
+               torture_assert(tctx,
+                       test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 3, version, 0, &info, &result),
+                       "failed to get driver info level 3");
+       } else {
+               torture_assert(tctx,
+                       test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 3, driver_name, &info),
+                       "failed to get driver info level 3");
+       }
 
        driver_path     = strip_path(info.info3.driver_path);
        data_file       = strip_path(info.info3.data_file);
@@ -4577,21 +5170,20 @@ static bool test_SetPrinterData_matrix(struct torture_context *tctx,
 
        for (i=0; i < ARRAY_SIZE(values); i++) {
 
-               enum winreg_Type type;
+               enum winreg_Type type, expected_type = REG_SZ;
                DATA_BLOB blob;
                uint8_t *data;
                uint32_t needed;
 
-               torture_assert(tctx,
-                       reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
-                                         "REG_SZ", "dog", &type, &blob), "");
+               torture_assert(tctx, push_reg_sz(tctx, &blob, "dog"), "");
+               type = REG_SZ;
 
                torture_assert(tctx,
                        test_SetPrinterData(tctx, b, handle, values[i], REG_SZ, blob.data, blob.length),
                        "SetPrinterData failed");
 
                torture_assert(tctx,
-                       test_GetPrinterData(tctx, b, handle, values[i], &type, &data, &needed),
+                       test_GetPrinterData_checktype(tctx, b, handle, values[i], &expected_type, &type, &data, &needed),
                        "GetPrinterData failed");
 
                torture_assert_int_equal(tctx, type, REG_SZ, "type mismatch");
@@ -4661,142 +5253,74 @@ static bool test_SetPrinterDataEx(struct torture_context *tctx,
        return true;
 }
 
-static bool test_SetPrinterDataEx_matrix(struct torture_context *tctx,
-                                        struct dcerpc_pipe *p,
-                                        struct policy_handle *handle,
-                                        const char *printername,
-                                        struct dcerpc_binding_handle *winreg_handle,
-                                        struct policy_handle *hive_handle)
+static bool test_SetPrinterDataEx_keys(struct torture_context *tctx,
+                                      struct dcerpc_pipe *p,
+                                      struct policy_handle *handle)
 {
        struct dcerpc_binding_handle *b = p->binding_handle;
        const char *value_name = "dog";
        const char *keys[] = {
                "torturedataex",
                "torture data ex",
-#if 0
-       /* FIXME: not working with s3 atm. */
                "torturedataex_with_subkey\\subkey",
                "torturedataex_with_subkey\\subkey:0",
                "torturedataex_with_subkey\\subkey:1",
                "torturedataex_with_subkey\\subkey\\subsubkey",
                "torturedataex_with_subkey\\subkey\\subsubkey:0",
                "torturedataex_with_subkey\\subkey\\subsubkey:1",
-#endif
                "torture,data",
-#if 0
-       /* FIXME: not working with s3 atm. */
-
                "torture,data,ex",
                "torture,data\\ex",
-               "torture\\data,ex"
-#endif
-       };
-       enum winreg_Type types[] = {
-               REG_SZ,
-               REG_MULTI_SZ,
-               REG_DWORD,
-               REG_BINARY
+               "torture\\data,ex",
+               "torture/data",
+               "torture/data ex",
+               "torture/data ex/sub",
+               "torture//data",
+               "torture//data ex",
+               "torture//data ex/sub",
+               "torture//data ex//sub",
        };
-       const char *str = "abcdefghijklmnopqrstuvwxzy";
-       int i, t, s;
-
+       int i;
 
        for (i=0; i < ARRAY_SIZE(keys); i++) {
-       for (t=0; t < ARRAY_SIZE(types); t++) {
-       for (s=0; s < strlen(str); s++) {
 
                char *c;
                const char *key;
                enum winreg_Type type;
-               const char *string = talloc_strndup(tctx, str, s);
-               DATA_BLOB blob = data_blob_string_const(string);
+               DATA_BLOB blob_in, blob_out;
                const char **subkeys;
-               DATA_BLOB data;
-               uint8_t *data_out;
-               uint32_t needed, offered = 0;
                uint32_t ecount;
                struct spoolss_PrinterEnumValues *einfo;
+               uint32_t needed;
 
-               if (types[t] == REG_DWORD) {
-                       s = 0xffff;
-               }
-
-               if (torture_setting_bool(tctx, "samba3", false)) {
-                       if ((types[t] == REG_MULTI_SZ) && s == 0) {
-                               torture_warning(tctx, "samba3 does not handle 4 byte emtpy REG_MULTI_SZ buffers");
-                               continue;
-                       }
-               }
+               blob_in = data_blob_talloc(tctx, NULL, 42);
 
-               switch (types[t]) {
-               case REG_BINARY:
-                       data = blob;
-                       offered = blob.length;
-                       break;
-               case REG_DWORD:
-                       data = data_blob_talloc(tctx, NULL, 4);
-                       SIVAL(data.data, 0, 0x12345678);
-                       offered = 4;
-                       break;
-               case REG_SZ:
-                       torture_assert(tctx,
-                               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
-                                                 "REG_SZ", string, &type, &data), "");
-                       offered = data.length;
-                       /*strlen_m_term(data.string)*2;*/
-                       break;
-               case REG_MULTI_SZ:
-                       torture_assert(tctx,
-                               reg_string_to_val(tctx, lp_iconv_convenience(tctx->lp_ctx),
-                                                 "REG_SZ", string, &type, &data), "");
-                       torture_assert(tctx, data_blob_realloc(tctx, &data, data.length + 2), "");
-                       memset(&data.data[data.length - 2], '\0', 2);
-                       offered = data.length;
-                       break;
-               default:
-                       torture_fail(tctx, talloc_asprintf(tctx, "type %d untested\n", types[t]));
-               }
+               generate_random_buffer(blob_in.data, blob_in.length);
 
                torture_assert(tctx,
-                       test_SetPrinterDataEx(tctx, b, handle, keys[i], value_name, types[t], data.data, offered),
+                       test_SetPrinterDataEx(tctx, b, handle, keys[i], value_name, REG_BINARY, blob_in.data, blob_in.length),
                        "failed to call SetPrinterDataEx");
 
                torture_assert(tctx,
-                       test_GetPrinterDataEx(tctx, p, handle, keys[i], value_name, &type, &data_out, &needed),
+                       test_GetPrinterDataEx(tctx, p, handle, keys[i], value_name, &type, &blob_out.data, &needed),
                        "failed to call GetPrinterDataEx");
 
+               blob_out.length = needed;
                torture_assert(tctx,
                        test_EnumPrinterDataEx(tctx, b, handle, keys[i], &ecount, &einfo),
                        "failed to call EnumPrinterDataEx");
 
-               torture_assert_int_equal(tctx, types[t], type, "type mismatch");
-               torture_assert_int_equal(tctx, needed, offered, "size mismatch");
-               torture_assert_mem_equal(tctx, data_out, data.data, offered, "buffer mismatch");
+               torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch");
+               torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch");
+               torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch");
 
                torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
                torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch");
                torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len");
-               torture_assert_int_equal(tctx, einfo[0].type, types[t], "type mismatch");
-               torture_assert_int_equal(tctx, einfo[0].data_length, offered, "size mismatch");
+               torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch");
+               torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch");
                if (einfo[0].data_length > 0) {
-                       torture_assert_mem_equal(tctx, einfo[0].data->data, data.data, offered, "buffer mismatch");
-               }
-
-               if (winreg_handle && hive_handle) {
-                       enum winreg_Type w_type;
-                       uint32_t w_size;
-                       uint32_t w_length;
-                       uint8_t *w_data;
-
-                       torture_assert(tctx,
-                               test_winreg_query_printerdata(tctx, winreg_handle, hive_handle,
-                                       printername, keys[i], value_name,
-                                       &w_type, &w_size, &w_length, &w_data), "");
-
-                       torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch");
-                       torture_assert_int_equal(tctx, w_size, offered, "winreg size mismatch");
-                       torture_assert_int_equal(tctx, w_length, offered, "winreg length mismatch");
-                       torture_assert_mem_equal(tctx, w_data, data.data, offered, "winreg buffer mismatch");
+                       torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch");
                }
 
                key = talloc_strdup(tctx, keys[i]);
@@ -4836,100 +5360,202 @@ static bool test_SetPrinterDataEx_matrix(struct torture_context *tctx,
                        }
                }
        }
-       }
-       }
 
        return true;
 }
 
-static bool test_PrinterData_winreg(struct torture_context *tctx,
-                                   struct dcerpc_pipe *p,
-                                   struct policy_handle *handle,
-                                   const char *printer_name)
+static bool test_SetPrinterDataEx_values(struct torture_context *tctx,
+                                        struct dcerpc_pipe *p,
+                                        struct policy_handle *handle)
 {
        struct dcerpc_binding_handle *b = p->binding_handle;
-       struct dcerpc_pipe *p2;
-       bool ret = true;
-       struct policy_handle hive_handle;
-       struct dcerpc_binding_handle *b2;
-
-       torture_assert_ntstatus_ok(tctx,
-               torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
-               "could not open winreg pipe");
-       b2 = p2->binding_handle;
+       const char *key = "torturedataex";
+       const char *values[] = {
+               "torture_value",
+               "torture value",
+               "torture,value",
+               "torture/value",
+               "torture\\value",
+               "torture\\\\value"
+       };
+       int i;
 
-       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+       for (i=0; i < ARRAY_SIZE(values); i++) {
 
-       ret &= test_SetPrinterData_matrix(tctx, b, handle, printer_name, b2, &hive_handle);
-       ret &= test_SetPrinterDataEx_matrix(tctx, p, handle, printer_name, b2, &hive_handle);
+               enum winreg_Type type;
+               DATA_BLOB blob_in, blob_out;
+               uint32_t ecount;
+               struct spoolss_PrinterEnumValues *einfo;
+               uint32_t needed;
 
-       test_winreg_CloseKey(tctx, b2, &hive_handle);
+               if (torture_setting_bool(tctx, "samba3", false)) {
+                       char *q;
+                       q = strrchr(values[i], ',');
+                       if (q) {
+                               torture_comment(tctx, "skipping valuename '%s' including ',' character against Samba3\n",
+                                               values[i]);
+                               continue;
+                       }
+               }
 
-       talloc_free(p2);
+               blob_in = data_blob_talloc(tctx, NULL, 42);
 
-       return ret;
-}
+               generate_random_buffer(blob_in.data, blob_in.length);
 
-static bool test_Forms_winreg(struct torture_context *tctx,
-                             struct dcerpc_binding_handle *b,
-                             struct policy_handle *handle,
-                             bool print_server,
-                             const char *printer_name)
-{
-       struct dcerpc_pipe *p2;
-       bool ret = true;
-       struct policy_handle hive_handle;
-       struct dcerpc_binding_handle *b2;
+               torture_assert(tctx,
+                       test_SetPrinterDataEx(tctx, b, handle, key, values[i], REG_BINARY, blob_in.data, blob_in.length),
+                       "failed to call SetPrinterDataEx");
 
-       torture_assert_ntstatus_ok(tctx,
-               torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
-               "could not open winreg pipe");
-       b2 = p2->binding_handle;
+               torture_assert(tctx,
+                       test_GetPrinterDataEx(tctx, p, handle, key, values[i], &type, &blob_out.data, &needed),
+                       "failed to call GetPrinterDataEx");
 
-       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+               blob_out.length = needed;
+               torture_assert(tctx,
+                       test_EnumPrinterDataEx(tctx, b, handle, key, &ecount, &einfo),
+                       "failed to call EnumPrinterDataEx");
 
-       ret = test_Forms(tctx, b, handle, print_server, printer_name, b2, &hive_handle);
+               torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch");
+               torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch");
+               torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch");
 
-       test_winreg_CloseKey(tctx, b2, &hive_handle);
+               torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+               torture_assert_str_equal(tctx, einfo[0].value_name, values[i], "value_name mismatch");
+               torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(values[i])*2, "unexpected value_name_len");
+               torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch");
+               torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch");
+               if (einfo[0].data_length > 0) {
+                       torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch");
+               }
 
-       talloc_free(p2);
+               torture_assert(tctx,
+                       test_DeletePrinterDataEx(tctx, b, handle, key, values[i]),
+                       "failed to call DeletePrinterDataEx");
+       }
 
-       return ret;
+       return true;
 }
 
-static bool test_PrinterInfo_winreg(struct torture_context *tctx,
-                                   struct dcerpc_pipe *p,
-                                   struct policy_handle *handle,
-                                   const char *printer_name)
+
+static bool test_SetPrinterDataEx_matrix(struct torture_context *tctx,
+                                        struct dcerpc_pipe *p,
+                                        struct policy_handle *handle,
+                                        const char *printername,
+                                        struct dcerpc_binding_handle *winreg_handle,
+                                        struct policy_handle *hive_handle)
 {
        struct dcerpc_binding_handle *b = p->binding_handle;
-       struct dcerpc_pipe *p2;
-       bool ret = true;
-       struct policy_handle hive_handle;
-       struct dcerpc_binding_handle *b2;
-
-       torture_assert_ntstatus_ok(tctx,
-               torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
-               "could not open winreg pipe");
-       b2 = p2->binding_handle;
+       const char *value_name = "dog";
+       const char *key_name = "torturedataex";
+       enum winreg_Type types[] = {
+               REG_SZ,
+               REG_MULTI_SZ,
+               REG_DWORD,
+               REG_BINARY
+       };
+       const char *str = "abcdefghi";
+       int t, s;
 
-       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+       for (t=0; t < ARRAY_SIZE(types); t++) {
+       for (s=0; s < strlen(str); s++) {
 
-       ret = test_GetPrinterInfo_winreg(tctx, b, handle, printer_name, b2, &hive_handle);
+               enum winreg_Type type;
+               const char *string = talloc_strndup(tctx, str, s);
+               const char *array[2];
+               DATA_BLOB blob = data_blob_string_const(string);
+               DATA_BLOB data;
+               uint8_t *data_out;
+               uint32_t needed, offered = 0;
+               uint32_t ecount;
+               struct spoolss_PrinterEnumValues *einfo;
 
-       test_winreg_CloseKey(tctx, b2, &hive_handle);
+               array[0] = talloc_strdup(tctx, string);
+               array[1] = NULL;
 
-       talloc_free(p2);
+               if (types[t] == REG_DWORD) {
+                       s = 0xffff;
+               }
 
-       return ret;
+               switch (types[t]) {
+               case REG_BINARY:
+                       data = blob;
+                       offered = blob.length;
+                       break;
+               case REG_DWORD:
+                       data = data_blob_talloc(tctx, NULL, 4);
+                       SIVAL(data.data, 0, 0x12345678);
+                       offered = 4;
+                       break;
+               case REG_SZ:
+                       torture_assert(tctx, push_reg_sz(tctx, &data, string), "");
+                       type = REG_SZ;
+                       offered = data.length;
+                       /*strlen_m_term(data.string)*2;*/
+                       break;
+               case REG_MULTI_SZ:
+                       torture_assert(tctx, push_reg_multi_sz(tctx, &data, array), "");
+                       type = REG_MULTI_SZ;
+                       offered = data.length;
+                       break;
+               default:
+                       torture_fail(tctx, talloc_asprintf(tctx, "type %d untested\n", types[t]));
+               }
+
+               torture_assert(tctx,
+                       test_SetPrinterDataEx(tctx, b, handle, key_name, value_name, types[t], data.data, offered),
+                       "failed to call SetPrinterDataEx");
+
+               torture_assert(tctx,
+                       test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name, &types[t], &type, &data_out, &needed),
+                       "failed to call GetPrinterDataEx");
+
+               torture_assert(tctx,
+                       test_EnumPrinterDataEx(tctx, b, handle, key_name, &ecount, &einfo),
+                       "failed to call EnumPrinterDataEx");
+
+               torture_assert_int_equal(tctx, types[t], type, "type mismatch");
+               torture_assert_int_equal(tctx, needed, offered, "size mismatch");
+               torture_assert_mem_equal(tctx, data_out, data.data, offered, "buffer mismatch");
+
+               torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+               torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch");
+               torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len");
+               torture_assert_int_equal(tctx, einfo[0].type, types[t], "type mismatch");
+               torture_assert_int_equal(tctx, einfo[0].data_length, offered, "size mismatch");
+               if (einfo[0].data_length > 0) {
+                       torture_assert_mem_equal(tctx, einfo[0].data->data, data.data, offered, "buffer mismatch");
+               }
+
+               if (winreg_handle && hive_handle) {
+                       enum winreg_Type w_type;
+                       uint32_t w_size;
+                       uint32_t w_length;
+                       uint8_t *w_data;
+
+                       torture_assert(tctx,
+                               test_winreg_query_printerdata(tctx, winreg_handle, hive_handle,
+                                       printername, key_name, value_name,
+                                       &w_type, &w_size, &w_length, &w_data), "");
+
+                       torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch");
+                       torture_assert_int_equal(tctx, w_size, offered, "winreg size mismatch");
+                       torture_assert_int_equal(tctx, w_length, offered, "winreg length mismatch");
+                       torture_assert_mem_equal(tctx, w_data, data.data, offered, "winreg buffer mismatch");
+               }
+
+               torture_assert(tctx,
+                       test_DeletePrinterDataEx(tctx, b, handle, key_name, value_name),
+                       "failed to call DeletePrinterDataEx");
+       }
+       }
+
+       return true;
 }
 
-static bool test_DriverInfo_winreg(struct torture_context *tctx,
-                                  struct dcerpc_pipe *p,
-                                  struct policy_handle *handle,
-                                  const char *printer_name,
-                                  const char *driver_name,
-                                  const char *environment)
+static bool test_PrinterData_winreg(struct torture_context *tctx,
+                                   struct dcerpc_pipe *p,
+                                   struct policy_handle *handle,
+                                   const char *printer_name)
 {
        struct dcerpc_binding_handle *b = p->binding_handle;
        struct dcerpc_pipe *p2;
@@ -4944,7 +5570,8 @@ static bool test_DriverInfo_winreg(struct torture_context *tctx,
 
        torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
 
-       ret = test_GetDriverInfo_winreg(tctx, b, handle, printer_name, driver_name, environment, b2, &hive_handle);
+       ret &= test_SetPrinterData_matrix(tctx, b, handle, printer_name, b2, &hive_handle);
+       ret &= test_SetPrinterDataEx_matrix(tctx, p, handle, printer_name, b2, &hive_handle);
 
        test_winreg_CloseKey(tctx, b2, &hive_handle);
 
@@ -4953,134 +5580,478 @@ static bool test_DriverInfo_winreg(struct torture_context *tctx,
        return ret;
 }
 
-static bool test_GetChangeID_PrinterData(struct torture_context *tctx,
-                                        struct dcerpc_binding_handle *b,
-                                        struct policy_handle *handle,
-                                        uint32_t *change_id)
+static bool test_Forms_winreg(struct torture_context *tctx,
+                             struct dcerpc_binding_handle *b,
+                             struct policy_handle *handle,
+                             bool print_server,
+                             const char *printer_name)
 {
-       enum winreg_Type type;
-       uint8_t *data;
-       uint32_t needed;
+       struct dcerpc_pipe *p2;
+       bool ret = true;
+       struct policy_handle hive_handle;
+       struct dcerpc_binding_handle *b2;
 
-       torture_assert(tctx,
-               test_GetPrinterData(tctx, b, handle, "ChangeID", &type, &data, &needed),
-               "failed to call GetPrinterData");
+       torture_assert_ntstatus_ok(tctx,
+               torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+               "could not open winreg pipe");
+       b2 = p2->binding_handle;
 
-       torture_assert(tctx, type == REG_DWORD, "unexpected type");
-       torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
 
-       *change_id = IVAL(data, 0);
+       ret = test_Forms(tctx, b, handle, print_server, printer_name, b2, &hive_handle);
 
-       return true;
-}
+       test_winreg_CloseKey(tctx, b2, &hive_handle);
 
-static bool test_GetChangeID_PrinterDataEx(struct torture_context *tctx,
-                                          struct dcerpc_pipe *p,
-                                          struct policy_handle *handle,
-                                          uint32_t *change_id)
-{
-       enum winreg_Type type;
-       uint8_t *data;
-       uint32_t needed;
+       talloc_free(p2);
 
-       torture_assert(tctx,
-               test_GetPrinterDataEx(tctx, p, handle, "PrinterDriverData", "ChangeID", &type, &data, &needed),
-               "failed to call GetPrinterData");
+       return ret;
+}
 
-       torture_assert(tctx, type == REG_DWORD, "unexpected type");
-       torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+static bool test_PrinterInfo_winreg(struct torture_context *tctx,
+                                   struct dcerpc_pipe *p,
+                                   struct policy_handle *handle,
+                                   const char *printer_name)
+{
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       struct dcerpc_pipe *p2;
+       bool ret = true;
+       struct policy_handle hive_handle;
+       struct dcerpc_binding_handle *b2;
 
-       *change_id = IVAL(data, 0);
+       torture_assert_ntstatus_ok(tctx,
+               torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+               "could not open winreg pipe");
+       b2 = p2->binding_handle;
 
-       return true;
-}
+       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
 
-static bool test_GetChangeID_PrinterInfo(struct torture_context *tctx,
-                                        struct dcerpc_binding_handle *b,
-                                        struct policy_handle *handle,
-                                        uint32_t *change_id)
-{
-       union spoolss_PrinterInfo info;
+       ret = test_GetPrinterInfo_winreg(tctx, b, handle, printer_name, b2, &hive_handle);
 
-       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 0, &info),
-               "failed to query Printer level 0");
+       test_winreg_CloseKey(tctx, b2, &hive_handle);
 
-       *change_id = info.info0.change_id;
+       talloc_free(p2);
 
-       return true;
+       return ret;
 }
 
-static bool test_ChangeID(struct torture_context *tctx,
-                         struct dcerpc_pipe *p,
-                         struct policy_handle *handle)
+static bool test_DriverInfo_winreg(struct torture_context *tctx,
+                                  struct dcerpc_pipe *p,
+                                  struct policy_handle *handle,
+                                  const char *printer_name,
+                                  const char *driver_name,
+                                  const char *environment,
+                                  enum spoolss_DriverOSVersion version)
 {
-       uint32_t change_id, change_id_ex, change_id_info;
-       uint32_t change_id2, change_id_ex2, change_id_info2;
-       union spoolss_PrinterInfo info;
-       const char *comment;
        struct dcerpc_binding_handle *b = p->binding_handle;
+       struct dcerpc_pipe *p2;
+       bool ret = true;
+       struct policy_handle hive_handle;
+       struct dcerpc_binding_handle *b2;
 
-       torture_comment(tctx, "Testing ChangeID: id change test #1\n");
+       torture_assert_ntstatus_ok(tctx,
+               torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+               "could not open winreg pipe");
+       b2 = p2->binding_handle;
 
-       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
-               "failed to query for ChangeID");
+       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
 
-       torture_assert_int_equal(tctx, change_id, change_id_ex,
-               "change_ids should all be equal");
-       torture_assert_int_equal(tctx, change_id_ex, change_id_info,
-               "change_ids should all be equal");
+       ret = test_GetDriverInfo_winreg(tctx, b, handle, printer_name, driver_name, environment, version, b2, &hive_handle, NULL);
 
+       test_winreg_CloseKey(tctx, b2, &hive_handle);
 
-       torture_comment(tctx, "Testing ChangeID: id change test #2\n");
+       talloc_free(p2);
 
-       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
-               "failed to query Printer level 2");
-       torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
-               "failed to query for ChangeID");
-       torture_assert_int_equal(tctx, change_id, change_id_ex,
-               "change_id should not have changed");
-       torture_assert_int_equal(tctx, change_id_ex, change_id_info,
-               "change_id should not have changed");
+       return ret;
+}
 
+static bool test_PrintProcessors_winreg(struct torture_context *tctx,
+                                       struct dcerpc_binding_handle *b,
+                                       const char *environment)
+{
+       struct dcerpc_pipe *p2;
+       bool ret = true;
+       struct policy_handle hive_handle;
+       struct dcerpc_binding_handle *b2;
 
-       torture_comment(tctx, "Testing ChangeID: id change test #3\n");
+       torture_assert_ntstatus_ok(tctx,
+               torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+               "could not open winreg pipe");
+       b2 = p2->binding_handle;
 
-       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
-               "failed to query for ChangeID");
-       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
-               "failed to query Printer level 2");
-       comment = talloc_strdup(tctx, info.info2.comment);
+       torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
 
-       {
-               struct spoolss_SetPrinterInfoCtr info_ctr;
-               struct spoolss_DevmodeContainer devmode_ctr;
-               struct sec_desc_buf secdesc_ctr;
-               union spoolss_SetPrinterInfo sinfo;
+       ret = test_PrintProcessors(tctx, b, environment, b2, &hive_handle);
 
-               ZERO_STRUCT(info_ctr);
-               ZERO_STRUCT(devmode_ctr);
-               ZERO_STRUCT(secdesc_ctr);
+       test_winreg_CloseKey(tctx, b2, &hive_handle);
 
+       talloc_free(p2);
 
-               torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
-               sinfo.info2->comment    = "torture_comment";
+       return ret;
+}
 
-               info_ctr.level = 2;
-               info_ctr.info = sinfo;
+static bool test_PrinterData_DsSpooler(struct torture_context *tctx,
+                                      struct dcerpc_pipe *p,
+                                      struct policy_handle *handle,
+                                      const char *printer_name)
+{
+       struct spoolss_SetPrinterInfoCtr info_ctr;
+       struct spoolss_DevmodeContainer devmode_ctr;
+       struct sec_desc_buf secdesc_ctr;
+       union spoolss_SetPrinterInfo sinfo;
+       union spoolss_PrinterInfo info;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *pname;
 
-               torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+       ZERO_STRUCT(info_ctr);
+       ZERO_STRUCT(devmode_ctr);
+       ZERO_STRUCT(secdesc_ctr);
+
+       torture_comment(tctx, "Testing DsSpooler <-> SetPrinter relations\n");
+
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, handle, 2, &info),
+               "failed to query Printer level 2");
+
+       torture_assert(tctx,
+               PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo),
+               "failed to convert");
+
+       info_ctr.level = 2;
+       info_ctr.info = sinfo;
+
+#define TEST_SZ(wname, iname) \
+do {\
+       enum winreg_Type type;\
+       uint8_t *data;\
+       uint32_t needed;\
+       DATA_BLOB blob;\
+       const char *str;\
+       torture_assert(tctx,\
+               test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+               "failed to query");\
+       torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+       blob = data_blob_const(data, needed);\
+       torture_assert(tctx,\
+               pull_reg_sz(tctx, &blob, &str),\
+               "failed to pull REG_SZ");\
+       torture_assert_str_equal(tctx, str, iname, "unexpected result");\
+} while(0);
+
+
+#define TEST_SET_SZ(wname, iname, val) \
+do {\
+       enum winreg_Type type;\
+       uint8_t *data;\
+       uint32_t needed;\
+       DATA_BLOB blob;\
+       const char *str;\
+       sinfo.info2->iname = val;\
+       torture_assert(tctx,\
+               test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+               "failed to call SetPrinter");\
+       torture_assert(tctx,\
+               test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+               "failed to query");\
+       torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+       blob = data_blob_const(data, needed);\
+       torture_assert(tctx,\
+               pull_reg_sz(tctx, &blob, &str),\
+               "failed to pull REG_SZ");\
+       torture_assert_str_equal(tctx, str, val, "unexpected result");\
+} while(0);
+
+#define TEST_SET_DWORD(wname, iname, val) \
+do {\
+       enum winreg_Type type;\
+       uint8_t *data;\
+       uint32_t needed;\
+       uint32_t value;\
+       sinfo.info2->iname = val;\
+       torture_assert(tctx,\
+               test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+               "failed to call SetPrinter");\
+       torture_assert(tctx,\
+               test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+               "failed to query");\
+       torture_assert_int_equal(tctx, type, REG_DWORD, "unexpected type");\
+       torture_assert_int_equal(tctx, needed, 4, "unexpected length");\
+       value = IVAL(data, 0); \
+       torture_assert_int_equal(tctx, value, val, "unexpected result");\
+} while(0);
+
+       TEST_SET_SZ("description", comment, "newval");
+       TEST_SET_SZ("location", location, "newval");
+       TEST_SET_SZ("driverName", drivername, "newval");
+/*     TEST_SET_DWORD("priority", priority, 25); */
+
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, handle, 2, &info),
+               "failed to query Printer level 2");
+
+       TEST_SZ("description", info.info2.comment);
+       TEST_SZ("driverName", info.info2.drivername);
+       TEST_SZ("location", info.info2.location);
+
+       pname = strrchr(info.info2.printername, '\\');
+       if (pname == NULL) {
+               pname = info.info2.printername;
+       } else {
+               pname++;
+       }
+       TEST_SZ("printerName", pname);
+       /* TEST_SZ("printSeparatorFile", info.info2.sepfile); */
+       /* TEST_SZ("printShareName", info.info2.sharename); */
+
+       /* FIXME gd: complete the list */
+
+#undef TEST_SZ
+#undef TEST_SET_SZ
+#undef TEST_DWORD
+
+       torture_comment(tctx, "DsSpooler <-> SetPrinter relations test succeeded\n\n");
+
+       return true;
+}
+
+static bool test_print_processors_winreg(struct torture_context *tctx,
+                                        void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       return test_PrintProcessors_winreg(tctx, b, ctx->environment);
+}
+
+static bool test_AddPrintProcessor(struct torture_context *tctx,
+                                  struct dcerpc_binding_handle *b,
+                                  const char *environment,
+                                  const char *path_name,
+                                  const char *print_processor_name,
+                                  WERROR expected_error)
+{
+       struct spoolss_AddPrintProcessor r;
+
+       r.in.server = NULL;
+       r.in.architecture = environment;
+       r.in.path_name = path_name;
+       r.in.print_processor_name = print_processor_name;
+
+       torture_comment(tctx, "Testing AddPrintProcessor(%s)\n",
+               print_processor_name);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_AddPrintProcessor_r(b, tctx, &r),
+               "spoolss_AddPrintProcessor failed");
+       torture_assert_werr_equal(tctx, r.out.result, expected_error,
+               "spoolss_AddPrintProcessor failed");
+
+       return true;
+}
+
+static bool test_DeletePrintProcessor(struct torture_context *tctx,
+                                     struct dcerpc_binding_handle *b,
+                                     const char *environment,
+                                     const char *print_processor_name,
+                                     WERROR expected_error)
+{
+       struct spoolss_DeletePrintProcessor r;
+
+       r.in.server = NULL;
+       r.in.architecture = environment;
+       r.in.print_processor_name = print_processor_name;
+
+       torture_comment(tctx, "Testing DeletePrintProcessor(%s)\n",
+               print_processor_name);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_DeletePrintProcessor_r(b, tctx, &r),
+               "spoolss_DeletePrintProcessor failed");
+       torture_assert_werr_equal(tctx, r.out.result, expected_error,
+               "spoolss_DeletePrintProcessor failed");
+
+       return true;
+}
+
+static bool test_add_print_processor(struct torture_context *tctx,
+                                    void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       int i;
+
+       struct {
+               const char *environment;
+               const char *path_name;
+               const char *print_processor_name;
+               WERROR expected_add_result;
+               WERROR expected_del_result;
+       } tests[] = {
+               {
+                       .environment            = ctx->environment,
+                       .path_name              = "",
+                       .print_processor_name   = "winprint",
+                       .expected_add_result    = WERR_PRINT_PROCESSOR_ALREADY_INSTALLED,
+                       .expected_del_result    = WERR_CAN_NOT_COMPLETE
+               },{
+                       .environment            = ctx->environment,
+                       .path_name              = "",
+                       .print_processor_name   = "unknown",
+                       .expected_add_result    = WERR_MOD_NOT_FOUND,
+                       .expected_del_result    = WERR_UNKNOWN_PRINTPROCESSOR
+               }
+       };
+
+       for (i=0; i < ARRAY_SIZE(tests); i++) {
+               torture_assert(tctx,
+                       test_AddPrintProcessor(tctx, b,
+                                              tests[i].environment,
+                                              tests[i].path_name,
+                                              tests[i].print_processor_name,
+                                              tests[i].expected_add_result),
+                       "add print processor failed");
+               torture_assert(tctx,
+                       test_DeletePrintProcessor(tctx, b,
+                                                 tests[i].environment,
+                                                 tests[i].print_processor_name,
+                                                 tests[i].expected_del_result),
+                       "delete print processor failed");
+       }
+
+       return true;
+}
+
+static bool test_GetChangeID_PrinterData(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        struct policy_handle *handle,
+                                        uint32_t *change_id)
+{
+       enum winreg_Type type;
+       uint8_t *data;
+       uint32_t needed;
+
+       torture_assert(tctx,
+               test_GetPrinterData(tctx, b, handle, "ChangeID", &type, &data, &needed),
+               "failed to call GetPrinterData");
+
+       torture_assert(tctx, type == REG_DWORD, "unexpected type");
+       torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+
+       *change_id = IVAL(data, 0);
+
+       return true;
+}
+
+static bool test_GetChangeID_PrinterDataEx(struct torture_context *tctx,
+                                          struct dcerpc_pipe *p,
+                                          struct policy_handle *handle,
+                                          uint32_t *change_id)
+{
+       enum winreg_Type type;
+       uint8_t *data;
+       uint32_t needed;
+
+       torture_assert(tctx,
+               test_GetPrinterDataEx(tctx, p, handle, "PrinterDriverData", "ChangeID", &type, &data, &needed),
+               "failed to call GetPrinterData");
+
+       torture_assert(tctx, type == REG_DWORD, "unexpected type");
+       torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+
+       *change_id = IVAL(data, 0);
+
+       return true;
+}
+
+static bool test_GetChangeID_PrinterInfo(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        struct policy_handle *handle,
+                                        uint32_t *change_id)
+{
+       union spoolss_PrinterInfo info;
+
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 0, &info),
+               "failed to query Printer level 0");
+
+       *change_id = info.info0.change_id;
+
+       return true;
+}
+
+static bool test_ChangeID(struct torture_context *tctx,
+                         struct dcerpc_pipe *p,
+                         struct policy_handle *handle)
+{
+       uint32_t change_id, change_id_ex, change_id_info;
+       uint32_t change_id2, change_id_ex2, change_id_info2;
+       union spoolss_PrinterInfo info;
+       const char *comment;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       torture_comment(tctx, "Testing ChangeID: id change test #1\n");
+
+       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+               "failed to query for ChangeID");
+       torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+               "failed to query for ChangeID");
+       torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+               "failed to query for ChangeID");
+
+       torture_assert_int_equal(tctx, change_id, change_id_ex,
+               "change_ids should all be equal");
+       torture_assert_int_equal(tctx, change_id_ex, change_id_info,
+               "change_ids should all be equal");
+
+
+       torture_comment(tctx, "Testing ChangeID: id change test #2\n");
+
+       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+               "failed to query for ChangeID");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+               "failed to query Printer level 2");
+       torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+               "failed to query for ChangeID");
+       torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+               "failed to query for ChangeID");
+       torture_assert_int_equal(tctx, change_id, change_id_ex,
+               "change_id should not have changed");
+       torture_assert_int_equal(tctx, change_id_ex, change_id_info,
+               "change_id should not have changed");
+
+
+       torture_comment(tctx, "Testing ChangeID: id change test #3\n");
+
+       torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+               "failed to query for ChangeID");
+       torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+               "failed to query for ChangeID");
+       torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+               "failed to query for ChangeID");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+               "failed to query Printer level 2");
+       comment = talloc_strdup(tctx, info.info2.comment);
+
+       {
+               struct spoolss_SetPrinterInfoCtr info_ctr;
+               struct spoolss_DevmodeContainer devmode_ctr;
+               struct sec_desc_buf secdesc_ctr;
+               union spoolss_SetPrinterInfo sinfo;
+
+               ZERO_STRUCT(info_ctr);
+               ZERO_STRUCT(devmode_ctr);
+               ZERO_STRUCT(secdesc_ctr);
+
+
+               torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+               sinfo.info2->comment    = "torture_comment";
+
+               info_ctr.level = 2;
+               info_ctr.info = sinfo;
+
+               torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
                        "failed to call SetPrinter");
 
                sinfo.info2->comment    = comment;
@@ -5184,8 +6155,8 @@ static bool test_OpenPrinter_badname(struct torture_context *tctx,
        opEx.in.datatype                = NULL;
        opEx.in.devmode_ctr.devmode     = NULL;
        opEx.in.access_mask             = 0;
-       opEx.in.level                   = 1;
-       opEx.in.userlevel.level1        = NULL;
+       opEx.in.userlevel_ctr.level             = 1;
+       opEx.in.userlevel_ctr.user_info.level1 = NULL;
        opEx.out.handle                 = &handle;
 
        torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername);
@@ -5203,9 +6174,11 @@ static bool test_OpenPrinter_badname(struct torture_context *tctx,
 }
 
 static bool test_OpenPrinter_badname_list(struct torture_context *tctx,
-                                         struct dcerpc_binding_handle *b,
-                                         const char *server_name)
+                                         void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
        const char *badnames[] = {
                "__INVALID_PRINTER__",
                "\\\\__INVALID_HOST__",
@@ -5214,6 +6187,9 @@ static bool test_OpenPrinter_badname_list(struct torture_context *tctx,
                "\\\\\\__INVALID_PRINTER__"
        };
        const char *badname;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       const char *server_name = dcerpc_server_name(p);
+       struct dcerpc_binding_handle *b = p->binding_handle;
        int i;
 
        for (i=0; i < ARRAY_SIZE(badnames); i++) {
@@ -5238,7 +6214,8 @@ static bool test_OpenPrinter_badname_list(struct torture_context *tctx,
 static bool test_OpenPrinter(struct torture_context *tctx,
                             struct dcerpc_pipe *p,
                             const char *name,
-                            const char *environment)
+                            const char *environment,
+                            bool open_only)
 {
        NTSTATUS status;
        struct spoolss_OpenPrinter r;
@@ -5246,7 +6223,7 @@ static bool test_OpenPrinter(struct torture_context *tctx,
        bool ret = true;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
-       r.in.printername        = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), name);
+       r.in.printername        = name;
        r.in.datatype           = NULL;
        r.in.devmode_ctr.devmode= NULL;
        r.in.access_mask        = SEC_FLAG_MAXIMUM_ALLOWED;
@@ -5260,6 +6237,10 @@ static bool test_OpenPrinter(struct torture_context *tctx,
 
        torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed");
 
+       if (open_only) {
+               goto close_printer;
+       }
+
        if (!test_GetPrinter(tctx, b, &handle, environment)) {
                ret = false;
        }
@@ -5270,6 +6251,7 @@ static bool test_OpenPrinter(struct torture_context *tctx,
                }
        }
 
+ close_printer:
        if (!test_ClosePrinter(tctx, b, &handle)) {
                ret = false;
        }
@@ -5277,32 +6259,47 @@ static bool test_OpenPrinter(struct torture_context *tctx,
        return ret;
 }
 
+static bool test_OpenPrinterEx(struct torture_context *tctx,
+                              struct dcerpc_binding_handle *b,
+                              const char *printername,
+                              const char *datatype,
+                              struct spoolss_DeviceMode *devmode,
+                              uint32_t access_mask,
+                              struct spoolss_UserLevelCtr *userlevel_ctr,
+                              struct policy_handle *handle,
+                              WERROR expected_result)
+{
+       struct spoolss_OpenPrinterEx r;
+
+       r.in.printername        = printername;
+       r.in.datatype           = datatype;
+       r.in.devmode_ctr.devmode= devmode;
+       r.in.access_mask        = access_mask;
+       r.in.userlevel_ctr      = *userlevel_ctr;
+       r.out.handle            = handle;
+
+       torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r),
+               "OpenPrinterEx failed");
+
+       torture_assert_werr_equal(tctx, r.out.result, expected_result,
+               "OpenPrinterEx failed");
+
+       return true;
+}
+
 static bool call_OpenPrinterEx(struct torture_context *tctx,
                               struct dcerpc_pipe *p,
                               const char *name,
                               struct spoolss_DeviceMode *devmode,
                               struct policy_handle *handle)
 {
-       struct spoolss_OpenPrinterEx r;
+       struct spoolss_UserLevelCtr userlevel_ctr;
        struct spoolss_UserLevel1 userlevel1;
-       NTSTATUS status;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
-       if (name && name[0]) {
-               r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s",
-                                                  dcerpc_server_name(p), name);
-       } else {
-               r.in.printername = talloc_asprintf(tctx, "\\\\%s",
-                                                  dcerpc_server_name(p));
-       }
-
-       r.in.datatype           = NULL;
-       r.in.devmode_ctr.devmode= devmode;
-       r.in.access_mask        = SEC_FLAG_MAXIMUM_ALLOWED;
-       r.in.level              = 1;
-       r.in.userlevel.level1   = &userlevel1;
-       r.out.handle = handle;
-
        userlevel1.size = 1234;
        userlevel1.client = "hello";
        userlevel1.user = "spottyfoot!";
@@ -5311,22 +6308,23 @@ static bool call_OpenPrinterEx(struct torture_context *tctx,
        userlevel1.minor = 3;
        userlevel1.processor = 4;
 
-       torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
-
-       status = dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r);
-
-       torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
-
-       torture_assert_werr_ok(tctx, r.out.result, "OpenPrinterEx failed");
+       userlevel_ctr.level = 1;
+       userlevel_ctr.user_info.level1 = &userlevel1;
 
-       return true;
+       return test_OpenPrinterEx(tctx, b, name, NULL, devmode,
+                                 SEC_FLAG_MAXIMUM_ALLOWED,
+                                 &userlevel_ctr,
+                                 handle,
+                                 WERR_OK);
 }
 
 static bool test_printer_rename(struct torture_context *tctx,
-                               struct dcerpc_pipe *p,
-                               struct policy_handle *handle,
-                               const char *name)
+                               void *private_data)
 {
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+
        bool ret = true;
        union spoolss_PrinterInfo info;
        union spoolss_SetPrinterInfo sinfo;
@@ -5346,7 +6344,7 @@ static bool test_printer_rename(struct torture_context *tctx,
        torture_comment(tctx, "Testing Printer rename operations\n");
 
        torture_assert(tctx,
-               test_GetPrinter_level(tctx, b, handle, 2, &info),
+               test_GetPrinter_level(tctx, b, &t->handle, 2, &info),
                "failed to call GetPrinter level 2");
 
        printer_name_orig = talloc_strdup(tctx, info.info2.printername);
@@ -5366,11 +6364,11 @@ static bool test_printer_rename(struct torture_context *tctx,
        info_ctr.info = sinfo;
 
        torture_assert(tctx,
-               test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+               test_SetPrinter(tctx, b, &t->handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
                "failed to call SetPrinter level 2");
 
        torture_assert(tctx,
-               test_GetPrinter_level(tctx, b, handle, 2, &info),
+               test_GetPrinter_level(tctx, b, &t->handle, 2, &info),
                "failed to call GetPrinter level 2");
 
        printer_name = talloc_strdup(tctx, info.info2.printername);
@@ -5403,11 +6401,7 @@ static bool test_printer_rename(struct torture_context *tctx,
                test_GetPrinter_level(tctx, b, &new_handle, 2, &info),
                "failed to call GetPrinter level 2");
 
-       /* FIXME: we openend with servername! */
-       printer_name = talloc_asprintf(tctx, "\\\\%s\\%s",
-               dcerpc_server_name(p), printer_name_new);
-
-       torture_assert_str_equal(tctx, info.info2.printername, printer_name,
+       torture_assert_str_equal(tctx, info.info2.printername, printer_name_new,
                "new printer name was not set");
 
        torture_assert(tctx,
@@ -5419,16 +6413,133 @@ static bool test_printer_rename(struct torture_context *tctx,
        return ret;
 }
 
+static bool test_openprinter(struct torture_context *tctx,
+                            struct dcerpc_binding_handle *b,
+                            const char *real_printername)
+{
+       struct spoolss_UserLevelCtr userlevel_ctr;
+       struct policy_handle handle;
+       struct spoolss_UserLevel1 userlevel1;
+       const char *printername = NULL;
+       int i;
 
-static bool test_OpenPrinterEx(struct torture_context *tctx,
-                              struct dcerpc_pipe *p,
-                              const char *name,
-                              const char *environment)
+       struct {
+               const char *suffix;
+               WERROR expected_result;
+       } tests[] = {
+               {
+                       .suffix                 = "rubbish",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ", LocalOnl",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ", localOnly",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ", localonl",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ",LocalOnl",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ",localOnl2",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ", DrvConver2t",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ", drvconvert",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ",drvconvert",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ", DrvConvert",
+                       .expected_result        = WERR_OK
+               },{
+                       .suffix                 = " , DrvConvert",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ",DrvConvert",
+                       .expected_result        = WERR_OK
+               },{
+                       .suffix                 = ", DrvConvertsadfasdf",
+                       .expected_result        = WERR_OK
+               },{
+                       .suffix                 = ",DrvConvertasdfasd",
+                       .expected_result        = WERR_OK
+               },{
+                       .suffix                 = ", LocalOnly",
+                       .expected_result        = WERR_OK
+               },{
+                       .suffix                 = " , LocalOnly",
+                       .expected_result        = WERR_INVALID_PRINTER_NAME
+               },{
+                       .suffix                 = ",LocalOnly",
+                       .expected_result        = WERR_OK
+               },{
+                       .suffix                 = ", LocalOnlysagi4gjfkd",
+                       .expected_result        = WERR_OK
+               },{
+                       .suffix                 = ",LocalOnlysagi4gjfkd",
+                       .expected_result        = WERR_OK
+               }
+       };
+
+       userlevel1.size = 1234;
+       userlevel1.client = "hello";
+       userlevel1.user = "spottyfoot!";
+       userlevel1.build = 1;
+       userlevel1.major = 2;
+       userlevel1.minor = 3;
+       userlevel1.processor = 4;
+
+       userlevel_ctr.level = 1;
+       userlevel_ctr.user_info.level1 = &userlevel1;
+
+       torture_comment(tctx, "Testing openprinterex printername pattern\n");
+
+       torture_assert(tctx,
+               test_OpenPrinterEx(tctx, b, real_printername, NULL, NULL, 0,
+                                  &userlevel_ctr, &handle,
+                                  WERR_OK),
+               "OpenPrinterEx failed");
+       test_ClosePrinter(tctx, b, &handle);
+
+       for (i=0; i < ARRAY_SIZE(tests); i++) {
+
+               printername = talloc_asprintf(tctx, "%s%s",
+                                             real_printername,
+                                             tests[i].suffix);
+
+               torture_assert(tctx,
+                       test_OpenPrinterEx(tctx, b, printername, NULL, NULL, 0,
+                                          &userlevel_ctr, &handle,
+                                          tests[i].expected_result),
+                       "OpenPrinterEx failed");
+               if (W_ERROR_IS_OK(tests[i].expected_result)) {
+                       test_ClosePrinter(tctx, b, &handle);
+               }
+       }
+
+       return true;
+}
+
+
+static bool test_existing_printer_openprinterex(struct torture_context *tctx,
+                                               struct dcerpc_pipe *p,
+                                               const char *name,
+                                               const char *environment)
 {
        struct policy_handle handle;
        bool ret = true;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
+       if (!test_openprinter(tctx, b, name)) {
+               return false;
+       }
+
        if (!call_OpenPrinterEx(tctx, p, name, NULL, &handle)) {
                return false;
        }
@@ -5465,7 +6576,7 @@ static bool test_OpenPrinterEx(struct torture_context *tctx,
                ret = false;
        }
 
-       if (!test_printer_keys(tctx, b, &handle)) {
+       if (!test_printer_all_keys(tctx, b, &handle)) {
                ret = false;
        }
 
@@ -5503,14 +6614,16 @@ static bool test_OpenPrinterEx(struct torture_context *tctx,
 }
 
 static bool test_EnumPrinters_old(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
-                                 const char *environment)
+                                 void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
        struct spoolss_EnumPrinters r;
        NTSTATUS status;
        uint16_t levels[] = {1, 2, 4, 5};
        int i;
        bool ret = true;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
        for (i=0;i<ARRAY_SIZE(levels);i++) {
@@ -5544,7 +6657,7 @@ static bool test_EnumPrinters_old(struct torture_context *tctx,
 
                torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
 
-               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+               CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
 
                if (!info) {
                        torture_comment(tctx, "No printers returned\n");
@@ -5554,7 +6667,7 @@ static bool test_EnumPrinters_old(struct torture_context *tctx,
                for (j=0;j<count;j++) {
                        if (r.in.level == 1) {
                                char *unc = talloc_strdup(tctx, info[j].info1.name);
-                               char *slash, *name;
+                               char *slash, *name, *full_name;
                                name = unc;
                                if (unc[0] == '\\' && unc[1] == '\\') {
                                        unc +=2;
@@ -5564,10 +6677,18 @@ static bool test_EnumPrinters_old(struct torture_context *tctx,
                                        slash++;
                                        name = slash;
                                }
-                               if (!test_OpenPrinter(tctx, p, name, environment)) {
+                               full_name = talloc_asprintf(tctx, "\\\\%s\\%s",
+                                                           dcerpc_server_name(p), name);
+                               if (!test_OpenPrinter(tctx, p, name, ctx->environment, true)) {
+                                       ret = false;
+                               }
+                               if (!test_OpenPrinter(tctx, p, full_name, ctx->environment, true)) {
+                                       ret = false;
+                               }
+                               if (!test_OpenPrinter(tctx, p, name, ctx->environment, false)) {
                                        ret = false;
                                }
-                               if (!test_OpenPrinterEx(tctx, p, name, environment)) {
+                               if (!test_existing_printer_openprinterex(tctx, p, name, ctx->environment)) {
                                        ret = false;
                                }
                        }
@@ -5577,6 +6698,210 @@ static bool test_EnumPrinters_old(struct torture_context *tctx,
        return ret;
 }
 
+static bool test_EnumPrinters_level(struct torture_context *tctx,
+                                   struct dcerpc_binding_handle *b,
+                                   uint32_t flags,
+                                   const char *servername,
+                                   uint32_t level,
+                                   uint32_t *count_p,
+                                   union spoolss_PrinterInfo **info_p)
+{
+       struct spoolss_EnumPrinters r;
+       union spoolss_PrinterInfo *info;
+       uint32_t needed;
+       uint32_t count;
+
+       r.in.flags      = flags;
+       r.in.server     = servername;
+       r.in.level      = level;
+       r.in.buffer     = NULL;
+       r.in.offered    = 0;
+       r.out.needed    = &needed;
+       r.out.count     = &count;
+       r.out.info      = &info;
+
+       torture_comment(tctx, "Testing EnumPrinters(%s) level %u\n",
+               r.in.server, r.in.level);
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+               "EnumPrinters failed");
+       if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+               DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+               r.in.buffer = &blob;
+               r.in.offered = needed;
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+                       "EnumPrinters failed");
+       }
+
+       torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+       CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
+
+       if (count_p) {
+               *count_p = count;
+       }
+       if (info_p) {
+               *info_p = info;
+       }
+
+       return true;
+}
+
+static const char *get_short_printername(struct torture_context *tctx,
+                                        const char *name)
+{
+       const char *short_name;
+
+       if (name[0] == '\\' && name[1] == '\\') {
+               name += 2;
+               short_name = strchr(name, '\\');
+               if (short_name) {
+                       return talloc_strdup(tctx, short_name+1);
+               }
+       }
+
+       return name;
+}
+
+static const char *get_full_printername(struct torture_context *tctx,
+                                       const char *name)
+{
+       const char *full_name = talloc_strdup(tctx, name);
+       char *p;
+
+       if (name && name[0] == '\\' && name[1] == '\\') {
+               name += 2;
+               p = strchr(name, '\\');
+               if (p) {
+                       return full_name;
+               }
+       }
+
+       return NULL;
+}
+
+static bool test_OnePrinter_servername(struct torture_context *tctx,
+                                      struct dcerpc_pipe *p,
+                                      struct dcerpc_binding_handle *b,
+                                      const char *servername,
+                                      const char *printername)
+{
+       union spoolss_PrinterInfo info;
+       const char *short_name = get_short_printername(tctx, printername);
+       const char *full_name = get_full_printername(tctx, printername);
+
+       if (short_name) {
+               struct policy_handle handle;
+               torture_assert(tctx,
+                       call_OpenPrinterEx(tctx, p, short_name, NULL, &handle),
+                       "failed to open printer");
+
+               torture_assert(tctx,
+                       test_GetPrinter_level(tctx, b, &handle, 2, &info),
+                       "failed to get printer info");
+
+               torture_assert_casestr_equal(tctx, info.info2.servername, NULL,
+                       "unexpected servername");
+               torture_assert_casestr_equal(tctx, info.info2.printername, short_name,
+                       "unexpected printername");
+
+               if (info.info2.devmode) {
+                       const char *expected_devicename;
+                       expected_devicename = talloc_strndup(tctx, short_name, MIN(strlen(short_name), 31));
+                       torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename,
+                               "unexpected devicemode devicename");
+               }
+
+               torture_assert(tctx,
+                       test_ClosePrinter(tctx, b, &handle),
+                       "failed to close printer");
+       }
+
+       if (full_name) {
+               struct policy_handle handle;
+
+               torture_assert(tctx,
+                       call_OpenPrinterEx(tctx, p, full_name, NULL, &handle),
+                       "failed to open printer");
+
+               torture_assert(tctx,
+                       test_GetPrinter_level(tctx, b, &handle, 2, &info),
+                       "failed to get printer info");
+
+               torture_assert_casestr_equal(tctx, info.info2.servername, servername,
+                       "unexpected servername");
+               torture_assert_casestr_equal(tctx, info.info2.printername, full_name,
+                       "unexpected printername");
+
+               if (info.info2.devmode) {
+                       const char *expected_devicename;
+                       expected_devicename = talloc_strndup(tctx, full_name, MIN(strlen(full_name), 31));
+                       torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename,
+                               "unexpected devicemode devicename");
+               }
+
+               torture_assert(tctx,
+                       test_ClosePrinter(tctx, b, &handle),
+                       "failed to close printer");
+       }
+
+       return true;
+}
+
+static bool test_EnumPrinters_servername(struct torture_context *tctx,
+                                        void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       int i;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       uint32_t count;
+       union spoolss_PrinterInfo *info;
+       const char *servername;
+       uint32_t flags = PRINTER_ENUM_NAME|PRINTER_ENUM_LOCAL;
+
+       torture_comment(tctx, "Testing servername behaviour in EnumPrinters and GetPrinters\n");
+
+       servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       torture_assert(tctx,
+               test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info),
+               "failed to enumerate printers");
+
+       for (i=0; i < count; i++) {
+
+               torture_assert_casestr_equal(tctx, info[i].info2.servername, servername,
+                       "unexpected servername");
+
+               torture_assert(tctx,
+                       test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername),
+                       "failed to check printer");
+       }
+
+       servername = "";
+
+       torture_assert(tctx,
+               test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info),
+               "failed to enumerate printers");
+
+       for (i=0; i < count; i++) {
+
+               torture_assert_casestr_equal(tctx, info[i].info2.servername, NULL,
+                       "unexpected servername");
+
+               torture_assert(tctx,
+                       test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername),
+                       "failed to check printer");
+       }
+
+
+       return true;
+}
+
+
 static bool test_GetPrinterDriver(struct torture_context *tctx,
                                  struct dcerpc_binding_handle *b,
                                  struct policy_handle *handle,
@@ -5607,7 +6932,7 @@ static bool test_GetPrinterDriver(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, r.out.result,
                "failed to call GetPrinterDriver");
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
 
        return true;
 }
@@ -5675,7 +7000,7 @@ static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, r.out.result,
                "failed to call GetPrinterDriver2");
 
-       CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+       CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
 
        if (info_p) {
                *info_p = *r.out.info;
@@ -5705,11 +7030,13 @@ static bool test_GetPrinterDriver2(struct torture_context *tctx,
 }
 
 static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
-                                       struct dcerpc_pipe *p,
-                                       const char *environment)
+                                       void *private_data)
 {
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
        uint16_t levels[] = {1, 2, 3, 4, 5, 6};
        int i;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
        const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
 
@@ -5719,7 +7046,7 @@ static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
                union spoolss_DriverInfo *info;
 
                torture_assert(tctx,
-                       test_EnumPrinterDrivers_args(tctx, b, server_name, environment, levels[i], &count, &info),
+                       test_EnumPrinterDrivers_args(tctx, b, server_name, ctx->environment, levels[i], &count, &info),
                        "failed to enumerate drivers");
 
                if (!info) {
@@ -5843,7 +7170,8 @@ static bool test_AddPrinter_wellknown(struct torture_context *tctx,
        ZERO_STRUCT(userlevel_ctr);
        ZERO_STRUCT(info1);
 
-       torture_comment(tctx, "Testing AddPrinter%s level 1\n", ex ? "Ex":"");
+       torture_comment(tctx, "Testing AddPrinter%s(%s) level 1\n",
+                       ex ? "Ex":"", printername);
 
        /* try to add printer to wellknown printer list (level 1) */
 
@@ -5925,6 +7253,7 @@ static bool test_AddPrinter_normal(struct torture_context *tctx,
                                   const char *printername,
                                   const char *drivername,
                                   const char *portname,
+                                  struct spoolss_DeviceMode *devmode,
                                   bool ex)
 {
        WERROR result;
@@ -5944,7 +7273,10 @@ static bool test_AddPrinter_normal(struct torture_context *tctx,
        ZERO_STRUCT(secdesc_ctr);
        ZERO_STRUCT(userlevel_ctr);
 
-       torture_comment(tctx, "Testing AddPrinter%s level 2\n", ex ? "Ex":"");
+       torture_comment(tctx, "Testing AddPrinter%s(%s) level 2\n",
+                       ex ? "Ex":"", printername);
+
+       devmode_ctr.devmode = devmode;
 
        userlevel_ctr.level = 1;
 
@@ -6000,677 +7332,1466 @@ static bool test_AddPrinter_normal(struct torture_context *tctx,
                torture_assert(tctx, test_ClosePrinter(tctx, b, &printer_handle),
                        "failed to close server handle");
 
-               existing_printer_deleted = true;
+               existing_printer_deleted = true;
+
+               goto again;
+       }
+
+       torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PORT,
+               "unexpected result code");
+
+       info2.portname = portname;
+
+       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+                                             dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+               "failed to add printer");
+       result = ex ? rex.out.result : r.out.result;
+       torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTER_DRIVER,
+               "unexpected result code");
+
+       info2.drivername = drivername;
+
+       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+                                             dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+               "failed to add printer");
+       result = ex ? rex.out.result : r.out.result;
+
+       /* w2k8r2 allows to add printer w/o defining printprocessor */
+
+       if (!W_ERROR_IS_OK(result)) {
+               torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTPROCESSOR,
+                       "unexpected result code");
+
+               info2.printprocessor = "winprint";
+
+               torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+                                                     dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+                       "failed to add printer");
+               result = ex ? rex.out.result : r.out.result;
+               torture_assert_werr_ok(tctx, result,
+                       "failed to add printer");
+       }
+
+       *handle_p = handle;
+
+       /* we are paranoid, really check if the printer is there now */
+
+       torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
+                                                       PRINTER_ENUM_LOCAL, 1,
+                                                       printername,
+                                                       &found),
+                       "failed to enum printers");
+       torture_assert(tctx, found, "failed to find newly added printer");
+
+       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+                                             dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+               "failed to add printer");
+       result = ex ? rex.out.result : r.out.result;
+       torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
+               "unexpected result code");
+
+       return true;
+}
+
+static bool test_printer_info(struct torture_context *tctx,
+                             void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       bool ret = true;
+
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skipping printer info cross tests against samba 3");
+       }
+
+       if (!test_PrinterInfo(tctx, b, &t->handle)) {
+               ret = false;
+       }
+
+       if (!test_SetPrinter_errors(tctx, b, &t->handle)) {
+               ret = false;
+       }
+
+       return ret;
+}
+
+static bool test_EnumPrinterKey(struct torture_context *tctx,
+                               struct dcerpc_binding_handle *b,
+                               struct policy_handle *handle,
+                               const char *key_name,
+                               const char ***array)
+{
+       struct spoolss_EnumPrinterKey r;
+       uint32_t needed = 0;
+       union spoolss_KeyNames key_buffer;
+       int32_t offered[] = { 0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, 256, 512, 1024, 2048 };
+       uint32_t _ndr_size;
+       int i;
+
+       r.in.handle = handle;
+       r.in.key_name = key_name;
+       r.out.key_buffer = &key_buffer;
+       r.out.needed = &needed;
+       r.out._ndr_size = &_ndr_size;
+
+       for (i=0; i < ARRAY_SIZE(offered); i++) {
+
+               if (offered[i] < 0 && needed) {
+                       if (needed <= 4) {
+                               continue;
+                       }
+                       r.in.offered = needed + offered[i];
+               } else {
+                       r.in.offered = offered[i];
+               }
+
+               ZERO_STRUCT(key_buffer);
+
+               torture_comment(tctx, "Testing EnumPrinterKey(%s) with %d offered\n", r.in.key_name, r.in.offered);
+
+               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
+                       "failed to call EnumPrinterKey");
+               if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+
+                       torture_assert(tctx, (_ndr_size == r.in.offered/2),
+                               talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
+                                       _ndr_size, r.in.offered/2));
+
+                       r.in.offered = needed;
+                       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
+                               "failed to call EnumPrinterKey");
+               }
+
+               if (offered[i] > 0) {
+                       torture_assert_werr_ok(tctx, r.out.result,
+                               "failed to call EnumPrinterKey");
+               }
+
+               torture_assert(tctx, (_ndr_size == r.in.offered/2),
+                       talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
+                               _ndr_size, r.in.offered/2));
+
+               torture_assert(tctx, (*r.out.needed <= r.in.offered),
+                       talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= offered %d", *r.out.needed, r.in.offered));
+
+               torture_assert(tctx, (*r.out.needed <= _ndr_size * 2),
+                       talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= _ndr_size %d * 2", *r.out.needed, _ndr_size));
+
+               if (key_buffer.string_array) {
+                       uint32_t calc_needed = 0;
+                       int s;
+                       for (s=0; key_buffer.string_array[s]; s++) {
+                               calc_needed += strlen_m_term(key_buffer.string_array[s])*2;
+                       }
+                       if (!key_buffer.string_array[0]) {
+                               calc_needed += 2;
+                       }
+                       calc_needed += 2;
+
+                       torture_assert_int_equal(tctx, *r.out.needed, calc_needed,
+                               "EnumPrinterKey unexpected size");
+               }
+       }
+
+       if (array) {
+               *array = key_buffer.string_array;
+       }
+
+       return true;
+}
+
+bool test_printer_all_keys(struct torture_context *tctx,
+                          struct dcerpc_binding_handle *b,
+                          struct policy_handle *handle)
+{
+       const char **key_array = NULL;
+       int i;
+
+       torture_comment(tctx, "Testing Printer Keys\n");
+
+       torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, "", &key_array),
+               "failed to call test_EnumPrinterKey");
+
+       for (i=0; key_array && key_array[i]; i++) {
+               torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, key_array[i], NULL),
+                       "failed to call test_EnumPrinterKey");
+       }
+       for (i=0; key_array && key_array[i]; i++) {
+               torture_assert(tctx, test_EnumPrinterDataEx(tctx, b, handle, key_array[i], NULL, NULL),
+                       "failed to call test_EnumPrinterDataEx");
+       }
+
+       torture_comment(tctx, "Printer Keys test succeeded\n\n");
+
+       return true;
+}
+
+static bool test_openprinter_wrap(struct torture_context *tctx,
+                                 void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *printername = t->info2.printername;
+
+       return test_openprinter(tctx, b, printername);
+}
+
+static bool test_csetprinter(struct torture_context *tctx,
+                            void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+
+       const char *printername = talloc_asprintf(tctx, "%s2", t->info2.printername);
+       const char *drivername = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+       const char *portname = t->info2.portname;
+
+       union spoolss_PrinterInfo info;
+       struct policy_handle new_handle, new_handle2;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       torture_comment(tctx, "Testing c_setprinter\n");
+
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+               "failed to get level 0 printer info");
+       torture_comment(tctx, "csetprinter on initial printer handle: %d\n",
+               info.info0.c_setprinter);
+
+       /* check if c_setprinter on 1st handle increases after a printer has
+        * been added */
+
+       torture_assert(tctx,
+               test_AddPrinter_normal(tctx, p, &new_handle, printername, drivername, portname, NULL, false),
+               "failed to add new printer");
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+               "failed to get level 0 printer info");
+       torture_comment(tctx, "csetprinter on initial printer handle (after add): %d\n",
+               info.info0.c_setprinter);
+
+       /* check if c_setprinter on new handle increases after a printer has
+        * been added */
+
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, &new_handle, 0, &info),
+               "failed to get level 0 printer info");
+       torture_comment(tctx, "csetprinter on created handle: %d\n",
+               info.info0.c_setprinter);
+
+       /* open the new printer and check if c_setprinter increases */
+
+       torture_assert(tctx,
+               call_OpenPrinterEx(tctx, p, printername, NULL, &new_handle2),
+               "failed to open created printer");
+       torture_assert(tctx,
+               test_GetPrinter_level(tctx, b, &new_handle2, 0, &info),
+               "failed to get level 0 printer info");
+       torture_comment(tctx, "csetprinter on new handle (after openprinter): %d\n",
+               info.info0.c_setprinter);
+
+       /* cleanup */
+
+       torture_assert(tctx,
+               test_ClosePrinter(tctx, b, &new_handle2),
+               "failed to close printer");
+       torture_assert(tctx,
+               test_DeletePrinter(tctx, b, &new_handle),
+               "failed to delete new printer");
+
+       return true;
+}
+
+static bool compose_local_driver_directory(struct torture_context *tctx,
+                                          const char *environment,
+                                          const char *local_dir,
+                                          const char **path)
+{
+       char *p;
+
+       p = strrchr(local_dir, '/');
+       if (!p) {
+               return NULL;
+       }
+       p++;
+
+       if (strequal(environment, "Windows x64")) {
+               if (!strequal(p, "x64")) {
+                       *path = talloc_asprintf(tctx, "%s/x64", local_dir);
+               }
+       } else if (strequal(environment, "Windows NT x86")) {
+               if (!strequal(p, "i386")) {
+                       *path = talloc_asprintf(tctx, "%s/i386", local_dir);
+               }
+       } else {
+               torture_assert(tctx, "unknown environment: '%s'\n", environment);
+       }
+
+       return true;
+}
+
+static struct spoolss_DeviceMode *torture_devicemode(TALLOC_CTX *mem_ctx,
+                                                    const char *devicename)
+{
+       struct spoolss_DeviceMode *r;
+
+       r = talloc_zero(mem_ctx, struct spoolss_DeviceMode);
+       if (r == NULL) {
+               return NULL;
+       }
+
+       r->devicename           = talloc_strdup(r, devicename);
+       r->specversion          = DMSPEC_NT4_AND_ABOVE;
+       r->driverversion        = 0x0600;
+       r->size                 = 0x00dc;
+       r->__driverextra_length = 0;
+       r->fields               = DEVMODE_FORMNAME |
+                                 DEVMODE_TTOPTION |
+                                 DEVMODE_PRINTQUALITY |
+                                 DEVMODE_DEFAULTSOURCE |
+                                 DEVMODE_COPIES |
+                                 DEVMODE_SCALE |
+                                 DEVMODE_PAPERSIZE |
+                                 DEVMODE_ORIENTATION;
+       r->orientation          = DMORIENT_PORTRAIT;
+       r->papersize            = DMPAPER_LETTER;
+       r->paperlength          = 0;
+       r->paperwidth           = 0;
+       r->scale                = 100;
+       r->copies               = 55;
+       r->defaultsource        = DMBIN_FORMSOURCE;
+       r->printquality         = DMRES_HIGH;
+       r->color                = DMRES_MONOCHROME;
+       r->duplex               = DMDUP_SIMPLEX;
+       r->yresolution          = 0;
+       r->ttoption             = DMTT_SUBDEV;
+       r->collate              = DMCOLLATE_FALSE;
+       r->formname             = talloc_strdup(r, "Letter");
+
+       return r;
+}
+
+static bool test_architecture_buffer(struct torture_context *tctx,
+                                    void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+       struct spoolss_OpenPrinterEx r;
+       struct spoolss_UserLevel1 u1;
+       struct policy_handle handle;
+       uint32_t architectures[] = {
+               PROCESSOR_ARCHITECTURE_INTEL,
+               PROCESSOR_ARCHITECTURE_IA64,
+               PROCESSOR_ARCHITECTURE_AMD64
+       };
+       uint32_t needed[3];
+       int i;
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       for (i=0; i < ARRAY_SIZE(architectures); i++) {
+
+               torture_comment(tctx, "Testing OpenPrinterEx with architecture %d\n", architectures[i]);
+
+               u1.size = 0;
+               u1.client = NULL;
+               u1.user = NULL;
+               u1.build = 0;
+               u1.major = 3;
+               u1.minor = 0;
+               u1.processor = architectures[i];
+
+               r.in.printername        = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+               r.in.datatype           = NULL;
+               r.in.devmode_ctr.devmode= NULL;
+               r.in.access_mask        = SEC_FLAG_MAXIMUM_ALLOWED;
+               r.in.userlevel_ctr.level = 1;
+               r.in.userlevel_ctr.user_info.level1 = &u1;
+               r.out.handle            = &handle;
+
+               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r), "");
+               torture_assert_werr_ok(tctx, r.out.result, "");
+
+               {
+                       struct spoolss_EnumPrinters e;
+                       uint32_t count;
+                       union spoolss_PrinterInfo *info;
+
+                       e.in.flags = PRINTER_ENUM_LOCAL;
+                       e.in.server = NULL;
+                       e.in.level = 2;
+                       e.in.buffer = NULL;
+                       e.in.offered = 0;
+                       e.out.count = &count;
+                       e.out.info = &info;
+                       e.out.needed = &needed[i];
+
+                       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e), "");
+#if 0
+                       torture_comment(tctx, "needed was %d\n", needed[i]);
+#endif
+               }
+
+               torture_assert(tctx, test_ClosePrinter(tctx, b, &handle), "");
+       }
+
+       for (i=1; i < ARRAY_SIZE(architectures); i++) {
+               if (needed[i-1] != needed[i]) {
+                       torture_fail(tctx,
+                               talloc_asprintf(tctx, "needed size %d for architecture %d != needed size %d for architecture %d\n",
+                                               needed[i-1], architectures[i-1], needed[i], architectures[i]));
+               }
+       }
+
+       return true;
+}
+
+static bool test_PrintServer_Forms_Winreg(struct torture_context *tctx,
+                                         void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       return test_Forms_winreg(tctx, b, &ctx->server_handle, true, NULL);
+}
+
+static bool test_PrintServer_Forms(struct torture_context *tctx,
+                                  void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       return test_Forms(tctx, b, &ctx->server_handle, true, NULL, NULL, NULL);
+}
+
+static bool test_PrintServer_EnumForms(struct torture_context *tctx,
+                                      void *private_data)
+{
+       struct test_spoolss_context *ctx =
+               talloc_get_type_abort(private_data, struct test_spoolss_context);
+       struct dcerpc_pipe *p = ctx->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+
+       return test_EnumForms_all(tctx, b, &ctx->server_handle, true);
+}
+
+static bool torture_rpc_spoolss_setup_common(struct torture_context *tctx, struct test_spoolss_context *t)
+{
+       NTSTATUS status;
+
+       status = torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss);
+
+       torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+       torture_assert(tctx,
+               test_OpenPrinter_server(tctx, t->spoolss_pipe, &t->server_handle),
+               "failed to open printserver");
+       torture_assert(tctx,
+               test_get_environment(tctx, t->spoolss_pipe->binding_handle, &t->server_handle, &t->environment),
+               "failed to get environment");
+
+       return true;
+}
+
+static bool torture_rpc_spoolss_setup(struct torture_context *tctx, void **data)
+{
+       struct test_spoolss_context *t;
+
+       *data = t = talloc_zero(tctx, struct test_spoolss_context);
+
+       return torture_rpc_spoolss_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_teardown_common(struct torture_context *tctx, struct test_spoolss_context *t)
+{
+       test_ClosePrinter(tctx, t->spoolss_pipe->binding_handle, &t->server_handle);
+
+       return true;
+}
+
+static bool torture_rpc_spoolss_teardown(struct torture_context *tctx, void *data)
+{
+       struct test_spoolss_context *t = talloc_get_type(data, struct test_spoolss_context);
+       bool ret;
+
+       ret = torture_rpc_spoolss_teardown_common(tctx, t);
+       talloc_free(t);
+
+       return ret;
+}
+
+static bool torture_rpc_spoolss_printer_setup_common(struct torture_context *tctx, struct torture_printer_context *t)
+{
+       struct dcerpc_pipe *p;
+       struct dcerpc_binding_handle *b;
+       const char *server_name_slash;
+       const char *driver_name;
+       const char *printer_name;
+       const char *port_name;
+
+       torture_assert_ntstatus_ok(tctx,
+               torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss),
+               "Error connecting to server");
+
+       p = t->spoolss_pipe;
+       b = p->binding_handle;
+       server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       t->driver.info8.version                 = SPOOLSS_DRIVER_VERSION_200X;
+       t->driver.info8.driver_name             = TORTURE_DRIVER;
+       t->driver.info8.driver_path             = "pscript5.dll";
+       t->driver.info8.data_file               = "cups6.ppd";
+       t->driver.info8.config_file             = "ps5ui.dll";
+       t->driver.info8.help_file               = "pscript.hlp";
+       t->driver.info8.default_datatype        = "RAW";
+       t->driver.info8.dependent_files         = talloc_zero(t, struct spoolss_StringArray);
+       t->driver.info8.dependent_files->string = talloc_zero_array(t, const char *, 8 + 1);
+       t->driver.info8.dependent_files->string[0] = "pscript5.dll";
+       t->driver.info8.dependent_files->string[1] = "cups6.ppd";
+       t->driver.info8.dependent_files->string[2] = "ps5ui.dll";
+       t->driver.info8.dependent_files->string[3] = "pscript.hlp";
+       t->driver.info8.dependent_files->string[4] = "pscript.ntf";
+       t->driver.info8.dependent_files->string[5] = "cups6.ini";
+       t->driver.info8.dependent_files->string[6] = "cupsps6.dll";
+       t->driver.info8.dependent_files->string[7] = "cupsui6.dll";
+
+       t->driver.local.driver_directory= "/usr/share/cups/drivers";
+
+       t->info2.portname               = "LPT1:";
+
+       printer_name = t->info2.printername;
+       port_name = t->info2.portname;
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, &t->driver),
+               "failed to fillup printserver info");
+
+       t->driver.info8.architecture = talloc_strdup(t, t->driver.remote.environment);
+
+       torture_assert(tctx,
+               compose_local_driver_directory(tctx, t->driver.remote.environment,
+                                              t->driver.local.driver_directory,
+                                              &t->driver.local.driver_directory),
+               "failed to compose local driver directory");
+
+       t->info2.drivername             = "Microsoft XPS Document Writer";
 
-               goto again;
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) {
+               torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) is present on server\n",
+                       t->info2.drivername, t->driver.remote.environment);
+               t->have_driver = true;
+               goto try_add;
        }
 
-       torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PORT,
-               "unexpected result code");
+       torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) does not exist on the server\n",
+               t->info2.drivername, t->driver.remote.environment);
 
-       info2.portname = portname;
+       t->info2.drivername             = "Microsoft XPS Document Writer v4";
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
-               "failed to add printer");
-       result = ex ? rex.out.result : r.out.result;
-       torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTER_DRIVER,
-               "unexpected result code");
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) {
+               torture_comment(tctx, "driver '%s' (architecture: %s, version: 4) is present on server\n",
+                       t->info2.drivername, t->driver.remote.environment);
+               t->have_driver = true;
+               goto try_add;
+       }
 
-       info2.drivername = drivername;
+       torture_comment(tctx, "trying to upload own driver\n");
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
-               "failed to add printer");
-       result = ex ? rex.out.result : r.out.result;
+       if (!directory_exist(t->driver.local.driver_directory)) {
+               torture_warning(tctx, "no local driver is available!");
+               t->have_driver = false;
+               goto try_add;
+       }
 
-       /* w2k8r2 allows to add printer w/o defining printprocessor */
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), &t->driver),
+               "failed to upload printer driver");
 
-       if (!W_ERROR_IS_OK(result)) {
-               torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTPROCESSOR,
-                       "unexpected result code");
+       torture_assert(tctx,
+               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &t->driver.info8, 0, false, NULL),
+               "failed to add driver");
 
-               info2.printprocessor = "winprint";
+       t->added_driver = true;
+       t->have_driver = true;
 
-               torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
-                                                     dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
-                       "failed to add printer");
-               result = ex ? rex.out.result : r.out.result;
-               torture_assert_werr_ok(tctx, result,
+ try_add:
+       driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+
+       if (t->wellknown) {
+               torture_assert(tctx,
+                       test_AddPrinter_wellknown(tctx, p, printer_name, t->ex),
+                       "failed to add wellknown printer");
+       } else {
+               torture_assert(tctx,
+                       test_AddPrinter_normal(tctx, p, &t->handle, printer_name, driver_name, port_name, t->devmode, t->ex),
                        "failed to add printer");
        }
 
-       *handle_p = handle;
+       return true;
+}
 
-       /* we are paranoid, really check if the printer is there now */
+static bool torture_rpc_spoolss_printer_setup(struct torture_context *tctx, void **data)
+{
+       struct torture_printer_context *t;
 
-       torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
-                                                       PRINTER_ENUM_LOCAL, 1,
-                                                       printername,
-                                                       &found),
-                       "failed to enum printers");
-       torture_assert(tctx, found, "failed to find newly added printer");
+       *data = t = talloc_zero(tctx, struct torture_printer_context);
 
-       torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
-                                             dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
-               "failed to add printer");
-       result = ex ? rex.out.result : r.out.result;
-       torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
-               "unexpected result code");
+       t->ex                   = false;
+       t->wellknown            = false;
+       t->info2.printername    = TORTURE_PRINTER;
+       t->devmode              = NULL;
 
-       return true;
+       return torture_rpc_spoolss_printer_setup_common(tctx, t);
 }
 
-static bool test_printer_info(struct torture_context *tctx,
-                             struct dcerpc_binding_handle *b,
-                             struct policy_handle *handle)
+static bool torture_rpc_spoolss_printerex_setup(struct torture_context *tctx, void **data)
 {
-       bool ret = true;
-
-       if (torture_setting_bool(tctx, "samba3", false)) {
-               torture_skip(tctx, "skipping printer info cross tests against samba 3");
-       }
+       struct torture_printer_context *t;
 
-       if (!test_PrinterInfo(tctx, b, handle)) {
-               ret = false;
-       }
+       *data = t = talloc_zero(tctx, struct torture_printer_context);
 
-       if (!test_SetPrinter_errors(tctx, b, handle)) {
-               ret = false;
-       }
+       t->ex                   = true;
+       t->wellknown            = false;
+       t->info2.printername    = TORTURE_PRINTER_EX;
+       t->devmode              = NULL;
 
-       return ret;
+       return torture_rpc_spoolss_printer_setup_common(tctx, t);
 }
 
-static bool test_EnumPrinterKey(struct torture_context *tctx,
-                               struct dcerpc_binding_handle *b,
-                               struct policy_handle *handle,
-                               const char *key_name,
-                               const char ***array)
+static bool torture_rpc_spoolss_printerwkn_setup(struct torture_context *tctx, void **data)
 {
-       struct spoolss_EnumPrinterKey r;
-       uint32_t needed = 0;
-       union spoolss_KeyNames key_buffer;
-       int32_t offered[] = { 0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, 256, 512, 1024, 2048 };
-       uint32_t _ndr_size;
-       int i;
+       struct torture_printer_context *t;
 
-       r.in.handle = handle;
-       r.in.key_name = key_name;
-       r.out.key_buffer = &key_buffer;
-       r.out.needed = &needed;
-       r.out._ndr_size = &_ndr_size;
+       *data = t = talloc_zero(tctx, struct torture_printer_context);
 
-       for (i=0; i < ARRAY_SIZE(offered); i++) {
+       t->ex                   = false;
+       t->wellknown            = true;
+       t->info2.printername    = TORTURE_WELLKNOWN_PRINTER;
+       t->devmode              = NULL;
 
-               if (offered[i] < 0 && needed) {
-                       if (needed <= 4) {
-                               continue;
-                       }
-                       r.in.offered = needed + offered[i];
-               } else {
-                       r.in.offered = offered[i];
-               }
+       /* FIXME */
+       if (t->wellknown) {
+               torture_skip(tctx, "skipping AddPrinter level 1");
+       }
 
-               ZERO_STRUCT(key_buffer);
+       return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
 
-               torture_comment(tctx, "Testing EnumPrinterKey(%s) with %d offered\n", r.in.key_name, r.in.offered);
+static bool torture_rpc_spoolss_printerexwkn_setup(struct torture_context *tctx, void **data)
+{
+       struct torture_printer_context *t;
 
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
-                       "failed to call EnumPrinterKey");
-               if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+       *data = t = talloc_zero(tctx, struct torture_printer_context);
 
-                       torture_assert(tctx, (_ndr_size == r.in.offered/2),
-                               talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
-                                       _ndr_size, r.in.offered/2));
+       t->ex                   = true;
+       t->wellknown            = true;
+       t->info2.printername    = TORTURE_WELLKNOWN_PRINTER_EX;
+       t->devmode              = NULL;
 
-                       r.in.offered = needed;
-                       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
-                               "failed to call EnumPrinterKey");
-               }
+       /* FIXME */
+       if (t->wellknown) {
+               torture_skip(tctx, "skipping AddPrinterEx level 1");
+       }
 
-               if (offered[i] > 0) {
-                       torture_assert_werr_ok(tctx, r.out.result,
-                               "failed to call EnumPrinterKey");
-               }
+       return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
 
-               torture_assert(tctx, (_ndr_size == r.in.offered/2),
-                       talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
-                               _ndr_size, r.in.offered/2));
+static bool torture_rpc_spoolss_printerdm_setup(struct torture_context *tctx, void **data)
+{
+       struct torture_printer_context *t;
 
-               torture_assert(tctx, (*r.out.needed <= r.in.offered),
-                       talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= offered %d", *r.out.needed, r.in.offered));
+       *data = t = talloc_zero(tctx, struct torture_printer_context);
 
-               torture_assert(tctx, (*r.out.needed <= _ndr_size * 2),
-                       talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= _ndr_size %d * 2", *r.out.needed, _ndr_size));
+       t->ex                   = true;
+       t->wellknown            = false;
+       t->info2.printername    = TORTURE_PRINTER_EX;
+       t->devmode              = torture_devicemode(t, TORTURE_PRINTER_EX);
 
-               if (key_buffer.string_array) {
-                       uint32_t calc_needed = 0;
-                       int s;
-                       for (s=0; key_buffer.string_array[s]; s++) {
-                               calc_needed += strlen_m_term(key_buffer.string_array[s])*2;
-                       }
-                       if (!key_buffer.string_array[0]) {
-                               calc_needed += 2;
-                       }
-                       calc_needed += 2;
+       return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
 
-                       torture_assert_int_equal(tctx, *r.out.needed, calc_needed,
-                               "EnumPrinterKey unexpected size");
-               }
-       }
+static bool torture_rpc_spoolss_printer_teardown_common(struct torture_context *tctx, struct torture_printer_context *t)
+{
+       bool found = false;
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b;
+       const char *printer_name = t->info2.printername;
 
-       if (array) {
-               *array = key_buffer.string_array;
+       if (t->added_driver) {
+               torture_assert(tctx,
+                       remove_printer_driver(tctx, dcerpc_server_name(t->spoolss_pipe), &t->driver),
+                       "failed to remove printer driver");
        }
 
-       return true;
-}
-
-bool test_printer_keys(struct torture_context *tctx,
-                      struct dcerpc_binding_handle *b,
-                      struct policy_handle *handle)
-{
-       const char **key_array = NULL;
-       int i;
+       if (p && !t->wellknown) {
+               b = p->binding_handle;
 
-       torture_comment(tctx, "Testing Printer Keys\n");
+               torture_assert(tctx,
+                       test_DeletePrinter(tctx, b, &t->handle),
+                       "failed to delete printer");
 
-       torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, "", &key_array),
-               "failed to call test_EnumPrinterKey");
+               torture_assert(tctx,
+                       test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1,
+                                                  printer_name, &found),
+                       "failed to enumerate printers");
 
-       for (i=0; key_array && key_array[i]; i++) {
-               torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, key_array[i], NULL),
-                       "failed to call test_EnumPrinterKey");
-       }
-       for (i=0; key_array && key_array[i]; i++) {
-               torture_assert(tctx, test_EnumPrinterDataEx(tctx, b, handle, key_array[i], NULL, NULL),
-                       "failed to call test_EnumPrinterDataEx");
+               torture_assert(tctx, !found, "deleted printer still there");
        }
 
-       torture_comment(tctx, "Printer Keys test succeeded\n\n");
-
        return true;
 }
 
-static bool test_one_printer(struct torture_context *tctx,
-                            struct dcerpc_pipe *p,
-                            struct policy_handle *handle,
-                            const char *name,
-                            const char *drivername,
-                            const char *environment,
-                            bool have_driver)
+static bool torture_rpc_spoolss_printer_teardown(struct torture_context *tctx, void *data)
 {
-       bool ret = true;
-       struct dcerpc_binding_handle *b = p->binding_handle;
-
-       if (!test_PausePrinter(tctx, b, handle)) {
-               ret = false;
-       }
-
-       if (!test_DoPrintTest(tctx, b, handle)) {
-               ret = false;
-       }
-
-       if (!test_ResumePrinter(tctx, b, handle)) {
-               ret = false;
-       }
+       struct torture_printer_context *t = talloc_get_type(data, struct torture_printer_context);
+       bool ret;
 
-       if (!test_printer_info(tctx, b, handle)) {
-               ret = false;
-       }
+       ret = torture_rpc_spoolss_printer_teardown_common(tctx, t);
+       talloc_free(t);
 
-       if (!test_PrinterInfo_SD(tctx, b, handle)) {
-               ret = false;
-       }
+       return ret;
+}
 
-       if (!test_PrinterInfo_DevMode(tctx, p, handle, name)) {
-               ret = false;
-       }
+static bool test_print_test(struct torture_context *tctx,
+                           void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
 
-       if (!test_PrinterInfo_winreg(tctx, p, handle, name)) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               test_PausePrinter(tctx, b, &t->handle),
+               "failed to pause printer");
 
-       if (!test_ChangeID(tctx, p, handle)) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               test_DoPrintTest(tctx, b, &t->handle),
+               "failed to do print test");
 
-       if (!test_printer_keys(tctx, b, handle)) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               test_ResumePrinter(tctx, b, &t->handle),
+               "failed to resume printer");
 
-       if (!test_EnumPrinterData_consistency(tctx, p, handle)) {
-               ret = false;
-       }
+       return true;
+}
 
-       if (!test_SetPrinterDataEx_matrix(tctx, p, handle, name, NULL, NULL)) {
-               ret = false;
-       }
+static bool test_print_test_extended(struct torture_context *tctx,
+                                    void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       bool ret = true;
 
-       if (!test_PrinterData_winreg(tctx, p, handle, name)) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               test_PausePrinter(tctx, b, &t->handle),
+               "failed to pause printer");
 
-       if (have_driver) {
-               if (!test_DriverInfo_winreg(tctx, p, handle, name, drivername, environment)) {
-                       ret = false;
+       ret = test_DoPrintTest_extended(tctx, b, &t->handle);
+       if (ret == false) {
+               torture_comment(tctx, "WARNING! failed to do extended print test\n");
+               if (torture_setting_bool(tctx, "samba3", false)) {
+                       torture_comment(tctx, "non-critical for samba3\n");
+                       ret = true;
+                       tctx->last_result = TORTURE_SKIP;
                }
        }
 
-       if (!test_printer_rename(tctx, p, handle, name)) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               test_ResumePrinter(tctx, b, &t->handle),
+               "failed to resume printer");
 
        return ret;
 }
 
-static bool test_csetprinter(struct torture_context *tctx,
-                            struct dcerpc_pipe *p,
-                            struct policy_handle *handle,
-                            const char *printername,
-                            const char *drivername,
-                            const char *portname)
+static bool test_print_test_properties(struct torture_context *tctx,
+                                      void *private_data)
 {
-       union spoolss_PrinterInfo info;
-       struct policy_handle new_handle, new_handle2;
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
 
-       torture_comment(tctx, "Testing c_setprinter\n");
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skip printer job property tests against samba");
+       }
 
        torture_assert(tctx,
-               test_GetPrinter_level(tctx, b, handle, 0, &info),
-               "failed to get level 0 printer info");
-       torture_comment(tctx, "csetprinter on initial printer handle: %d\n",
-               info.info0.c_setprinter);
-
-       /* check if c_setprinter on 1st handle increases after a printer has
-        * been added */
+               test_PausePrinter(tctx, b, &t->handle),
+               "failed to pause printer");
 
        torture_assert(tctx,
-               test_AddPrinter_normal(tctx, p, &new_handle, printername, drivername, portname, false),
-               "failed to add new printer");
+               test_DoPrintTest_properties(tctx, b, &t->handle),
+               "failed to test print job properties");
+
        torture_assert(tctx,
-               test_GetPrinter_level(tctx, b, handle, 0, &info),
-               "failed to get level 0 printer info");
-       torture_comment(tctx, "csetprinter on initial printer handle (after add): %d\n",
-               info.info0.c_setprinter);
+               test_ResumePrinter(tctx, b, &t->handle),
+               "failed to resume printer");
 
-       /* check if c_setprinter on new handle increases after a printer has
-        * been added */
+       return true;
+}
 
-       torture_assert(tctx,
-               test_GetPrinter_level(tctx, b, &new_handle, 0, &info),
-               "failed to get level 0 printer info");
-       torture_comment(tctx, "csetprinter on created handle: %d\n",
-               info.info0.c_setprinter);
+/* use smbd file IO to spool a print job */
+static bool test_print_test_smbd(struct torture_context *tctx,
+                                void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       NTSTATUS status;
+       uint32_t count;
+       union spoolss_JobInfo *info = NULL;
+       int i;
 
-       /* open the new printer and check if c_setprinter increases */
+       struct smb2_tree *tree;
+       struct smb2_handle job_h;
+       struct cli_credentials *credentials = cmdline_credentials;
+       struct smbcli_options options;
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       /*
+        * Do not test against the dynamically added printers, printing via
+        * smbd means that a different spoolss process may handle the
+        * OpenPrinter request to the one that handled the AddPrinter request.
+        * This currently leads to an ugly race condition where one process
+        * sees the new printer and one doesn't.
+        */
+       const char *share = TORTURE_PRINTER_STATIC1;
+
+       torture_comment(tctx, "Testing smbd job spooling\n");
+       lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+       status = smb2_connect_ext(mem_ctx,
+                                 torture_setting_string(tctx, "host", NULL),
+                                 lpcfg_smb_ports(tctx->lp_ctx),
+                                 share,
+                                 lpcfg_resolve_context(tctx->lp_ctx),
+                                 credentials,
+                                 0,
+                                 &tree,
+                                 tctx->ev,
+                                 &options,
+                                 lpcfg_socket_options(tctx->lp_ctx),
+                                 lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to connect to SMB2 printer %s - %s\n",
+                      share, nt_errstr(status));
+               return false;
+       }
 
-       torture_assert(tctx,
-               call_OpenPrinterEx(tctx, p, printername, NULL, &new_handle2),
-               "failed to open created printer");
-       torture_assert(tctx,
-               test_GetPrinter_level(tctx, b, &new_handle2, 0, &info),
-               "failed to get level 0 printer info");
-       torture_comment(tctx, "csetprinter on new handle (after openprinter): %d\n",
-               info.info0.c_setprinter);
+       status = torture_smb2_testfile(tree, "smbd_spooler_job", &job_h);
+       torture_assert_ntstatus_ok(tctx, status, "smbd spool job create");
 
-       /* cleanup */
+       status = smb2_util_write(tree, job_h, "exciting print job data", 0,
+                                sizeof("exciting print job data"));
+       torture_assert_ntstatus_ok(tctx, status, "smbd spool job write");
 
+       /* check back end spoolss job was created */
        torture_assert(tctx,
-               test_ClosePrinter(tctx, b, &new_handle2),
-               "failed to close printer");
-       torture_assert(tctx,
-               test_DeletePrinter(tctx, b, &new_handle),
-               "failed to delete new printer");
+               test_EnumJobs_args(tctx, b, &t->handle, 1, &count, &info),
+               "EnumJobs level 1 failed");
+
+       for (i = 0; i < count; i++) {
+               if (!strcmp(info[i].info1.document_name, "smbd_spooler_job")) {
+                       break;
+               }
+       }
+       torture_assert(tctx, (i != count), "smbd_spooler_job not found");
+
+       status = smb2_util_close(tree, job_h);
+       torture_assert_ntstatus_ok(tctx, status, "smbd spool job close");
+
+       /* disconnect from printer share */
+       talloc_free(mem_ctx);
 
        return true;
 }
 
-static bool test_add_printer_args_with_driver(struct torture_context *tctx,
-                                             struct dcerpc_pipe *p,
-                                             struct torture_printer_context *t)
+static bool test_printer_sd(struct torture_context *tctx,
+                           void *private_data)
 {
-       bool ret = true;
-       struct policy_handle handle;
-       bool found = false;
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
-       const char *printer_name = t->info2.printername;
-       const char *driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
-       const char *port_name = t->info2.portname;
-       const char *printer_name2 = talloc_asprintf(tctx, "%s2", printer_name);
 
-       if (t->wellknown) {
-               torture_assert(tctx,
-                       test_AddPrinter_wellknown(tctx, p, printer_name, t->ex),
-                       "failed to add wellknown printer");
-       } else {
-               torture_assert(tctx,
-                       test_AddPrinter_normal(tctx, p, &handle, printer_name, driver_name, port_name, t->ex),
-                       "failed to add printer");
-       }
+       torture_assert(tctx,
+               test_PrinterInfo_SD(tctx, b, &t->handle),
+               "failed to test security descriptors");
 
-       if (!test_csetprinter(tctx, p, &handle, printer_name2, driver_name, port_name)) {
-               ret = false;
-       }
+       return true;
+}
 
-       if (!test_one_printer(tctx, p, &handle, printer_name, driver_name, t->driver.remote.environment, t->have_driver)) {
-               ret = false;
-       }
+static bool test_printer_dm(struct torture_context *tctx,
+                           void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
-       if (!test_DeletePrinter(tctx, b, &handle)) {
-               ret = false;
-       }
+       torture_assert(tctx,
+               test_PrinterInfo_DevMode(tctx, p, &t->handle, t->info2.printername, t->devmode),
+               "failed to test devicemodes");
 
-       if (!test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1,
-                                       printer_name, &found)) {
-               ret = false;
-       }
+       return true;
+}
+
+static bool test_printer_info_winreg(struct torture_context *tctx,
+                                    void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
-       torture_assert(tctx, !found, "deleted printer still there");
+       torture_assert(tctx,
+               test_PrinterInfo_winreg(tctx, p, &t->handle, t->info2.printername),
+               "failed to test printer info winreg");
 
-       return ret;
+       return true;
 }
 
-static bool compose_local_driver_directory(struct torture_context *tctx,
-                                          const char *environment,
-                                          const char *local_dir,
-                                          const char **path)
+static bool test_printer_change_id(struct torture_context *tctx,
+                                  void *private_data)
 {
-       char *p;
-
-       p = strrchr(local_dir, '/');
-       if (!p) {
-               return NULL;
-       }
-       p++;
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
-       if (strequal(environment, "Windows x64")) {
-               if (!strequal(p, "x64")) {
-                       *path = talloc_asprintf(tctx, "%s/x64", local_dir);
-               }
-       } else if (strequal(environment, "Windows NT x86")) {
-               if (!strequal(p, "i386")) {
-                       *path = talloc_asprintf(tctx, "%s/i386", local_dir);
-               }
-       } else {
-               torture_assert(tctx, "unknown environment: '%s'\n", environment);
-       }
+       torture_assert(tctx,
+               test_ChangeID(tctx, p, &t->handle),
+               "failed to test change id");
 
        return true;
 }
 
-static bool test_add_printer_args(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
-                                 struct torture_printer_context *t)
+static bool test_printer_keys(struct torture_context *tctx,
+                             void *private_data)
 {
-       bool ret = true;
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
        struct dcerpc_binding_handle *b = p->binding_handle;
-       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
-
-       if (t->wellknown && torture_setting_bool(tctx, "samba3", false)) {
-               torture_skip(tctx, "skipping AddPrinter level 1 against samba");
-       }
 
        torture_assert(tctx,
-               fillup_printserver_info(tctx, p, &t->driver),
-               "failed to fillup printserver info");
+               test_printer_all_keys(tctx, b, &t->handle),
+               "failed to test printer keys");
 
-       t->driver.info8.architecture = talloc_strdup(t, t->driver.remote.environment);
+       return true;
+}
 
-       torture_assert(tctx,
-               compose_local_driver_directory(tctx, t->driver.remote.environment,
-                                              t->driver.local.driver_directory,
-                                              &t->driver.local.driver_directory),
-               "failed to compose local driver directory");
+static bool test_printer_data_consistency(struct torture_context *tctx,
+                                         void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
-       if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername)) {
-               t->have_driver = true;
-               goto try_run;
-       }
+       torture_assert(tctx,
+               test_EnumPrinterData_consistency(tctx, p, &t->handle),
+               "failed to test printer data consistency");
 
-       torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) does not exist on the server\n",
-               t->info2.drivername, t->driver.remote.environment);
-       torture_comment(tctx, "trying to upload own driver\n");
+       return true;
+}
 
-       if (!directory_exist(t->driver.local.driver_directory)) {
-               torture_warning(tctx, "no local driver is available!");
-               t->have_driver = false;
-               goto try_run;
-       }
+static bool test_printer_data_keys(struct torture_context *tctx,
+                                  void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
        torture_assert(tctx,
-               upload_printer_driver(tctx, dcerpc_server_name(p), &t->driver),
-               "failed to upload printer driver");
+               test_SetPrinterDataEx_keys(tctx, p, &t->handle),
+               "failed to test printer data keys");
+
+       return true;
+}
+
+static bool test_printer_data_values(struct torture_context *tctx,
+                                    void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
        torture_assert(tctx,
-               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &t->driver.info8, 0, false),
-               "failed to add driver");
+               test_SetPrinterDataEx_values(tctx, p, &t->handle),
+               "failed to test printer data values");
 
-       t->added_driver = true;
-       t->have_driver = true;
+       return true;
+}
 
- try_run:
-       ret = test_add_printer_args_with_driver(tctx, p, t);
+static bool test_printer_data_set(struct torture_context *tctx,
+                                 void *private_data)
+{
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
-       if (t->added_driver) {
-               torture_assert(tctx,
-                       remove_printer_driver(tctx, dcerpc_server_name(p), &t->driver),
-                       "failed to remove printer driver");
-       }
+       torture_assert(tctx,
+               test_SetPrinterDataEx_matrix(tctx, p, &t->handle, t->info2.printername, NULL, NULL),
+               "failed to test printer data set");
 
-       return ret;
+       return true;
 }
 
-static bool test_add_printer(struct torture_context *tctx,
-                            struct dcerpc_pipe *p,
-                            void *private_data)
+static bool test_printer_data_winreg(struct torture_context *tctx,
+                                    void *private_data)
 {
        struct torture_printer_context *t =
                (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
-       t->ex                   = false;
-       t->wellknown            = false;
-       t->info2.printername    = TORTURE_PRINTER;
+       torture_assert(tctx,
+               test_PrinterData_winreg(tctx, p, &t->handle, t->info2.printername),
+               "failed to test printer data winreg");
 
-       return test_add_printer_args(tctx, p, t);
+       return true;
 }
 
-static bool test_add_printer_wellknown(struct torture_context *tctx,
-                                      struct dcerpc_pipe *p,
-                                      void *private_data)
+static bool test_printer_data_dsspooler(struct torture_context *tctx,
+                                       void *private_data)
 {
        struct torture_printer_context *t =
                (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
 
-       t->ex                   = false;
-       t->wellknown            = true;
-       t->info2.printername    = TORTURE_WELLKNOWN_PRINTER;
+       torture_assert(tctx,
+               test_PrinterData_DsSpooler(tctx, p, &t->handle, t->info2.printername),
+               "failed to test printer data winreg dsspooler");
 
-       return test_add_printer_args(tctx, p, t);
+       return true;
 }
 
-static bool test_add_printer_ex(struct torture_context *tctx,
-                               struct dcerpc_pipe *p,
-                               void *private_data)
+static bool test_printer_ic(struct torture_context *tctx,
+                           void *private_data)
 {
        struct torture_printer_context *t =
-               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+               talloc_get_type_abort(private_data,
+                                     struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       struct policy_handle gdi_handle;
+
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skip printer information context tests against samba");
+       }
+
+       {
+               struct spoolss_CreatePrinterIC r;
+               struct spoolss_DevmodeContainer devmode_ctr;
+
+               ZERO_STRUCT(devmode_ctr);
+
+               r.in.handle = &t->handle;
+               r.in.devmode_ctr = &devmode_ctr;
+               r.out.gdi_handle = &gdi_handle;
+
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_CreatePrinterIC_r(b, tctx, &r),
+                       "CreatePrinterIC failed");
+               torture_assert_werr_ok(tctx, r.out.result,
+                       "CreatePrinterIC failed");
+       }
+
+       {
+               struct spoolss_PlayGDIScriptOnPrinterIC r;
+               DATA_BLOB in,out;
+               int i;
+               uint32_t num_fonts = 0;
+
+               in = data_blob_string_const("");
+
+               r.in.gdi_handle = &gdi_handle;
+               r.in.pIn = in.data;
+               r.in.cIn = in.length;
+               r.in.ul = 0;
+
+               for (i = 0; i < 4; i++) {
+
+                       out = data_blob_talloc_zero(tctx, i);
+
+                       r.in.cOut = out.length;
+                       r.out.pOut = out.data;
+
+                       torture_assert_ntstatus_ok(tctx,
+                               dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+                               "PlayGDIScriptOnPrinterIC failed");
+                       torture_assert_werr_equal(tctx, r.out.result, WERR_NOMEM,
+                               "PlayGDIScriptOnPrinterIC failed");
+               }
+
+               out = data_blob_talloc_zero(tctx, 4);
+
+               r.in.cOut = out.length;
+               r.out.pOut = out.data;
+
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+                       "PlayGDIScriptOnPrinterIC failed");
+               torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+                       "PlayGDIScriptOnPrinterIC failed");
+
+               /* now we should have the required length, so retry with a
+                * buffer which is large enough to carry all font ids */
+
+               num_fonts = IVAL(r.out.pOut, 0);
+
+               torture_comment(tctx, "PlayGDIScriptOnPrinterIC gave font count of %d\n", num_fonts);
+
+               out = data_blob_talloc_zero(tctx,
+                       num_fonts * sizeof(struct UNIVERSAL_FONT_ID) + 4);
+
+               r.in.cOut = out.length;
+               r.out.pOut = out.data;
+
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+                       "PlayGDIScriptOnPrinterIC failed");
+               torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+                       "PlayGDIScriptOnPrinterIC failed");
+
+       }
+
+       {
+               struct spoolss_DeletePrinterIC r;
+
+               r.in.gdi_handle = &gdi_handle;
+               r.out.gdi_handle = &gdi_handle;
+
+               torture_assert_ntstatus_ok(tctx,
+                       dcerpc_spoolss_DeletePrinterIC_r(b, tctx, &r),
+                       "DeletePrinterIC failed");
+               torture_assert_werr_ok(tctx, r.out.result,
+                       "DeletePrinterIC failed");
 
-       t->ex                   = true;
-       t->wellknown            = false;
-       t->info2.printername    = TORTURE_PRINTER_EX;
+       }
 
-       return test_add_printer_args(tctx, p, t);
+       return true;
 }
 
-static bool test_add_printer_ex_wellknown(struct torture_context *tctx,
-                                         struct dcerpc_pipe *p,
-                                         void *private_data)
+static bool test_printer_bidi(struct torture_context *tctx,
+                             void *private_data)
 {
        struct torture_printer_context *t =
-               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+               talloc_get_type_abort(private_data,
+                                     struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       struct spoolss_RpcSendRecvBidiData r;
+       struct RPC_BIDI_REQUEST_CONTAINER bidi_req;
+       struct RPC_BIDI_RESPONSE_CONTAINER *bidi_rep = NULL;
 
-       t->ex                   = true;
-       t->wellknown            = true;
-       t->info2.printername    = TORTURE_WELLKNOWN_PRINTER_EX;
+       if (torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skip printer bidirectional tests against samba");
+       }
+
+       ZERO_STRUCT(bidi_req);
+
+       r.in.hPrinter = t->handle;
+       r.in.pAction = "foobar";
+       r.in.pReqData = &bidi_req;
+       r.out.ppRespData = &bidi_rep;
+
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_RpcSendRecvBidiData_r(b, tctx, &r),
+               "RpcSendRecvBidiData failed");
+       torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+               "RpcSendRecvBidiData failed");
+
+       if (!(t->info2.attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI)) {
+               torture_skip(tctx, "skipping further tests as printer is not BIDI enabled");
+       }
+
+       r.in.pAction = BIDI_ACTION_ENUM_SCHEMA;
 
-       return test_add_printer_args(tctx, p, t);
+       torture_assert_ntstatus_ok(tctx,
+               dcerpc_spoolss_RpcSendRecvBidiData_r(b, tctx, &r),
+               "RpcSendRecvBidiData failed");
+       torture_assert_werr_ok(tctx, r.out.result,
+               "RpcSendRecvBidiData failed");
+
+       return true;
 }
 
-static bool test_architecture_buffer(struct torture_context *tctx,
-                                    struct dcerpc_pipe *p)
+static bool test_printer_set_publish(struct torture_context *tctx,
+                                      struct dcerpc_binding_handle *b,
+                                      struct policy_handle *handle)
 {
-       struct spoolss_OpenPrinterEx r;
-       struct spoolss_UserLevel1 u1;
-       struct policy_handle handle;
-       uint32_t architectures[] = {
-               PROCESSOR_ARCHITECTURE_INTEL,
-               PROCESSOR_ARCHITECTURE_IA64,
-               PROCESSOR_ARCHITECTURE_AMD64
-       };
-       uint32_t needed[3];
-       int i;
-       struct dcerpc_binding_handle *b = p->binding_handle;
+       union spoolss_PrinterInfo info;
+       struct spoolss_SetPrinterInfo7 info7;
+       struct spoolss_SetPrinterInfoCtr info_ctr;
+       struct spoolss_DevmodeContainer devmode_ctr;
+       struct sec_desc_buf secdesc_ctr;
 
-       for (i=0; i < ARRAY_SIZE(architectures); i++) {
+       info7.guid = "";
+       info7.action = DSPRINT_PUBLISH;
 
-               torture_comment(tctx, "Testing OpenPrinterEx with architecture %d\n", architectures[i]);
+       ZERO_STRUCT(info_ctr);
+       ZERO_STRUCT(devmode_ctr);
+       ZERO_STRUCT(secdesc_ctr);
+       info_ctr.level = 7;
+       info_ctr.info.info7 = &info7;
 
-               u1.size = 0;
-               u1.client = NULL;
-               u1.user = NULL;
-               u1.build = 0;
-               u1.major = 3;
-               u1.minor = 0;
-               u1.processor = architectures[i];
+       torture_assert(tctx,
+                      test_SetPrinter(tctx, b, handle, &info_ctr,
+                                      &devmode_ctr, &secdesc_ctr, 0), "");
 
-               r.in.printername        = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
-               r.in.datatype           = NULL;
-               r.in.devmode_ctr.devmode= NULL;
-               r.in.access_mask        = SEC_FLAG_MAXIMUM_ALLOWED;
-               r.in.level               = 1;
-               r.in.userlevel.level1   = &u1;
-               r.out.handle            = &handle;
+       torture_assert(tctx,
+                      test_GetPrinter_level(tctx, b, handle, 2, &info),
+                      "");
+       torture_assert(tctx,
+                      (info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED),
+                      "info2 publish flag not set");
+       torture_assert(tctx,
+                      test_GetPrinter_level(tctx, b, handle, 7, &info),
+                      "");
+       if (info.info7.action & DSPRINT_PENDING) {
+               torture_comment(tctx, "publish is pending\n");
+               torture_assert_int_equal(tctx,
+                                        info.info7.action,
+                                        (DSPRINT_PENDING | DSPRINT_PUBLISH),
+                                        "info7 publish flag not set");
+       } else {
+               struct GUID guid;
+               torture_assert_int_equal(tctx,
+                                        info.info7.action,
+                                        DSPRINT_PUBLISH,
+                                        "info7 publish flag not set");
+               torture_assert_ntstatus_ok(tctx,
+                                          GUID_from_string(info.info7.guid,
+                                          &guid),
+                                          "invalid published printer GUID");
+       }
 
-               torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r), "");
-               torture_assert_werr_ok(tctx, r.out.result, "");
+       return true;
+}
 
-               {
-                       struct spoolss_EnumPrinters e;
-                       uint32_t count;
-                       union spoolss_PrinterInfo *info;
+static bool test_printer_set_unpublish(struct torture_context *tctx,
+                                      struct dcerpc_binding_handle *b,
+                                      struct policy_handle *handle)
+{
+       union spoolss_PrinterInfo info;
+       struct spoolss_SetPrinterInfo7 info7;
+       struct spoolss_SetPrinterInfoCtr info_ctr;
+       struct spoolss_DevmodeContainer devmode_ctr;
+       struct sec_desc_buf secdesc_ctr;
 
-                       e.in.flags = PRINTER_ENUM_LOCAL;
-                       e.in.server = NULL;
-                       e.in.level = 2;
-                       e.in.buffer = NULL;
-                       e.in.offered = 0;
-                       e.out.count = &count;
-                       e.out.info = &info;
-                       e.out.needed = &needed[i];
+       info7.action = DSPRINT_UNPUBLISH;
+       info7.guid = "";
 
-                       torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e), "");
-#if 0
-                       torture_comment(tctx, "needed was %d\n", needed[i]);
-#endif
-               }
+       ZERO_STRUCT(info_ctr);
+       ZERO_STRUCT(devmode_ctr);
+       ZERO_STRUCT(secdesc_ctr);
+       info_ctr.level = 7;
+       info_ctr.info.info7 = &info7;
 
-               torture_assert(tctx, test_ClosePrinter(tctx, b, &handle), "");
+       torture_assert(tctx,
+                      test_SetPrinter(tctx, b, handle, &info_ctr,
+                                      &devmode_ctr, &secdesc_ctr, 0), "");
+
+       torture_assert(tctx,
+                      test_GetPrinter_level(tctx, b, handle, 2, &info),
+                      "");
+       torture_assert(tctx,
+                      !(info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED),
+                      "info2 publish flag still set");
+       torture_assert(tctx,
+                      test_GetPrinter_level(tctx, b, handle, 7, &info),
+                      "");
+
+       if (info.info7.action & DSPRINT_PENDING) {
+               struct GUID guid;
+               torture_comment(tctx, "unpublish is pending\n");
+               torture_assert_int_equal(tctx,
+                                        info.info7.action,
+                                        (DSPRINT_PENDING | DSPRINT_UNPUBLISH),
+                                        "info7 unpublish flag not set");
+               torture_assert_ntstatus_ok(tctx,
+                                          GUID_from_string(info.info7.guid,
+                                          &guid),
+                                          "invalid printer GUID");
+       } else {
+               torture_assert_int_equal(tctx,
+                                        info.info7.action, DSPRINT_UNPUBLISH,
+                                        "info7 unpublish flag not set");
        }
 
-       for (i=1; i < ARRAY_SIZE(architectures); i++) {
-               if (needed[i-1] != needed[i]) {
-                       torture_fail(tctx,
-                               talloc_asprintf(tctx, "needed size %d for architecture %d != needed size %d for architecture %d\n",
-                                               needed[i-1], architectures[i-1], needed[i], architectures[i]));
-               }
+       return true;
+}
+
+static bool test_printer_publish_toggle(struct torture_context *tctx,
+                                          void *private_data)
+{
+       struct torture_printer_context *t =
+               talloc_get_type_abort(private_data,
+                                     struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       struct policy_handle *handle = &t->handle;
+       union spoolss_PrinterInfo info7;
+       union spoolss_PrinterInfo info2;
+
+       /* check publish status via level 7 and level 2 */
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 7, &info7),
+                      "");
+       torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info2),
+                      "");
+
+       if (info2.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
+               torture_assert_int_equal(tctx,
+                                        info7.info7.action, DSPRINT_PUBLISH,
+                                        "info7 publish flag not set");
+               torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), "");
+               torture_assert(tctx, test_printer_set_publish(tctx, b, handle), "");
+       } else {
+               torture_assert_int_equal(tctx,
+                                        info7.info7.action, DSPRINT_UNPUBLISH,
+                                        "info7 unpublish flag not set");
+               torture_assert(tctx, test_printer_set_publish(tctx, b, handle), "");
+               torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), "");
        }
 
        return true;
 }
 
-bool torture_rpc_spoolss(struct torture_context *torture)
+static bool test_driver_info_winreg(struct torture_context *tctx,
+                                   void *private_data)
 {
-       NTSTATUS status;
-       struct dcerpc_pipe *p;
-       struct dcerpc_binding_handle *b;
-       bool ret = true;
-       struct test_spoolss_context *ctx;
-       const char *environment = SPOOLSS_ARCHITECTURE_NT_X86;
+       struct torture_printer_context *t =
+               (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+       struct dcerpc_pipe *p = t->spoolss_pipe;
+       const char *driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
 
-       status = torture_rpc_connection(torture, &p, &ndr_table_spoolss);
-       if (!NT_STATUS_IS_OK(status)) {
-               return false;
+       if (!t->have_driver) {
+               torture_skip(tctx, "skipping driver info winreg test as we don't have a driver");
        }
-       b = p->binding_handle;
 
-       ctx = talloc_zero(torture, struct test_spoolss_context);
-
-       ret &= test_OpenPrinter_server(torture, p, &ctx->server_handle);
-       ret &= test_GetPrinterData_list(torture, p, &ctx->server_handle, &environment);
-       ret &= test_EnumForms_all(torture, b, &ctx->server_handle, true);
-       ret &= test_Forms(torture, b, &ctx->server_handle, true, NULL, NULL, NULL);
-       ret &= test_Forms_winreg(torture, b, &ctx->server_handle, true, NULL);
-       ret &= test_EnumPorts(torture, b, ctx);
-       ret &= test_GetPrinterDriverDirectory(torture, p, environment);
-       ret &= test_GetPrintProcessorDirectory(torture, p, environment);
-       ret &= test_EnumPrinterDrivers(torture, p, ctx, environment);
-       ret &= test_EnumPrinterDrivers(torture, p, ctx, SPOOLSS_ARCHITECTURE_ALL);
-       ret &= test_EnumMonitors(torture, b, ctx);
-       ret &= test_EnumPrintProcessors(torture, b, ctx, environment);
-       ret &= test_EnumPrintProcDataTypes(torture, b);
-       ret &= test_EnumPrinters(torture, b, ctx);
-       ret &= test_OpenPrinter_badname_list(torture, b, dcerpc_server_name(p));
-
-       ret &= test_AddPort(torture, p);
-       ret &= test_EnumPorts_old(torture, p);
-       ret &= test_EnumPrinters_old(torture, p, environment);
-       ret &= test_EnumPrinterDrivers_old(torture, p, environment);
-       ret &= test_architecture_buffer(torture, p);
+       torture_assert(tctx,
+               test_DriverInfo_winreg(tctx, p, &t->handle, t->info2.printername, driver_name, t->driver.remote.environment, 3),
+               "failed to test driver info winreg");
 
-       return ret;
+       return true;
+}
+
+void torture_tcase_printer(struct torture_tcase *tcase)
+{
+       torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter_wrap);
+       torture_tcase_add_simple_test(tcase, "csetprinter", test_csetprinter);
+       torture_tcase_add_simple_test(tcase, "print_test", test_print_test);
+       torture_tcase_add_simple_test(tcase, "print_test_extended", test_print_test_extended);
+       torture_tcase_add_simple_test(tcase, "print_test_smbd", test_print_test_smbd);
+       torture_tcase_add_simple_test(tcase, "print_test_properties", test_print_test_properties);
+       torture_tcase_add_simple_test(tcase, "printer_info", test_printer_info);
+       torture_tcase_add_simple_test(tcase, "sd", test_printer_sd);
+       torture_tcase_add_simple_test(tcase, "dm", test_printer_dm);
+       torture_tcase_add_simple_test(tcase, "printer_info_winreg", test_printer_info_winreg);
+       torture_tcase_add_simple_test(tcase, "change_id", test_printer_change_id);
+       torture_tcase_add_simple_test(tcase, "keys", test_printer_keys);
+       torture_tcase_add_simple_test(tcase, "printerdata_consistency", test_printer_data_consistency);
+       torture_tcase_add_simple_test(tcase, "printerdata_keys", test_printer_data_keys);
+       torture_tcase_add_simple_test(tcase, "printerdata_values", test_printer_data_values);
+       torture_tcase_add_simple_test(tcase, "printerdata_set", test_printer_data_set);
+       torture_tcase_add_simple_test(tcase, "printerdata_winreg", test_printer_data_winreg);
+       torture_tcase_add_simple_test(tcase, "printerdata_dsspooler", test_printer_data_dsspooler);
+       torture_tcase_add_simple_test(tcase, "driver_info_winreg", test_driver_info_winreg);
+       torture_tcase_add_simple_test(tcase, "printer_rename", test_printer_rename);
+       torture_tcase_add_simple_test(tcase, "printer_ic", test_printer_ic);
+       torture_tcase_add_simple_test(tcase, "bidi", test_printer_bidi);
+       torture_tcase_add_simple_test(tcase, "publish_toggle",
+                                     test_printer_publish_toggle);
 }
 
 struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx)
 {
-       struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-PRINTER");
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "printer");
+       struct torture_tcase *tcase;
 
-       struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
-                                                       "printer", &ndr_table_spoolss);
+       tcase = torture_suite_add_tcase(suite, "addprinter");
 
-       struct torture_printer_context *t;
+       torture_tcase_set_fixture(tcase,
+                                 torture_rpc_spoolss_printer_setup,
+                                 torture_rpc_spoolss_printer_teardown);
 
-       t = talloc_zero(mem_ctx, struct torture_printer_context);
+       torture_tcase_printer(tcase);
 
-       t->driver.info8.version                 = SPOOLSS_DRIVER_VERSION_200X;
-       t->driver.info8.driver_name             = TORTURE_DRIVER;
-       t->driver.info8.driver_path             = "pscript5.dll";
-       t->driver.info8.data_file               = "cups6.ppd";
-       t->driver.info8.config_file             = "ps5ui.dll";
-       t->driver.info8.help_file               = "pscript.hlp";
-       t->driver.info8.default_datatype        = "RAW";
-       t->driver.info8.dependent_files         = talloc_zero(t, struct spoolss_StringArray);
-       t->driver.info8.dependent_files->string = talloc_zero_array(t, const char *, 8 + 1);
-       t->driver.info8.dependent_files->string[0] = "pscript5.dll";
-       t->driver.info8.dependent_files->string[1] = "cups6.ppd";
-       t->driver.info8.dependent_files->string[2] = "ps5ui.dll";
-       t->driver.info8.dependent_files->string[3] = "pscript.hlp";
-       t->driver.info8.dependent_files->string[4] = "pscript.ntf";
-       t->driver.info8.dependent_files->string[5] = "cups6.ini";
-       t->driver.info8.dependent_files->string[6] = "cupsps6.dll";
-       t->driver.info8.dependent_files->string[7] = "cupsui6.dll";
+       tcase = torture_suite_add_tcase(suite, "addprinterex");
 
-       t->driver.local.driver_directory= "/usr/share/cups/drivers";
+       torture_tcase_set_fixture(tcase,
+                                 torture_rpc_spoolss_printerex_setup,
+                                 torture_rpc_spoolss_printer_teardown);
 
-       t->info2.drivername             = "Microsoft XPS Document Writer";
-       t->info2.portname               = "LPT1:";
+       torture_tcase_printer(tcase);
+
+       tcase = torture_suite_add_tcase(suite, "addprinterwkn");
+
+       torture_tcase_set_fixture(tcase,
+                                 torture_rpc_spoolss_printerwkn_setup,
+                                 torture_rpc_spoolss_printer_teardown);
+
+       tcase = torture_suite_add_tcase(suite, "addprinterexwkn");
+
+       torture_tcase_set_fixture(tcase,
+                                 torture_rpc_spoolss_printerexwkn_setup,
+                                 torture_rpc_spoolss_printer_teardown);
+
+#if 0
+       /* test is not correct */
+       tcase = torture_suite_add_tcase(suite, "addprinterdm");
+
+       torture_tcase_set_fixture(tcase,
+                                 torture_rpc_spoolss_printerdm_setup,
+                                 torture_rpc_spoolss_printer_teardown);
 
-       torture_rpc_tcase_add_test_ex(tcase, "add_printer", test_add_printer, t);
-       torture_rpc_tcase_add_test_ex(tcase, "add_printer_wellknown", test_add_printer_wellknown, t);
-       torture_rpc_tcase_add_test_ex(tcase, "add_printer_ex", test_add_printer_ex, t);
-       torture_rpc_tcase_add_test_ex(tcase, "add_printer_ex_wellknown", test_add_printer_ex_wellknown, t);
+       torture_tcase_printer(tcase);
+#endif
+       return suite;
+}
+
+struct torture_suite *torture_rpc_spoolss(TALLOC_CTX *mem_ctx)
+{
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss");
+       struct torture_tcase *tcase = torture_suite_add_tcase(suite, "printserver");
+
+       torture_tcase_set_fixture(tcase,
+                                 torture_rpc_spoolss_setup,
+                                 torture_rpc_spoolss_teardown);
+
+       torture_tcase_add_simple_test(tcase, "openprinter_badnamelist", test_OpenPrinter_badname_list);
+       torture_tcase_add_simple_test(tcase, "printer_data_list", test_GetPrinterData_list);
+       torture_tcase_add_simple_test(tcase, "enum_forms", test_PrintServer_EnumForms);
+       torture_tcase_add_simple_test(tcase, "forms", test_PrintServer_Forms);
+       torture_tcase_add_simple_test(tcase, "forms_winreg", test_PrintServer_Forms_Winreg);
+       torture_tcase_add_simple_test(tcase, "enum_ports", test_EnumPorts);
+       torture_tcase_add_simple_test(tcase, "add_port", test_AddPort);
+       torture_tcase_add_simple_test(tcase, "get_printer_driver_directory", test_GetPrinterDriverDirectory);
+       torture_tcase_add_simple_test(tcase, "get_print_processor_directory", test_GetPrintProcessorDirectory);
+       torture_tcase_add_simple_test(tcase, "enum_printer_drivers", test_EnumPrinterDrivers);
+       torture_tcase_add_simple_test(tcase, "enum_monitors", test_EnumMonitors);
+       torture_tcase_add_simple_test(tcase, "enum_print_processors", test_EnumPrintProcessors);
+       torture_tcase_add_simple_test(tcase, "print_processors_winreg", test_print_processors_winreg);
+       torture_tcase_add_simple_test(tcase, "add_processor", test_add_print_processor);
+       torture_tcase_add_simple_test(tcase, "enum_printprocdata", test_EnumPrintProcDataTypes);
+       torture_tcase_add_simple_test(tcase, "enum_printers", test_EnumPrinters);
+       torture_tcase_add_simple_test(tcase, "enum_ports_old", test_EnumPorts_old);
+       torture_tcase_add_simple_test(tcase, "enum_printers_old", test_EnumPrinters_old);
+       torture_tcase_add_simple_test(tcase, "enum_printers_servername", test_EnumPrinters_servername);
+       torture_tcase_add_simple_test(tcase, "enum_printer_drivers_old", test_EnumPrinterDrivers_old);
+       torture_tcase_add_simple_test(tcase, "architecture_buffer", test_architecture_buffer);
+
+       torture_suite_add_suite(suite, torture_rpc_spoolss_printer(suite));
 
        return suite;
 }
@@ -6815,12 +8936,18 @@ static bool test_AddPrinterDriverEx_exp(struct torture_context *tctx,
        return true;
 }
 
+#define ASSERT_DRIVER_PATH(tctx, path, driver_dir, cmt) \
+       if (path && strlen(path)) {\
+               torture_assert_strn_equal(tctx, path, driver_dir, strlen(driver_dir), cmt); \
+       }
+
 static bool test_AddPrinterDriver_args_level_1(struct torture_context *tctx,
                                               struct dcerpc_binding_handle *b,
                                               const char *server_name,
                                               struct spoolss_AddDriverInfo8 *r,
                                               uint32_t flags,
-                                              bool ex)
+                                              bool ex,
+                                              const char *remote_driver_dir)
 {
        struct spoolss_AddDriverInfoCtr info_ctr;
        struct spoolss_AddDriverInfo1 info1;
@@ -6860,10 +8987,12 @@ static bool test_AddPrinterDriver_args_level_2(struct torture_context *tctx,
                                               const char *server_name,
                                               struct spoolss_AddDriverInfo8 *r,
                                               uint32_t flags,
-                                              bool ex)
+                                              bool ex,
+                                              const char *remote_driver_dir)
 {
        struct spoolss_AddDriverInfoCtr info_ctr;
        struct spoolss_AddDriverInfo2 info2;
+       union spoolss_DriverInfo info;
 
        ZERO_STRUCT(info2);
 
@@ -6959,9 +9088,15 @@ static bool test_AddPrinterDriver_args_level_2(struct torture_context *tctx,
        }
 
        torture_assert(tctx,
-               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 2, r->driver_name),
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 2, r->driver_name, &info),
                "failed to find added printer driver");
 
+       if (remote_driver_dir) {
+               ASSERT_DRIVER_PATH(tctx, info.info2.driver_path, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info2.data_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info2.config_file, remote_driver_dir, "unexpected path");
+       }
+
        return true;
 }
 
@@ -6970,10 +9105,12 @@ static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
                                               const char *server_name,
                                               struct spoolss_AddDriverInfo8 *r,
                                               uint32_t flags,
-                                              bool ex)
+                                              bool ex,
+                                              const char *remote_driver_dir)
 {
        struct spoolss_AddDriverInfoCtr info_ctr;
        struct spoolss_AddDriverInfo3 info3;
+       union spoolss_DriverInfo info;
 
        info3.driver_name       = r->driver_name;
        info3.version           = r->version;
@@ -7001,9 +9138,20 @@ static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
        }
 
        torture_assert(tctx,
-               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 3, r->driver_name),
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 3, r->driver_name, &info),
                "failed to find added printer driver");
 
+       if (remote_driver_dir) {
+               int i;
+               ASSERT_DRIVER_PATH(tctx, info.info3.driver_path, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info3.data_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info3.config_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info3.help_file, remote_driver_dir, "unexpected path");
+               for (i=0; info.info3.dependent_files && info.info3.dependent_files[i] != NULL; i++) {
+                       ASSERT_DRIVER_PATH(tctx, info.info3.dependent_files[i], remote_driver_dir, "unexpected path");
+               }
+       }
+
        return true;
 }
 
@@ -7012,10 +9160,12 @@ static bool test_AddPrinterDriver_args_level_4(struct torture_context *tctx,
                                               const char *server_name,
                                               struct spoolss_AddDriverInfo8 *r,
                                               uint32_t flags,
-                                              bool ex)
+                                              bool ex,
+                                              const char *remote_driver_dir)
 {
        struct spoolss_AddDriverInfoCtr info_ctr;
        struct spoolss_AddDriverInfo4 info4;
+       union spoolss_DriverInfo info;
 
        info4.version           = r->version;
        info4.driver_name       = r->driver_name;
@@ -7045,9 +9195,20 @@ static bool test_AddPrinterDriver_args_level_4(struct torture_context *tctx,
        }
 
        torture_assert(tctx,
-               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 4, r->driver_name),
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 4, r->driver_name, &info),
                "failed to find added printer driver");
 
+       if (remote_driver_dir) {
+               int i;
+               ASSERT_DRIVER_PATH(tctx, info.info4.driver_path, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info4.data_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info4.config_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info4.help_file, remote_driver_dir, "unexpected path");
+               for (i=0; info.info4.dependent_files && info.info4.dependent_files[i] != NULL; i++) {
+                       ASSERT_DRIVER_PATH(tctx, info.info4.dependent_files[i], remote_driver_dir, "unexpected path");
+               }
+       }
+
        return true;
 }
 
@@ -7056,10 +9217,12 @@ static bool test_AddPrinterDriver_args_level_6(struct torture_context *tctx,
                                               const char *server_name,
                                               struct spoolss_AddDriverInfo8 *r,
                                               uint32_t flags,
-                                              bool ex)
+                                              bool ex,
+                                              const char *remote_driver_dir)
 {
        struct spoolss_AddDriverInfoCtr info_ctr;
        struct spoolss_AddDriverInfo6 info6;
+       union spoolss_DriverInfo info;
 
        info6.version           = r->version;
        info6.driver_name       = r->driver_name;
@@ -7101,9 +9264,23 @@ static bool test_AddPrinterDriver_args_level_6(struct torture_context *tctx,
        }
 
        torture_assert(tctx,
-               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 6, r->driver_name),
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 6, r->driver_name, &info),
                "failed to find added printer driver");
 
+       if (remote_driver_dir) {
+               int i;
+               ASSERT_DRIVER_PATH(tctx, info.info6.driver_path, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info6.data_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info6.config_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info6.help_file, remote_driver_dir, "unexpected path");
+               for (i=0; info.info6.dependent_files && info.info6.dependent_files[i] != NULL; i++) {
+                       ASSERT_DRIVER_PATH(tctx, info.info6.dependent_files[i], remote_driver_dir, "unexpected path");
+               }
+       }
+
+       torture_assert_nttime_equal(tctx, info.info6.driver_date, info6.driver_date, "driverdate mismatch");
+       torture_assert_u64_equal(tctx, info.info6.driver_version, info6.driver_version, "driverversion mismatch");
+
        return true;
 }
 
@@ -7112,9 +9289,11 @@ static bool test_AddPrinterDriver_args_level_8(struct torture_context *tctx,
                                               const char *server_name,
                                               struct spoolss_AddDriverInfo8 *r,
                                               uint32_t flags,
-                                              bool ex)
+                                              bool ex,
+                                              const char *remote_driver_dir)
 {
        struct spoolss_AddDriverInfoCtr info_ctr;
+       union spoolss_DriverInfo info;
 
        info_ctr.level = 8;
        info_ctr.info.info8 = r;
@@ -7136,12 +9315,28 @@ static bool test_AddPrinterDriver_args_level_8(struct torture_context *tctx,
        }
 
        torture_assert(tctx,
-               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 8, r->driver_name),
+               test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 8, r->driver_name, &info),
                "failed to find added printer driver");
 
+       if (remote_driver_dir) {
+               int i;
+               ASSERT_DRIVER_PATH(tctx, info.info8.driver_path, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info8.data_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info8.config_file, remote_driver_dir, "unexpected path");
+               ASSERT_DRIVER_PATH(tctx, info.info8.help_file, remote_driver_dir, "unexpected path");
+               for (i=0; info.info8.dependent_files && info.info8.dependent_files[i] != NULL; i++) {
+                       ASSERT_DRIVER_PATH(tctx, info.info8.dependent_files[i], remote_driver_dir, "unexpected path");
+               }
+       }
+
+       torture_assert_nttime_equal(tctx, info.info8.driver_date, r->driver_date, "driverdate mismatch");
+       torture_assert_u64_equal(tctx, info.info8.driver_version, r->driver_version, "driverversion mismatch");
+
        return true;
 }
 
+#undef ASSERT_DRIVER_PATH
+
 static bool test_DeletePrinterDriver_exp(struct torture_context *tctx,
                                         struct dcerpc_binding_handle *b,
                                         const char *server,
@@ -7208,7 +9403,7 @@ static bool test_DeletePrinterDriver(struct torture_context *tctx,
                test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_OK),
                "failed to delete driver");
 
-       if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver)) {
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) {
                torture_fail(tctx, "deleted driver still enumerated");
        }
 
@@ -7235,7 +9430,7 @@ static bool test_DeletePrinterDriverEx(struct torture_context *tctx,
                test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_OK),
                "failed to delete driver");
 
-       if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver)) {
+       if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) {
                torture_fail(tctx, "deleted driver still enumerated");
        }
 
@@ -7254,28 +9449,29 @@ static bool test_PrinterDriver_args(struct torture_context *tctx,
                                    uint32_t add_flags,
                                    uint32_t delete_flags,
                                    uint32_t delete_version,
-                                   bool ex)
+                                   bool ex,
+                                   const char *remote_driver_dir)
 {
        bool ret = true;
 
        switch (level) {
        case 1:
-               ret = test_AddPrinterDriver_args_level_1(tctx, b, server_name, r, add_flags, ex);
+               ret = test_AddPrinterDriver_args_level_1(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
                break;
        case 2:
-               ret = test_AddPrinterDriver_args_level_2(tctx, b, server_name, r, add_flags, ex);
+               ret = test_AddPrinterDriver_args_level_2(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
                break;
        case 3:
-               ret = test_AddPrinterDriver_args_level_3(tctx, b, server_name, r, add_flags, ex);
+               ret = test_AddPrinterDriver_args_level_3(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
                break;
        case 4:
-               ret = test_AddPrinterDriver_args_level_4(tctx, b, server_name, r, add_flags, ex);
+               ret = test_AddPrinterDriver_args_level_4(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
                break;
        case 6:
-               ret = test_AddPrinterDriver_args_level_6(tctx, b, server_name, r, add_flags, ex);
+               ret = test_AddPrinterDriver_args_level_6(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
                break;
        case 8:
-               ret = test_AddPrinterDriver_args_level_8(tctx, b, server_name, r, add_flags, ex);
+               ret = test_AddPrinterDriver_args_level_8(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
                break;
        default:
                return false;
@@ -7295,6 +9491,25 @@ static bool test_PrinterDriver_args(struct torture_context *tctx,
                return ret;
        }
 
+       {
+               struct dcerpc_pipe *p2;
+               struct policy_handle hive_handle;
+               struct dcerpc_binding_handle *b2;
+
+               torture_assert_ntstatus_ok(tctx,
+                       torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+                       "could not open winreg pipe");
+               b2 = p2->binding_handle;
+
+               torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+               ret = test_GetDriverInfo_winreg(tctx, b, NULL, NULL, r->driver_name, r->architecture, r->version, b2, &hive_handle, server_name);
+
+               test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+               talloc_free(p2);
+       }
+
        if (ex) {
                return test_DeletePrinterDriverEx(tctx, b, server_name, r->driver_name, r->architecture, delete_flags, r->version);
        } else {
@@ -7378,7 +9593,7 @@ static bool upload_printer_driver_file(struct torture_context *tctx,
        const char *local_name = talloc_asprintf(tctx, "%s/%s", d->local.driver_directory, file_name);
        const char *remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, file_name);
 
-       if (!file_name) {
+       if (!file_name || strlen(file_name) == 0) {
                return true;
        }
 
@@ -7396,6 +9611,7 @@ static bool upload_printer_driver_file(struct torture_context *tctx,
 
        buf = talloc_array(tctx, uint8_t, maxwrite);
        if (!buf) {
+               x_fclose(f);
                return false;
        }
 
@@ -7440,32 +9656,100 @@ static bool connect_printer_driver_share(struct torture_context *tctx,
        struct smbcli_options smb_options;
        struct smbcli_session_options smb_session_options;
 
-       torture_comment(tctx, "Connecting printer driver share '%s' on '%s'\n",
-               share_name, server_name);
+       torture_comment(tctx, "Connecting printer driver share '%s' on '%s'\n",
+               share_name, server_name);
+
+       lpcfg_smbcli_options(tctx->lp_ctx, &smb_options);
+       lpcfg_smbcli_session_options(tctx->lp_ctx, &smb_session_options);
+
+       torture_assert_ntstatus_ok(tctx,
+               smbcli_full_connection(tctx, cli, server_name,
+                                       lpcfg_smb_ports(tctx->lp_ctx),
+                                       share_name, NULL,
+                                       lpcfg_socket_options(tctx->lp_ctx),
+                                       cmdline_credentials,
+                                       lpcfg_resolve_context(tctx->lp_ctx),
+                                       tctx->ev,
+                                       &smb_options,
+                                       &smb_session_options,
+                                       lpcfg_gensec_settings(tctx, tctx->lp_ctx)),
+               "failed to open driver share");
+
+       return true;
+}
+
+static bool upload_printer_driver(struct torture_context *tctx,
+                                 const char *server_name,
+                                 struct torture_driver_context *d)
+{
+       struct smbcli_state *cli;
+       const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+       int i;
+
+       torture_assert(tctx,
+               connect_printer_driver_share(tctx, server_name, share_name, &cli),
+               "failed to connect to driver share");
+
+       torture_comment(tctx, "Uploading printer driver files to \\\\%s\\%s\n",
+               server_name, share_name);
+
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.driver_path),
+               "failed to upload driver_path");
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.data_file),
+               "failed to upload data_file");
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.config_file),
+               "failed to upload config_file");
+       torture_assert(tctx,
+               upload_printer_driver_file(tctx, cli, d, d->info8.help_file),
+               "failed to upload help_file");
+       if (d->info8.dependent_files) {
+               for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+                       torture_assert(tctx,
+                               upload_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
+                               "failed to upload dependent_files");
+               }
+       }
+
+       talloc_free(cli);
+
+       return true;
+}
+
+static bool check_printer_driver_file(struct torture_context *tctx,
+                                     struct smbcli_state *cli,
+                                     struct torture_driver_context *d,
+                                     const char *file_name)
+{
+       const char *remote_arch_dir = driver_directory_dir(d->remote.driver_directory);
+       const char *remote_name = talloc_asprintf(tctx, "%s\\%d\\%s",
+                                                 remote_arch_dir,
+                                                 d->info8.version,
+                                                 file_name);
+       int fnum;
+
+       torture_assert(tctx, (file_name && strlen(file_name) != 0), "invalid filename");
 
-       lp_smbcli_options(tctx->lp_ctx, &smb_options);
-       lp_smbcli_session_options(tctx->lp_ctx, &smb_session_options);
+       torture_comment(tctx, "checking for driver file at %s\n", remote_name);
+
+       fnum = smbcli_open(cli->tree, remote_name, O_RDONLY, DENY_NONE);
+       if (fnum == -1) {
+               return false;
+       }
 
        torture_assert_ntstatus_ok(tctx,
-               smbcli_full_connection(tctx, cli, server_name,
-                                       lp_smb_ports(tctx->lp_ctx),
-                                       share_name, NULL,
-                                       lp_socket_options(tctx->lp_ctx),
-                                       cmdline_credentials,
-                                       lp_resolve_context(tctx->lp_ctx),
-                                       tctx->ev,
-                                       &smb_options,
-                                       &smb_session_options,
-                                       lp_iconv_convenience(tctx->lp_ctx),
-                                       lp_gensec_settings(tctx, tctx->lp_ctx)),
-               "failed to open driver share");
+               smbcli_close(cli->tree, fnum),
+               "failed to close driver file");
 
        return true;
 }
 
-static bool upload_printer_driver(struct torture_context *tctx,
-                                 const char *server_name,
-                                 struct torture_driver_context *d)
+static bool check_printer_driver_files(struct torture_context *tctx,
+                                      const char *server_name,
+                                      struct torture_driver_context *d,
+                                      bool expect_exist)
 {
        struct smbcli_state *cli;
        const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
@@ -7475,26 +9759,35 @@ static bool upload_printer_driver(struct torture_context *tctx,
                connect_printer_driver_share(tctx, server_name, share_name, &cli),
                "failed to connect to driver share");
 
-       torture_comment(tctx, "Uploading printer driver files to \\\\%s\\%s\n",
-               server_name, share_name);
+       torture_comment(tctx, "checking %sexistent driver files at \\\\%s\\%s\n",
+                       (expect_exist ? "": "non-"),
+                       server_name, share_name);
 
-       torture_assert(tctx,
-               upload_printer_driver_file(tctx, cli, d, d->info8.driver_path),
-               "failed to upload driver_path");
-       torture_assert(tctx,
-               upload_printer_driver_file(tctx, cli, d, d->info8.data_file),
-               "failed to upload data_file");
-       torture_assert(tctx,
-               upload_printer_driver_file(tctx, cli, d, d->info8.config_file),
-               "failed to upload config_file");
-       torture_assert(tctx,
-               upload_printer_driver_file(tctx, cli, d, d->info8.help_file),
-               "failed to upload help_file");
+       if (d->info8.driver_path && d->info8.driver_path[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.driver_path) == expect_exist,
+                       "failed driver_path check");
+       }
+       if (d->info8.data_file && d->info8.data_file[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.data_file) == expect_exist,
+                       "failed data_file check");
+       }
+       if (d->info8.config_file && d->info8.config_file[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.config_file) == expect_exist,
+                       "failed config_file check");
+       }
+       if (d->info8.help_file && d->info8.help_file[0]) {
+               torture_assert(tctx,
+                       check_printer_driver_file(tctx, cli, d, d->info8.help_file) == expect_exist,
+                       "failed help_file check");
+       }
        if (d->info8.dependent_files) {
                for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
                        torture_assert(tctx,
-                               upload_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
-                               "failed to upload dependent_files");
+                               check_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]) == expect_exist,
+                               "failed dependent_files check");
                }
        }
 
@@ -7511,7 +9804,7 @@ static bool remove_printer_driver_file(struct torture_context *tctx,
        const char *remote_name;
        const char *remote_dir =  driver_directory_dir(d->remote.driver_directory);
 
-       if (!file_name) {
+       if (!file_name || strlen(file_name) == 0) {
                return true;
        }
 
@@ -7547,9 +9840,11 @@ static bool remove_printer_driver(struct torture_context *tctx,
        torture_assert(tctx,
                remove_printer_driver_file(tctx, cli, d, d->info8.data_file),
                "failed to remove data_file");
-       torture_assert(tctx,
-               remove_printer_driver_file(tctx, cli, d, d->info8.config_file),
-               "failed to remove config_file");
+       if (!strequal(d->info8.config_file, d->info8.driver_path)) {
+               torture_assert(tctx,
+                       remove_printer_driver_file(tctx, cli, d, d->info8.config_file),
+                       "failed to remove config_file");
+       }
        torture_assert(tctx,
                remove_printer_driver_file(tctx, cli, d, d->info8.help_file),
                "failed to remove help_file");
@@ -7603,12 +9898,19 @@ static bool test_add_driver_arg(struct torture_context *tctx,
                upload_printer_driver(tctx, dcerpc_server_name(p), d),
                "failed to upload printer driver");
 
-       info8.version           = d->info8.version;
-       info8.driver_name       = d->info8.driver_name;
-       info8.architecture      = d->local.environment;
-       info8.driver_path       = d->info8.driver_path;
-       info8.data_file         = d->info8.data_file;
-       info8.config_file       = d->info8.config_file;
+       info8 = d->info8;
+       if (d->info8.dependent_files) {
+               info8.dependent_files = talloc_zero(tctx, struct spoolss_StringArray);
+               if (d->info8.dependent_files->string) {
+                       for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+                       }
+                       info8.dependent_files->string = talloc_zero_array(info8.dependent_files, const char *, i+1);
+                       for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+                               info8.dependent_files->string[i] = talloc_strdup(info8.dependent_files->string, d->info8.dependent_files->string[i]);
+                       }
+               }
+       }
+       info8.architecture      = d->local.environment;
 
        for (i=0; i < ARRAY_SIZE(levels); i++) {
 
@@ -7623,17 +9925,36 @@ static bool test_add_driver_arg(struct torture_context *tctx,
                                break;
                        }
                }
+               if (torture_setting_bool(tctx, "w2k3", false)) {
+                       switch (levels[i]) {
+                       case 8:
+                               torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]);
+                               continue;
+                       default:
+                               break;
+                       }
+               }
 
                torture_comment(tctx,
                        "Testing PrinterDriver%s '%s' add & delete level %d\n",
                                d->ex ? "Ex" : "", info8.driver_name, levels[i]);
 
-               ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex);
+               ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory);
        }
 
        info8.driver_path       = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.driver_path);
        info8.data_file         = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.data_file);
-       info8.config_file       = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.config_file);
+       if (d->info8.config_file) {
+               info8.config_file       = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.config_file);
+       }
+       if (d->info8.help_file) {
+               info8.help_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.help_file);
+       }
+       if (d->info8.dependent_files && d->info8.dependent_files->string) {
+               for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+                       info8.dependent_files->string[i] = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.dependent_files->string[i]);
+               }
+       }
 
        for (i=0; i < ARRAY_SIZE(levels); i++) {
 
@@ -7648,13 +9969,21 @@ static bool test_add_driver_arg(struct torture_context *tctx,
                                break;
                        }
                }
-
+               if (torture_setting_bool(tctx, "w2k3", false)) {
+                       switch (levels[i]) {
+                       case 8:
+                               torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]);
+                               continue;
+                       default:
+                               break;
+                       }
+               }
 
                torture_comment(tctx,
                        "Testing PrinterDriver%s '%s' add & delete level %d (full unc paths)\n",
                                d->ex ? "Ex" : "", info8.driver_name, levels[i]);
 
-               ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex);
+               ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory);
        }
 
        torture_assert(tctx,
@@ -7667,87 +9996,454 @@ static bool test_add_driver_arg(struct torture_context *tctx,
 }
 
 static bool test_add_driver_ex_64(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
-                                 void *private_data)
+                                 struct dcerpc_pipe *p)
 {
-       struct torture_driver_context *d =
-               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
+       struct torture_driver_context *d;
 
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER_EX;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
        d->local.environment            = talloc_strdup(d, "Windows x64");
        d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/x64");
-       d->info8.driver_name            = TORTURE_DRIVER_EX;
        d->ex                           = true;
 
        return test_add_driver_arg(tctx, p, d);
 }
 
 static bool test_add_driver_ex_32(struct torture_context *tctx,
-                                 struct dcerpc_pipe *p,
-                                 void *private_data)
+                                 struct dcerpc_pipe *p)
 {
-       struct torture_driver_context *d =
-               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
+       struct torture_driver_context *d;
+
+       d = talloc_zero(tctx, struct torture_driver_context);
 
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER_EX;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
        d->local.environment            = talloc_strdup(d, "Windows NT x86");
        d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
-       d->info8.driver_name            = TORTURE_DRIVER_EX;
        d->ex                           = true;
 
        return test_add_driver_arg(tctx, p, d);
 }
 
 static bool test_add_driver_64(struct torture_context *tctx,
-                              struct dcerpc_pipe *p,
-                              void *private_data)
+                              struct dcerpc_pipe *p)
 {
-       struct torture_driver_context *d =
-               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
+       struct torture_driver_context *d;
+
+       d = talloc_zero(tctx, struct torture_driver_context);
 
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
        d->local.environment            = talloc_strdup(d, "Windows x64");
        d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/x64");
-       d->info8.driver_name            = TORTURE_DRIVER;
        d->ex                           = false;
 
        return test_add_driver_arg(tctx, p, d);
 }
 
 static bool test_add_driver_32(struct torture_context *tctx,
-                              struct dcerpc_pipe *p,
-                              void *private_data)
+                              struct dcerpc_pipe *p)
 {
-       struct torture_driver_context *d =
-               (struct torture_driver_context *)talloc_get_type_abort(private_data, struct torture_driver_context);
+       struct torture_driver_context *d;
+
+       d = talloc_zero(tctx, struct torture_driver_context);
 
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
        d->local.environment            = talloc_strdup(d, "Windows NT x86");
        d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
-       d->info8.driver_name            = TORTURE_DRIVER;
        d->ex                           = false;
 
        return test_add_driver_arg(tctx, p, d);
 }
 
+static bool test_add_driver_adobe(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+
+       if (!torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skipping adobe test which only works against samba3");
+       }
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_9X;
+       d->info8.driver_name            = TORTURE_DRIVER_ADOBE;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "ADOBEPS4.DRV");
+       d->info8.data_file              = talloc_strdup(d, "DEFPRTR2.PPD");
+       d->info8.config_file            = talloc_strdup(d, "ADOBEPS4.DRV");
+#if 0
+       d->info8.help_file              = talloc_strdup(d, "ADOBEPS4.HLP");
+       d->info8.monitor_name           = talloc_strdup(d, "PSMON.DLL");
+#endif
+       d->local.environment            = talloc_strdup(d, "Windows 4.0");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/adobe/");
+       d->ex                           = false;
+
+       return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_adobe_cupsaddsmb(struct torture_context *tctx,
+                                            struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+       struct spoolss_StringArray *a;
+
+       if (!torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skipping cupsaddsmb test which only works against samba3");
+       }
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_9X;
+       d->info8.driver_name            = TORTURE_DRIVER_ADOBE_CUPSADDSMB;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "ADOBEPS4.DRV");
+       d->info8.data_file              = talloc_strdup(d, "DEFPRTR2.PPD");
+       d->info8.config_file            = NULL;
+       d->info8.help_file              = talloc_strdup(d, "ADOBEPS4.HLP");
+       d->info8.monitor_name           = talloc_strdup(d, "PSMON.DLL");
+       d->info8.default_datatype       = talloc_strdup(d, "RAW");
+
+       a                               = talloc_zero(d, struct spoolss_StringArray);
+       a->string                       = talloc_zero_array(a, const char *, 7);
+       a->string[0]                    = talloc_strdup(a->string, "ADOBEPS4.DRV");
+       a->string[1]                    = talloc_strdup(a->string, "DEFPRTR2.PPD");
+       a->string[2]                    = talloc_strdup(a->string, "ADOBEPS4.HLP");
+       a->string[3]                    = talloc_strdup(a->string, "PSMON.DLL");
+       a->string[4]                    = talloc_strdup(a->string, "ADFONTS.MFM");
+       a->string[5]                    = talloc_strdup(a->string, "ICONLIB.DLL");
+
+       d->info8.dependent_files        = a;
+       d->local.environment            = talloc_strdup(d, "Windows 4.0");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/adobe/");
+       d->ex                           = false;
+
+       return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_timestamps(struct torture_context *tctx,
+                                      struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+       struct timeval t = timeval_current();
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER_TIMESTAMPS;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
+       d->info8.driver_date            = timeval_to_nttime(&t);
+       d->local.environment            = talloc_strdup(d, "Windows NT x86");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+       d->ex                           = true;
+
+       torture_assert(tctx,
+               test_add_driver_arg(tctx, p, d),
+               "");
+
+       unix_to_nt_time(&d->info8.driver_date, 1);
+
+       torture_assert(tctx,
+               test_add_driver_arg(tctx, p, d),
+               "");
+
+       return true;
+}
+
+static bool test_multiple_drivers(struct torture_context *tctx,
+                                 struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+       int i;
+       struct spoolss_AddDriverInfo8 info8;
+       uint32_t add_flags = APD_COPY_NEW_FILES;
+       uint32_t delete_flags = 0;
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
+       d->local.environment            = talloc_strdup(d, "Windows NT x86");
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+       d->ex                           = true;
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d),
+               "failed to fillup printserver info");
+
+       if (!directory_exist(d->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to upload printer driver");
+
+       info8 = d->info8;
+       info8.architecture      = d->local.environment;
+
+       for (i=0; i < 3; i++) {
+               info8.driver_name               = talloc_asprintf(d, "torture_test_driver_%d", i);
+
+               torture_assert(tctx,
+                       test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &info8, add_flags, true, NULL),
+                       "failed to add driver");
+       }
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_0", info8.architecture, delete_flags, info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_1", NULL),
+               "torture_test_driver_1 no longer on the server");
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+               "torture_test_driver_2 no longer on the server");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_1", info8.architecture, delete_flags, info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+               "torture_test_driver_2 no longer on the server");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_2", info8.architecture, delete_flags, info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               remove_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to remove printer driver");
+
+       return true;
+}
+
+static bool test_del_driver_all_files(struct torture_context *tctx,
+                                     struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d;
+       struct spoolss_StringArray *a;
+       uint32_t add_flags = APD_COPY_NEW_FILES;
+       uint32_t delete_flags = DPD_DELETE_ALL_FILES;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       d = talloc_zero(tctx, struct torture_driver_context);
+
+       d->ex                           = true;
+       d->info8.version                = SPOOLSS_DRIVER_VERSION_200X;
+       d->info8.driver_name            = TORTURE_DRIVER_DELETER;
+       d->info8.architecture           = NULL;
+       d->info8.driver_path            = talloc_strdup(d, "pscript5.dll");
+       d->info8.data_file              = talloc_strdup(d, "cups6.ppd");
+       d->info8.config_file            = talloc_strdup(d, "cupsui6.dll");
+       d->info8.help_file              = talloc_strdup(d, "pscript.hlp");
+       d->local.environment            = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+       d->local.driver_directory       = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+       a                               = talloc_zero(d, struct spoolss_StringArray);
+       a->string                       = talloc_zero_array(a, const char *, 3);
+       a->string[0]                    = talloc_strdup(a->string, "cups6.inf");
+       a->string[1]                    = talloc_strdup(a->string, "cups6.ini");
+
+       d->info8.dependent_files        = a;
+       d->info8.architecture           = d->local.environment;
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d),
+               "failed to fillup printserver info");
+
+       if (!directory_exist(d->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d),
+               "failed to upload printer driver");
+
+       torture_assert(tctx,
+               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d->info8, add_flags, true, NULL),
+               "failed to add driver");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx(tctx, b, server_name_slash,
+                                          d->info8.driver_name,
+                                          d->local.environment,
+                                          delete_flags,
+                                          d->info8.version),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d, false),
+               "printer driver file check failed");
+
+       talloc_free(d);
+       return true;
+}
+
+static bool test_del_driver_unused_files(struct torture_context *tctx,
+                                        struct dcerpc_pipe *p)
+{
+       struct torture_driver_context *d1;
+       struct torture_driver_context *d2;
+       uint32_t add_flags = APD_COPY_NEW_FILES;
+       struct dcerpc_binding_handle *b = p->binding_handle;
+       const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+       d1 = talloc_zero(tctx, struct torture_driver_context);
+       d1->ex                          = true;
+       d1->info8.version               = SPOOLSS_DRIVER_VERSION_200X;
+       d1->info8.driver_name           = TORTURE_DRIVER_DELETER;
+       d1->info8.architecture          = NULL;
+       d1->info8.driver_path           = talloc_strdup(d1, "pscript5.dll");
+       d1->info8.data_file             = talloc_strdup(d1, "cups6.ppd");
+       d1->info8.config_file           = talloc_strdup(d1, "cupsui6.dll");
+       d1->info8.help_file             = talloc_strdup(d1, "pscript.hlp");
+       d1->local.environment           = talloc_strdup(d1, SPOOLSS_ARCHITECTURE_x64);
+       d1->local.driver_directory      = talloc_strdup(d1, "/usr/share/cups/drivers/x64");
+       d1->info8.architecture          = d1->local.environment;
+
+       d2 = talloc_zero(tctx, struct torture_driver_context);
+       d2->ex                          = true;
+       d2->info8.version               = SPOOLSS_DRIVER_VERSION_200X;
+       d2->info8.driver_name           = TORTURE_DRIVER_DELETERIN;
+       d2->info8.architecture          = NULL;
+       d2->info8.driver_path           = talloc_strdup(d2, "pscript5.dll");    /* overlapping */
+       d2->info8.data_file             = talloc_strdup(d2, "cupsps6.dll");
+       d2->info8.config_file           = talloc_strdup(d2, "cups6.ini");
+       d2->info8.help_file             = talloc_strdup(d2, "pscript.hlp");     /* overlapping */
+       d2->local.environment           = talloc_strdup(d2, SPOOLSS_ARCHITECTURE_x64);
+       d2->local.driver_directory      = talloc_strdup(d2, "/usr/share/cups/drivers/x64");
+       d2->info8.architecture          = d2->local.environment;
+
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d1),
+               "failed to fillup printserver info");
+       torture_assert(tctx,
+               fillup_printserver_info(tctx, p, d2),
+               "failed to fillup printserver info");
+
+       if (!directory_exist(d1->local.driver_directory)) {
+               torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+       }
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d1),
+               "failed to upload printer driver");
+       torture_assert(tctx,
+               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d1->info8, add_flags, true, NULL),
+               "failed to add driver");
+
+       torture_assert(tctx,
+               upload_printer_driver(tctx, dcerpc_server_name(p), d2),
+               "failed to upload printer driver");
+       torture_assert(tctx,
+               test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d2->info8, add_flags, true, NULL),
+               "failed to add driver");
+
+       /* some files are in use by a separate driver, should fail */
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+                                              d1->info8.driver_name,
+                                              d1->local.environment,
+                                              DPD_DELETE_ALL_FILES,
+                                              d1->info8.version,
+                                              WERR_PRINTER_DRIVER_IN_USE),
+               "invalid delete driver response");
+
+       /* should only delete files not in use by other driver */
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+                                              d1->info8.driver_name,
+                                              d1->local.environment,
+                                              DPD_DELETE_UNUSED_FILES,
+                                              d1->info8.version,
+                                              WERR_OK),
+               "failed to delete driver (unused files)");
+
+       /* check non-overlapping were deleted */
+       d1->info8.driver_path = NULL;
+       d1->info8.help_file = NULL;
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d1, false),
+               "printer driver file check failed");
+       /* d2 files should be uneffected */
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d2, true),
+               "printer driver file check failed");
+
+       torture_assert(tctx,
+               test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+                                              d2->info8.driver_name,
+                                              d2->local.environment,
+                                              DPD_DELETE_ALL_FILES,
+                                              d2->info8.version,
+                                              WERR_OK),
+               "failed to delete driver");
+
+       torture_assert(tctx,
+               check_printer_driver_files(tctx, dcerpc_server_name(p), d2, false),
+               "printer driver file check failed");
+
+       talloc_free(d1);
+       talloc_free(d2);
+       return true;
+}
+
 struct torture_suite *torture_rpc_spoolss_driver(TALLOC_CTX *mem_ctx)
 {
-       struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-DRIVER");
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.driver");
 
        struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
                                                        "driver", &ndr_table_spoolss);
-       struct torture_driver_context *t;
+       torture_rpc_tcase_add_test(tcase, "add_driver_64", test_add_driver_64);
+       torture_rpc_tcase_add_test(tcase, "add_driver_ex_64", test_add_driver_ex_64);
+
+       torture_rpc_tcase_add_test(tcase, "add_driver_32", test_add_driver_32);
+       torture_rpc_tcase_add_test(tcase, "add_driver_ex_32", test_add_driver_ex_32);
+
+       torture_rpc_tcase_add_test(tcase, "add_driver_adobe", test_add_driver_adobe);
+
+       torture_rpc_tcase_add_test(tcase, "add_driver_adobe_cupsaddsmb", test_add_driver_adobe_cupsaddsmb);
 
-       t = talloc_zero(mem_ctx, struct torture_driver_context);
+       torture_rpc_tcase_add_test(tcase, "add_driver_timestamps", test_add_driver_timestamps);
 
-       t->info8.version        = SPOOLSS_DRIVER_VERSION_200X;
-       t->info8.driver_name    = NULL;
-       t->info8.architecture   = NULL;
-       t->info8.driver_path    = talloc_strdup(t, "pscript5.dll");
-       t->info8.data_file      = talloc_strdup(t, "cups6.ppd");
-       t->info8.config_file    = talloc_strdup(t, "cupsui6.dll");
+       torture_rpc_tcase_add_test(tcase, "multiple_drivers", test_multiple_drivers);
 
-       torture_rpc_tcase_add_test_ex(tcase, "add_driver_64", test_add_driver_64, t);
-       torture_rpc_tcase_add_test_ex(tcase, "add_driver_ex_64", test_add_driver_ex_64, t);
+       torture_rpc_tcase_add_test(tcase, "del_driver_all_files", test_del_driver_all_files);
 
-       torture_rpc_tcase_add_test_ex(tcase, "add_driver_32", test_add_driver_32, t);
-       torture_rpc_tcase_add_test_ex(tcase, "add_driver_ex_32", test_add_driver_ex_32, t);
+       torture_rpc_tcase_add_test(tcase, "del_driver_unused_files", test_del_driver_unused_files);
 
        return suite;
 }