torture/spoolss: issue GetJob after StartDocPrinter
[obnox/samba/samba-obnox.git] / source4 / torture / rpc / spoolss.c
index b3180039eb0696a049d1ef3177de20180962e892..23f501d7401802021f4f663ef1567a04ccde1cd1 100644 (file)
@@ -77,8 +77,8 @@ struct test_spoolss_context {
        union spoolss_PortInfo *ports[3];
 
        /* for EnumPrinterDrivers */
-       uint32_t driver_count[8];
-       union spoolss_DriverInfo *drivers[8];
+       uint32_t driver_count[9];
+       union spoolss_DriverInfo *drivers[9];
 
        /* for EnumMonitors */
        uint32_t monitor_count[3];
@@ -513,30 +513,36 @@ static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
        return true;
 }
 
-static bool test_EnumPrinterDrivers_args(struct torture_context *tctx,
-                                        struct dcerpc_binding_handle *b,
-                                        const char *server_name,
-                                        const char *environment,
-                                        uint32_t level,
-                                        uint32_t *count_p,
-                                        union spoolss_DriverInfo **info_p)
+static bool test_EnumPrinterDrivers_buffers(struct torture_context *tctx,
+                                           struct dcerpc_binding_handle *b,
+                                           const char *server_name,
+                                           const char *environment,
+                                           uint32_t level,
+                                           uint32_t offered,
+                                           uint32_t *count_p,
+                                           union spoolss_DriverInfo **info_p)
 {
        struct spoolss_EnumPrinterDrivers r;
        uint32_t needed;
        uint32_t count;
        union spoolss_DriverInfo *info;
+       DATA_BLOB buffer;
+
+       if (offered > 0) {
+               buffer = data_blob_talloc_zero(tctx, offered);
+       }
 
        r.in.server             = server_name;
        r.in.environment        = environment;
        r.in.level              = level;
-       r.in.buffer             = NULL;
-       r.in.offered            = 0;
+       r.in.buffer             = offered ? &buffer : NULL;
+       r.in.offered            = offered;
        r.out.needed            = &needed;
        r.out.count             = &count;
        r.out.info              = &info;
 
-       torture_comment(tctx, "Testing EnumPrinterDrivers(%s) level %u\n",
-               r.in.environment, r.in.level);
+       torture_comment(tctx, "Testing EnumPrinterDrivers(%s) level %u, offered: %u\n",
+               r.in.environment, r.in.level, r.in.offered);
 
        torture_assert_ntstatus_ok(tctx,
                dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r),
@@ -567,6 +573,20 @@ static bool test_EnumPrinterDrivers_args(struct torture_context *tctx,
 
 }
 
+
+static bool test_EnumPrinterDrivers_args(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        const char *server_name,
+                                        const char *environment,
+                                        uint32_t level,
+                                        uint32_t *count_p,
+                                        union spoolss_DriverInfo **info_p)
+{
+       return test_EnumPrinterDrivers_buffers(tctx, b, server_name,
+                                              environment, level, 0,
+                                              count_p, info_p);
+}
+
 static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx,
                                            struct dcerpc_binding_handle *b,
                                            const char *server_name,
@@ -585,7 +605,7 @@ static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx,
                "failed to enumerate printer drivers");
 
        for (i=0; i < count; i++) {
-               const char *driver_name_ret;
+               const char *driver_name_ret = "";
                switch (level) {
                case 1:
                        driver_name_ret = info[i].info1.driver_name;
@@ -642,6 +662,7 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
        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 };
+       uint16_t buffer_sizes[] = { 0, 1024, 6040, 0xffff };
        int i, j, a;
 
        /* FIXME: gd, come back and fix "" as server, and handle
@@ -655,6 +676,15 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
 
        for (a=0;a<ARRAY_SIZE(environments);a++) {
 
+       for (i=0;i<ARRAY_SIZE(buffer_sizes);i++) {
+               torture_assert(tctx,
+                       test_EnumPrinterDrivers_buffers(tctx, b, server_name,
+                                                       environments[a], 3,
+                                                       buffer_sizes[i],
+                                                       NULL, NULL),
+                       "failed to enumerate drivers");
+       }
+
        for (i=0;i<ARRAY_SIZE(levels);i++) {
                int level = levels[i];
                uint32_t count;
@@ -681,7 +711,7 @@ static bool test_EnumPrinterDrivers(struct torture_context *tctx,
 
                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];
+                       union spoolss_DriverInfo *ref = &ctx->drivers[7][j];
 
                        switch (level) {
                        case 1:
@@ -1247,8 +1277,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;
 }
@@ -1438,8 +1469,6 @@ 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,
@@ -1518,6 +1547,9 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                0x80000000 */
        };
 
+       torture_skip(tctx, "Printer Info test is currently broken, skipping");
+
+
        ZERO_STRUCT(devmode_ctr);
        ZERO_STRUCT(secdesc_ctr);
 
@@ -1622,11 +1654,13 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                }
 
 #define TEST_PRINTERINFO_STRING_EXP_ERR(lvl1, field1, lvl2, field2, value, err) do { \
+               void *p; \
                torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
                q.in.level = lvl1; \
                TESTGETCALL(GetPrinter, q) \
                info_ctr.level = lvl1; \
-               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
+               p = (void *)&q.out.info->info ## lvl1; \
+               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
                info_ctr.info.info ## lvl1->field1 = value;\
                TESTSETCALL_EXP(SetPrinter, s, err) \
                info_ctr.info.info ## lvl1->field1 = ""; \
@@ -1635,7 +1669,8 @@ static bool test_PrinterInfo(struct torture_context *tctx,
                STRING_EQUAL(info_ctr.info.info ## lvl1->field1, value, field1); \
                q.in.level = lvl2; \
                TESTGETCALL(GetPrinter, q) \
-               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)(void *)&q.out.info->info ## lvl2; \
+               p = (void *)&q.out.info->info ## lvl2; \
+               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \
                STRING_EQUAL(info_ctr.info.info ## lvl2->field2, value, field2); \
        } while (0)
 
@@ -1644,20 +1679,24 @@ static bool test_PrinterInfo(struct torture_context *tctx,
        } while (0);
 
 #define TEST_PRINTERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value) do { \
+               void *p; \
                torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
                q.in.level = lvl1; \
                TESTGETCALL(GetPrinter, q) \
                info_ctr.level = lvl1; \
-               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
+               p = (void *)&q.out.info->info ## lvl1; \
+               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
                info_ctr.info.info ## lvl1->field1 = value; \
                TESTSETCALL(SetPrinter, s) \
                info_ctr.info.info ## lvl1->field1 = 0; \
                TESTGETCALL(GetPrinter, q) \
-               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
+               p = (void *)&q.out.info->info ## lvl1; \
+               info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
                INT_EQUAL(info_ctr.info.info ## lvl1->field1, exp_value, field1); \
                q.in.level = lvl2; \
                TESTGETCALL(GetPrinter, q) \
-               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)(void *)&q.out.info->info ## lvl2; \
+               p = (void *)&q.out.info->info ## lvl2; \
+               info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \
                INT_EQUAL(info_ctr.info.info ## lvl2->field2, exp_value, field1); \
        } while (0)
 
@@ -1865,13 +1904,14 @@ static bool test_sd_set_level(struct torture_context *tctx,
        struct spoolss_DevmodeContainer devmode_ctr;
        struct sec_desc_buf secdesc_ctr;
        union spoolss_SetPrinterInfo sinfo;
+       union spoolss_PrinterInfo info;
+       struct spoolss_SetPrinterInfo3 info3;
 
        ZERO_STRUCT(devmode_ctr);
        ZERO_STRUCT(secdesc_ctr);
 
        switch (level) {
        case 2: {
-               union spoolss_PrinterInfo info;
                torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
                torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
 
@@ -1881,7 +1921,6 @@ static bool test_sd_set_level(struct torture_context *tctx,
                break;
        }
        case 3: {
-               struct spoolss_SetPrinterInfo3 info3;
 
                info3.sec_desc_ptr = NULL;
 
@@ -2131,7 +2170,6 @@ static bool test_devicemode_full(struct torture_context *tctx,
 {
        struct spoolss_SetPrinter s;
        struct spoolss_GetPrinter q;
-       struct spoolss_GetPrinter q0;
        struct spoolss_SetPrinterInfoCtr info_ctr;
        struct spoolss_SetPrinterInfo8 info8;
        union spoolss_PrinterInfo info;
@@ -2147,7 +2185,8 @@ static bool test_devicemode_full(struct torture_context *tctx,
                TESTGETCALL(GetPrinter, q) \
                info_ctr.level = lvl1; \
                if (lvl1 == 2) {\
-                       info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)(void *)&q.out.info->info ## lvl1; \
+                       void *p = (void *)&q.out.info->info ## lvl1; \
+                       info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
                } else if (lvl1 == 8) {\
                        info_ctr.info.info ## lvl1 = &info8; \
                }\
@@ -2183,7 +2222,6 @@ static bool test_devicemode_full(struct torture_context *tctx,
 
        q.in.handle = handle;
        q.out.info = &info;
-       q0 = q;
 
 #if 0
        const char *devicename;/* [charset(UTF16)] */
@@ -3109,6 +3147,7 @@ static bool test_GetJob_args(struct torture_context *tctx,
        return true;
 }
 
+#if 0
 static bool test_GetJob(struct torture_context *tctx,
                        struct dcerpc_binding_handle *b,
                        struct policy_handle *handle,
@@ -3125,6 +3164,7 @@ static bool test_GetJob(struct torture_context *tctx,
 
        return true;
 }
+#endif
 
 static bool test_SetJob(struct torture_context *tctx,
                        struct dcerpc_binding_handle *b,
@@ -3198,11 +3238,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;
@@ -3213,6 +3255,7 @@ static bool test_EnumJobs_args(struct torture_context *tctx,
                               struct dcerpc_binding_handle *b,
                               struct policy_handle *handle,
                               uint32_t level,
+                              WERROR werr_expected,
                               uint32_t *count_p,
                               union spoolss_JobInfo **info_p)
 {
@@ -3246,13 +3289,15 @@ static bool test_EnumJobs_args(struct torture_context *tctx,
                status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r);
 
                torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
-               torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
+               torture_assert_werr_equal(tctx, r.out.result, werr_expected,
+                                         "EnumJobs failed");
                torture_assert(tctx, info, "No jobs returned");
 
                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");
+               torture_assert_werr_equal(tctx, r.out.result, werr_expected,
+                                         "EnumJobs failed");
        }
 
        if (count_p) {
@@ -3265,10 +3310,111 @@ static bool test_EnumJobs_args(struct torture_context *tctx,
        return true;
 }
 
-static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
+static bool test_JobPropertiesEnum(struct torture_context *tctx,
+                                  struct dcerpc_binding_handle *b,
+                                  struct policy_handle *handle,
+                                  uint32_t job_id)
+{
+       struct spoolss_RpcEnumJobNamedProperties r;
+       uint32_t pcProperties = 0;
+       struct RPC_PrintNamedProperty *ppProperties = NULL;
+
+       r.in.hPrinter = handle;
+       r.in.JobId = job_id;
+       r.out.pcProperties = &pcProperties;
+       r.out.ppProperties = &ppProperties;
+
+       torture_comment(tctx, "Testing RpcEnumJobNamedProperties(%d)\n", 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");
+
+       return true;
+}
+
+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;
+
+       r.in.hPrinter = handle;
+       r.in.JobId = job_id;
+       r.in.pProperty = property;
+
+       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_common(struct torture_context *tctx,
                                         struct dcerpc_binding_handle *b,
                                         struct policy_handle *handle,
                                         const char *document_name,
+                                        const char *datatype,
                                         uint32_t *job_id)
 {
        NTSTATUS status;
@@ -3290,7 +3436,7 @@ static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
 
        info1.document_name     = document_name;
        info1.output_file       = NULL;
-       info1.datatype          = "RAW";
+       info1.datatype          = datatype;
 
        info_ctr.level          = 1;
        info_ctr.info.info1     = &info1;
@@ -3300,6 +3446,9 @@ static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
        torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed");
 
        for (i=1; i < 4; i++) {
+               union spoolss_JobInfo ginfo;
+               bool ok;
+
                torture_comment(tctx, "Testing StartPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
 
                sp.in.handle            = handle;
@@ -3309,6 +3458,11 @@ static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
                                           "dcerpc_spoolss_StartPagePrinter failed");
                torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed");
 
+               ok = test_GetJob_args(tctx, b, handle, *job_id, 1, &ginfo);
+               if (!ok) {
+                       torture_comment(tctx, "test_GetJob failed for JobId[%d]\n", *job_id);
+               }
+
                torture_comment(tctx, "Testing WritePrinter: Page[%d], JobId[%d]\n", i, *job_id);
 
                w.in.handle             = handle;
@@ -3339,6 +3493,29 @@ static bool test_DoPrintTest_add_one_job(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,
+                                        const char *document_name,
+                                        uint32_t *job_id)
+{
+       test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "RAW", job_id);
+
+       return true;
+}
+
+static bool test_DoPrintTest_add_one_job_v4(struct torture_context *tctx,
+                                        struct dcerpc_binding_handle *b,
+                                        struct policy_handle *handle,
+                                        const char *document_name,
+                                        uint32_t *job_id)
+{
+       test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "XPS_PASS", job_id);
+
+       return true;
+}
+
+
 static bool test_DoPrintTest_check_jobs(struct torture_context *tctx,
                                        struct dcerpc_binding_handle *b,
                                        struct policy_handle *handle,
@@ -3354,7 +3531,7 @@ static bool test_DoPrintTest_check_jobs(struct torture_context *tctx,
                "AddJob failed");
 
        torture_assert(tctx,
-               test_EnumJobs_args(tctx, b, handle, 1, &count, &info),
+               test_EnumJobs_args(tctx, b, handle, 1, WERR_OK, &count, &info),
                "EnumJobs level 1 failed");
 
        torture_assert_int_equal(tctx, count, num_jobs, "unexpected number of jobs in queue");
@@ -3441,6 +3618,14 @@ static bool test_DoPrintTest(struct torture_context *tctx,
                ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
        }
 
+       for (i=0; i < num_jobs; i++) {
+               ret &= test_DoPrintTest_add_one_job_v4(tctx, b, handle, "TorturePrintJob v4", &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");
        }
@@ -3477,6 +3662,170 @@ static bool test_DoPrintTest_extended(struct torture_context *tctx,
        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)
@@ -3543,6 +3892,37 @@ static bool test_ResumePrinter(struct torture_context *tctx,
        return true;
 }
 
+static bool test_printer_purge(struct torture_context *tctx,
+                              struct dcerpc_binding_handle *b,
+                              struct policy_handle *handle)
+{
+       NTSTATUS status;
+       struct spoolss_SetPrinter r;
+       struct spoolss_SetPrinterInfoCtr info_ctr;
+       struct spoolss_DevmodeContainer devmode_ctr;
+       struct sec_desc_buf secdesc_ctr;
+
+       info_ctr.level = 0;
+       info_ctr.info.info0 = NULL;
+
+       ZERO_STRUCT(devmode_ctr);
+       ZERO_STRUCT(secdesc_ctr);
+
+       r.in.handle             = handle;
+       r.in.info_ctr           = &info_ctr;
+       r.in.devmode_ctr        = &devmode_ctr;
+       r.in.secdesc_ctr        = &secdesc_ctr;
+       r.in.command            = SPOOLSS_PRINTER_CONTROL_PURGE;
+
+       torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PURGE\n");
+
+       status = dcerpc_spoolss_SetPrinter_r(b, tctx, &r);
+       torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+       torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+       return true;
+}
+
 static bool test_GetPrinterData_checktype(struct torture_context *tctx,
                                          struct dcerpc_binding_handle *b,
                                          struct policy_handle *handle,
@@ -4675,7 +5055,7 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
                                      struct policy_handle *hive_handle,
                                      const char *server_name_slash)
 {
-       WERROR result;
+       WERROR result = WERR_OK;
        union spoolss_DriverInfo info;
        const char *driver_key;
        struct policy_handle key_handle;
@@ -4692,6 +5072,8 @@ static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
        const char *driver_version;
        const char *inbox_driver_version;
 
+       ZERO_STRUCT(key_handle);
+
        torture_comment(tctx, "Testing Driver Info and winreg consistency\n");
 
        driver_key = talloc_asprintf(tctx, "%s\\%s\\Drivers\\Version-%d\\%s",
@@ -5556,6 +5938,106 @@ static bool test_print_processors_winreg(struct torture_context *tctx,
        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,
@@ -5725,7 +6207,7 @@ static bool test_SecondaryClosePrinter(struct torture_context *tctx,
                                       struct policy_handle *handle)
 {
        NTSTATUS status;
-       struct dcerpc_binding *b;
+       const struct dcerpc_binding *binding2;
        struct dcerpc_pipe *p2;
        struct spoolss_ClosePrinter cp;
 
@@ -5736,10 +6218,8 @@ static bool test_SecondaryClosePrinter(struct torture_context *tctx,
 
        torture_comment(tctx, "Testing close on secondary pipe\n");
 
-       status = dcerpc_parse_binding(tctx, p->conn->binding_string, &b);
-       torture_assert_ntstatus_ok(tctx, status, "Failed to parse dcerpc binding");
-
-       status = dcerpc_secondary_connection(p, &p2, b);
+       binding2 = p->binding;
+       status = dcerpc_secondary_connection(p, &p2, binding2);
        torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
 
        status = dcerpc_bind_auth_none(p2, &ndr_table_spoolss);
@@ -6533,7 +7013,7 @@ static bool test_EnumPrinters_servername(struct torture_context *tctx,
        return true;
 }
 
-
+#if 0
 static bool test_GetPrinterDriver(struct torture_context *tctx,
                                  struct dcerpc_binding_handle *b,
                                  struct policy_handle *handle,
@@ -6568,6 +7048,7 @@ static bool test_GetPrinterDriver(struct torture_context *tctx,
 
        return true;
 }
+#endif
 
 static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
                                         struct dcerpc_binding_handle *b,
@@ -7268,6 +7749,7 @@ static bool compose_local_driver_directory(struct torture_context *tctx,
        return true;
 }
 
+#if 0
 static struct spoolss_DeviceMode *torture_devicemode(TALLOC_CTX *mem_ctx,
                                                     const char *devicename)
 {
@@ -7308,6 +7790,7 @@ static struct spoolss_DeviceMode *torture_devicemode(TALLOC_CTX *mem_ctx,
 
        return r;
 }
+#endif
 
 static bool test_architecture_buffer(struct torture_context *tctx,
                                     void *private_data)
@@ -7500,7 +7983,6 @@ static bool torture_rpc_spoolss_printer_setup_common(struct torture_context *tct
 
        t->driver.local.driver_directory= "/usr/share/cups/drivers";
 
-       t->info2.drivername             = "Microsoft XPS Document Writer";
        t->info2.portname               = "LPT1:";
 
        printer_name = t->info2.printername;
@@ -7518,6 +8000,8 @@ static bool torture_rpc_spoolss_printer_setup_common(struct torture_context *tct
                                               &t->driver.local.driver_directory),
                "failed to compose local driver directory");
 
+       t->info2.drivername             = "Microsoft XPS Document Writer";
+
        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);
@@ -7527,6 +8011,16 @@ static bool torture_rpc_spoolss_printer_setup_common(struct torture_context *tct
 
        torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) does not exist on the server\n",
                t->info2.drivername, t->driver.remote.environment);
+
+       t->info2.drivername             = "Microsoft XPS Document Writer v4";
+
+       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;
+       }
+
        torture_comment(tctx, "trying to upload own driver\n");
 
        if (!directory_exist(t->driver.local.driver_directory)) {
@@ -7628,6 +8122,7 @@ static bool torture_rpc_spoolss_printerexwkn_setup(struct torture_context *tctx,
        return torture_rpc_spoolss_printer_setup_common(tctx, t);
 }
 
+#if 0
 static bool torture_rpc_spoolss_printerdm_setup(struct torture_context *tctx, void **data)
 {
        struct torture_printer_context *t;
@@ -7641,6 +8136,7 @@ static bool torture_rpc_spoolss_printerdm_setup(struct torture_context *tctx, vo
 
        return torture_rpc_spoolss_printer_setup_common(tctx, t);
 }
+#endif
 
 static bool torture_rpc_spoolss_printer_teardown_common(struct torture_context *tctx, struct torture_printer_context *t)
 {
@@ -7737,6 +8233,33 @@ static bool test_print_test_extended(struct torture_context *tctx,
        return ret;
 }
 
+static bool test_print_test_properties(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 (torture_setting_bool(tctx, "samba3", false)) {
+               torture_skip(tctx, "skip printer job property tests against samba");
+       }
+
+       torture_assert(tctx,
+               test_PausePrinter(tctx, b, &t->handle),
+               "failed to pause printer");
+
+       torture_assert(tctx,
+               test_DoPrintTest_properties(tctx, b, &t->handle),
+               "failed to test print job properties");
+
+       torture_assert(tctx,
+               test_ResumePrinter(tctx, b, &t->handle),
+               "failed to resume printer");
+
+       return true;
+}
+
 /* use smbd file IO to spool a print job */
 static bool test_print_test_smbd(struct torture_context *tctx,
                                 void *private_data)
@@ -7767,18 +8290,17 @@ static bool test_print_test_smbd(struct torture_context *tctx,
        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));
+       status = smb2_connect(mem_ctx,
+                             torture_setting_string(tctx, "host", NULL),
+                             lpcfg_smb_ports(tctx->lp_ctx),
+                             share,
+                             lpcfg_resolve_context(tctx->lp_ctx),
+                             credentials,
+                             &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));
@@ -7794,7 +8316,8 @@ static bool test_print_test_smbd(struct torture_context *tctx,
 
        /* check back end spoolss job was created */
        torture_assert(tctx,
-               test_EnumJobs_args(tctx, b, &t->handle, 1, &count, &info),
+               test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+                                  &count, &info),
                "EnumJobs level 1 failed");
 
        for (i = 0; i < count; i++) {
@@ -7813,6 +8336,60 @@ static bool test_print_test_smbd(struct torture_context *tctx,
        return true;
 }
 
+static bool test_print_test_purge(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;
+       uint32_t num_jobs = 8;
+       uint32_t *job_ids;
+       int i;
+       bool ret = true;
+       uint32_t count;
+       union spoolss_JobInfo *info;
+
+       torture_assert(tctx,
+               test_PausePrinter(tctx, b, &t->handle),
+               "failed to pause printer");
+
+       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, &t->handle,
+                                                  "TorturePrintJob",
+                                                  &job_ids[i]);
+               torture_assert(tctx, ret, "failed to add print job");
+       }
+
+       torture_assert(tctx,
+               test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+                                  &count, &info),
+               "EnumJobs level 1 failed");
+
+       torture_assert_int_equal(tctx, count, num_jobs,
+                                "unexpected number of jobs in queue");
+
+       torture_assert(tctx,
+               test_printer_purge(tctx, b, &t->handle),
+               "failed to purge printer");
+
+       torture_assert(tctx,
+               test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+                                  &count, &info),
+               "EnumJobs level 1 failed");
+
+       torture_assert_int_equal(tctx, count, 0,
+                                "unexpected number of jobs in queue");
+
+       torture_assert(tctx,
+               test_ResumePrinter(tctx, b, &t->handle),
+               "failed to resume printer");
+
+       return true;
+}
+
 static bool test_printer_sd(struct torture_context *tctx,
                            void *private_data)
 {
@@ -8076,6 +8653,190 @@ static bool test_printer_ic(struct torture_context *tctx,
        return true;
 }
 
+static bool test_printer_bidi(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 spoolss_RpcSendRecvBidiData r;
+       struct RPC_BIDI_REQUEST_CONTAINER bidi_req;
+       struct RPC_BIDI_RESPONSE_CONTAINER *bidi_rep = NULL;
+
+       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;
+
+       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_printer_set_publish(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;
+
+       info7.guid = "";
+       info7.action = DSPRINT_PUBLISH;
+
+       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_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 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");
+       }
+
+       return true;
+}
+
+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;
+
+       info7.action = DSPRINT_UNPUBLISH;
+       info7.guid = "";
+
+       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_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");
+       }
+
+       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;
+}
 
 static bool test_driver_info_winreg(struct torture_context *tctx,
                                    void *private_data)
@@ -8096,6 +8857,86 @@ static bool test_driver_info_winreg(struct torture_context *tctx,
        return true;
 }
 
+static bool test_print_job_enum(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;
+       uint32_t num_jobs = 8;
+       uint32_t *job_ids;
+       int i;
+       union spoolss_JobInfo *info = NULL;
+       uint32_t count;
+
+       torture_assert(tctx,
+               test_PausePrinter(tctx, b, &t->handle),
+               "failed to pause printer");
+
+       /* purge in case of any jobs from previous tests */
+       torture_assert(tctx,
+               test_printer_purge(tctx, b, &t->handle),
+               "failed to purge printer");
+
+       /* enum before jobs, valid level */
+       torture_assert(tctx,
+                      test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+                                         &count, &info),
+                      "EnumJobs with valid level");
+       torture_assert_int_equal(tctx, count, 0, "EnumJobs count");
+       torture_assert(tctx,
+                      test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK,
+                                         &count, &info),
+                      "EnumJobs with valid level");
+       torture_assert_int_equal(tctx, count, 0, "EnumJobs count");
+       /* enum before jobs, invalid level - expect failure */
+       torture_assert(tctx,
+                      test_EnumJobs_args(tctx, b, &t->handle, 100,
+                                         WERR_INVALID_LEVEL,
+                                         &count, &info),
+                      "EnumJobs with invalid level");
+
+       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, &t->handle,
+                                                   "TorturePrintJob",
+                                                   &job_ids[i]);
+               torture_assert(tctx, ret, "failed to add print job");
+       }
+
+       /* enum after jobs, valid level */
+       torture_assert(tctx,
+                      test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+                                         &count, &info),
+                      "EnumJobs with valid level");
+       torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count");
+       torture_assert(tctx,
+                      test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK,
+                                         &count, &info),
+                      "EnumJobs with valid level");
+       torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count");
+       /* enum after jobs, invalid level - expect failure */
+       torture_assert(tctx,
+                      test_EnumJobs_args(tctx, b, &t->handle, 100,
+                                         WERR_INVALID_LEVEL,
+                                         &count, &info),
+                      "EnumJobs with invalid level");
+
+       for (i = 0; i < num_jobs; i++) {
+               test_SetJob(tctx, b, &t->handle, job_ids[i], NULL,
+                           SPOOLSS_JOB_CONTROL_DELETE);
+       }
+
+       torture_assert(tctx,
+               test_ResumePrinter(tctx, b, &t->handle),
+               "failed to resume printer");
+
+       return true;
+}
+
 void torture_tcase_printer(struct torture_tcase *tcase)
 {
        torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter_wrap);
@@ -8103,6 +8944,8 @@ void torture_tcase_printer(struct torture_tcase *tcase)
        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, "print_test_purge", test_print_test_purge);
        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);
@@ -8118,6 +8961,10 @@ void torture_tcase_printer(struct torture_tcase *tcase)
        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);
+       torture_tcase_add_simple_test(tcase, "print_job_enum", test_print_job_enum);
 }
 
 struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx)
@@ -8188,6 +9035,7 @@ struct torture_suite *torture_rpc_spoolss(TALLOC_CTX *mem_ctx)
        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);