As promised in the previous patch, also test the new
nbd_opt_set_meta_context() API in Python, OCaml, and Golang.
---
 python/t/250-opt-set-meta.py                  | 126 ++++++++
 python/t/255-opt-set-meta-queries.py          |  76 +++++
 ocaml/tests/Makefile.am                       |   2 +
 ocaml/tests/test_250_opt_set_meta.ml          | 151 ++++++++++
 ocaml/tests/test_255_opt_set_meta_queries.ml  |  79 +++++
 tests/opt-set-meta-queries.c                  |   1 +
 tests/opt-set-meta.c                          |   1 +
 golang/Makefile.am                            |   2 +
 golang/libnbd_250_opt_set_meta_test.go        | 276 ++++++++++++++++++
 .../libnbd_255_opt_set_meta_queries_test.go   | 141 +++++++++
 10 files changed, 855 insertions(+)
 create mode 100644 python/t/250-opt-set-meta.py
 create mode 100644 python/t/255-opt-set-meta-queries.py
 create mode 100644 ocaml/tests/test_250_opt_set_meta.ml
 create mode 100644 ocaml/tests/test_255_opt_set_meta_queries.ml
 create mode 100644 golang/libnbd_250_opt_set_meta_test.go
 create mode 100644 golang/libnbd_255_opt_set_meta_queries_test.go

diff --git a/python/t/250-opt-set-meta.py b/python/t/250-opt-set-meta.py
new file mode 100644
index 00000000..c543a5d6
--- /dev/null
+++ b/python/t/250-opt-set-meta.py
@@ -0,0 +1,126 @@
+# libnbd Python bindings
+# Copyright (C) 2010-2022 Red Hat Inc.
+#
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import nbd
+
+
+count = 0
+seen = False
+
+
+def f(user_data, name):
+    global count
+    global seen
+    assert user_data == 42
+    count = count + 1
+    if name == nbd.CONTEXT_BASE_ALLOCATION:
+        seen = True
+
+
+def must_fail(f, *args, **kwds):
+    try:
+        f(*args, **kwds)
+        assert False
+    except nbd.Error:
+        pass
+
+
+# First process, with structured replies. Get into negotiating state.
+h = nbd.NBD()
+h.set_opt_mode(True)
+h.connect_command(["nbdkit", "-s", "--exit-with-parent", "-v",
+                   "memory", "size=1M"])
+
+# No contexts negotiated yet; can_meta should be error if any requested
+assert h.get_structured_replies_negotiated() is True
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is False
+h.add_meta_context(nbd.CONTEXT_BASE_ALLOCATION)
+must_fail(h.can_meta_context, nbd.CONTEXT_BASE_ALLOCATION)
+
+# FIXME: Once nbd_opt_structured_reply exists, check that set before
+# SR fails server-side, then enable SR for rest of process.
+
+# nbdkit does not match wildcard for SET, even though it does for LIST
+count = 0
+seen = False
+h.clear_meta_contexts()
+h.add_meta_context("base:")
+r = h.opt_set_meta_context(lambda *args: f(42, *args))
+assert r == count
+assert r == 0
+assert seen is False
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is False
+
+# Negotiating with no contexts is not an error, but selects nothing
+count = 0
+seen = False
+h.clear_meta_contexts()
+r = h.opt_set_meta_context(lambda *args: f(42, *args))
+assert r == 0
+assert r == count
+assert seen is False
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is False
+
+# Request 2 with expectation of 1; with set_request_meta_context off
+count = 0
+seen = False
+h.add_meta_context("x-nosuch:context")
+h.add_meta_context(nbd.CONTEXT_BASE_ALLOCATION)
+h.set_request_meta_context(False)
+r = h.opt_set_meta_context(lambda *args: f(42, *args))
+assert r == 1
+assert count == 1
+assert seen is True
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is True
+
+# Transition to transmission phase; our last set should remain active
+h.clear_meta_contexts()
+h.add_meta_context("x-nosuch:context")
+h.opt_go()
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is True
+
+# Now too late to set; but should not lose earlier state
+count = 0
+seen = False
+must_fail(h.opt_set_meta_context, lambda *args: f(42, *args))
+assert count == 0
+assert seen is False
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is True
+
+h.shutdown()
+
+# Second process, this time without structured replies server-side.
+h = nbd.NBD()
+h.set_opt_mode(True)
+h.add_meta_context(nbd.CONTEXT_BASE_ALLOCATION)
+h.connect_command(["nbdkit", "-s", "--exit-with-parent", "-v",
+                   "memory", "size=1M", "--no-sr"])
+assert h.get_structured_replies_negotiated() is False
+
+# Expect server-side failure here
+count = 0
+seen = False
+must_fail(h.opt_set_meta_context, lambda *args: f(42, *args))
+assert count == 0
+assert seen is False
+must_fail(h.can_meta_context, nbd.CONTEXT_BASE_ALLOCATION)
+
+# Even though can_meta fails after failed SET, it returns 0 after go
+h.opt_go()
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is False
+
+h.shutdown()
diff --git a/python/t/255-opt-set-meta-queries.py 
b/python/t/255-opt-set-meta-queries.py
new file mode 100644
index 00000000..54a10bc0
--- /dev/null
+++ b/python/t/255-opt-set-meta-queries.py
@@ -0,0 +1,76 @@
+# libnbd Python bindings
+# Copyright (C) 2010-2022 Red Hat Inc.
+#
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import nbd
+
+
+count = 0
+seen = False
+
+
+def f(user_data, name):
+    global count
+    global seen
+    assert user_data == 42
+    count = count + 1
+    if name == nbd.CONTEXT_BASE_ALLOCATION:
+        seen = True
+
+
+# Get into negotiating state.
+h = nbd.NBD()
+h.set_opt_mode(True)
+h.connect_command(["nbdkit", "-s", "--exit-with-parent", "-v",
+                   "memory", "size=1M"])
+
+# nbdkit does not match wildcard for SET, even though it does for LIST
+count = 0
+seen = False
+r = h.opt_set_meta_context_queries(["base:"], lambda *args: f(42, *args))
+assert r == count
+assert r == 0
+assert seen is False
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is False
+
+# Negotiating with no contexts is not an error, but selects nothing.
+# An explicit empty list overrides a non-empty implicit list.
+count = 0
+seen = False
+h.add_meta_context(nbd.CONTEXT_BASE_ALLOCATION)
+r = h.opt_set_meta_context_queries([], lambda *args: f(42, *args))
+assert r == 0
+assert r == count
+assert seen is False
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is False
+
+# Request 2 with expectation of 1.
+count = 0
+seen = False
+r = h.opt_set_meta_context_queries(
+    ["x-nosuch:context", nbd.CONTEXT_BASE_ALLOCATION],
+    lambda *args: f(42, *args))
+assert r == 1
+assert count == 1
+assert seen is True
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is True
+
+# Transition to transmission phase; our last set should remain active
+h.set_request_meta_context(False)
+h.opt_go()
+assert h.can_meta_context(nbd.CONTEXT_BASE_ALLOCATION) is True
+
+h.shutdown()
diff --git a/ocaml/tests/Makefile.am b/ocaml/tests/Makefile.am
index 1b4d1135..328d53e5 100644
--- a/ocaml/tests/Makefile.am
+++ b/ocaml/tests/Makefile.am
@@ -32,6 +32,8 @@ ML_TESTS = \
        test_230_opt_info.ml \
        test_240_opt_list_meta.ml \
        test_245_opt_list_meta_queries.ml \
+       test_250_opt_set_meta.ml \
+       test_255_opt_set_meta_queries.ml \
        test_300_get_size.ml \
        test_400_pread.ml \
        test_405_pread_structured.ml \
diff --git a/ocaml/tests/test_250_opt_set_meta.ml 
b/ocaml/tests/test_250_opt_set_meta.ml
new file mode 100644
index 00000000..f35012fd
--- /dev/null
+++ b/ocaml/tests/test_250_opt_set_meta.ml
@@ -0,0 +1,151 @@
+(* hey emacs, this is OCaml code: -*- tuareg -*- *)
+(* libnbd OCaml test case
+ * Copyright (C) 2013-2022 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+let count = ref 0
+let seen = ref false
+let f user_data name =
+  assert (user_data = 42);
+  count := !count + 1;
+  if name = NBD.context_base_allocation then
+    seen := true;
+  0
+
+let () =
+  (* First process, with structured replies. Get into negotiating state. *)
+  let nbd = NBD.create () in
+  NBD.set_opt_mode nbd true;
+  NBD.connect_command nbd
+                      ["nbdkit"; "-s"; "--exit-with-parent"; "-v";
+                       "memory"; "size=1M"];
+
+  (* No contexts negotiated yet; can_meta should be error if any requested *)
+  let sr = NBD.get_structured_replies_negotiated nbd in
+  assert sr;
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert (not m);
+  NBD.add_meta_context nbd NBD.context_base_allocation;
+  (try
+     let _ = NBD.can_meta_context nbd NBD.context_base_allocation in
+     assert false
+   with
+     NBD.Error (errstr, errno) -> ()
+  );
+
+  (* FIXME: Once nbd_opt_structured_reply exists, check that set before
+   * SR fails server-side, then enable SR for rest of process.
+   *)
+
+  (* nbdkit does not match wildcard for SET, even though it does for LIST *)
+  count := 0;
+  seen := false;
+  NBD.clear_meta_contexts nbd;
+  NBD.add_meta_context nbd "base:";
+  let r = NBD.opt_set_meta_context nbd (f 42) in
+  assert (r = !count);
+  assert (r = 0);
+  assert (not !seen);
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert (not m);
+
+  (* Negotiating with no contexts is not an error, but selects nothing *)
+  count := 0;
+  seen := false;
+  NBD.clear_meta_contexts nbd;
+  let r = NBD.opt_set_meta_context nbd (f 42) in
+  assert (r = 0);
+  assert (r = !count);
+  assert (not !seen);
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert (not m);
+
+  (* Request 2 with expectation of 1; with set_request_meta_context off *)
+  count := 0;
+  seen := false;
+  NBD.add_meta_context nbd "x-nosuch:context";
+  NBD.add_meta_context nbd NBD.context_base_allocation;
+  NBD.set_request_meta_context nbd false;
+  let r = NBD.opt_set_meta_context nbd (f 42) in
+  assert (r = 1);
+  assert (r = !count);
+  assert !seen;
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert m;
+
+  (* Transition to transmission phase; our last set should remain active *)
+  NBD.clear_meta_contexts nbd;
+  NBD.add_meta_context nbd "x-nosuch:context";
+  NBD.opt_go nbd;
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert m;
+
+  (* Now too late to set; but should not lose earlier state *)
+  count := 0;
+  seen := false;
+  (try
+     let _ = NBD.opt_set_meta_context nbd (f 42) in
+     assert false
+   with
+     NBD.Error (errstr, errno) -> ()
+  );
+  assert (0 = !count);
+  assert (not !seen);
+  let s = NBD.get_size nbd in
+  assert (s = 1048576_L);
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert m;
+
+  NBD.shutdown nbd;
+
+  (* Second process, this time without structured replies server-side. *)
+  let nbd = NBD.create () in
+  NBD.set_opt_mode nbd true;
+  NBD.add_meta_context nbd NBD.context_base_allocation;
+  NBD.connect_command nbd
+                      ["nbdkit"; "-s"; "--exit-with-parent"; "-v";
+                       "memory"; "size=1M"; "--no-sr"];
+  let sr = NBD.get_structured_replies_negotiated nbd in
+  assert (not sr);
+
+  (* Expect server-side failure here *)
+  count := 0;
+  seen := false;
+  NBD.add_meta_context nbd "base:";
+  (try
+     let _ = NBD.opt_set_meta_context nbd (f 42) in
+     assert false
+   with
+     NBD.Error (errstr, errno) -> ()
+  );
+  assert (0 = !count);
+  assert (not !seen);
+  (try
+     let _ = NBD.can_meta_context nbd NBD.context_base_allocation in
+     assert false
+   with
+     NBD.Error (errstr, errno) -> ()
+  );
+
+  (* Even though can_meta fails after failed SET, it returns 0 after go *)
+  NBD.opt_go nbd;
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert (not m);
+
+  NBD.shutdown nbd
+
+let () = Gc.compact ()
diff --git a/ocaml/tests/test_255_opt_set_meta_queries.ml 
b/ocaml/tests/test_255_opt_set_meta_queries.ml
new file mode 100644
index 00000000..86e27e1f
--- /dev/null
+++ b/ocaml/tests/test_255_opt_set_meta_queries.ml
@@ -0,0 +1,79 @@
+(* hey emacs, this is OCaml code: -*- tuareg -*- *)
+(* libnbd OCaml test case
+ * Copyright (C) 2013-2022 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+let count = ref 0
+let seen = ref false
+let f user_data name =
+  assert (user_data = 42);
+  count := !count + 1;
+  if name = NBD.context_base_allocation then
+    seen := true;
+  0
+
+let () =
+  (* Get into negotiating state. *)
+  let nbd = NBD.create () in
+  NBD.set_opt_mode nbd true;
+  NBD.connect_command nbd
+                      ["nbdkit"; "-s"; "--exit-with-parent"; "-v";
+                       "memory"; "size=1M"];
+
+  (* nbdkit does not match wildcard for SET, even though it does for LIST *)
+  count := 0;
+  seen := false;
+  let r = NBD.opt_set_meta_context_queries nbd ["base:"] (f 42) in
+  assert (r = !count);
+  assert (r = 0);
+  assert (not !seen);
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert (not m);
+
+  (* Negotiating with no contexts is not an error, but selects nothing.
+   * An explicit empty list overrides a non-empty implicit list.
+   *)
+  count := 0;
+  seen := false;
+  NBD.add_meta_context nbd NBD.context_base_allocation;
+  let r = NBD.opt_set_meta_context_queries nbd [] (f 42) in
+  assert (r = 0);
+  assert (r = !count);
+  assert (not !seen);
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert (not m);
+
+  (* Request 2 with expectation of 1. *)
+  count := 0;
+  seen := false;
+  let r = NBD.opt_set_meta_context_queries nbd
+            ["x-nosuch:context"; NBD.context_base_allocation] (f 42) in
+  assert (r = 1);
+  assert (r = !count);
+  assert !seen;
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert m;
+
+  (* Transition to transmission phase; our last set should remain active *)
+  NBD.set_request_meta_context nbd false;
+  NBD.opt_go nbd;
+  let m = NBD.can_meta_context nbd NBD.context_base_allocation in
+  assert m;
+
+  NBD.shutdown nbd
+
+let () = Gc.compact ()
diff --git a/tests/opt-set-meta-queries.c b/tests/opt-set-meta-queries.c
index 0b162607..2c3ee29a 100644
--- a/tests/opt-set-meta-queries.c
+++ b/tests/opt-set-meta-queries.c
@@ -17,6 +17,7 @@
  */

 /* Test behavior of nbd_opt_set_meta_context_queries. */
+/* See also unit test 255 in the various language ports. */

 #include <config.h>

diff --git a/tests/opt-set-meta.c b/tests/opt-set-meta.c
index 95f5a6d7..a4366d24 100644
--- a/tests/opt-set-meta.c
+++ b/tests/opt-set-meta.c
@@ -17,6 +17,7 @@
  */

 /* Test behavior of nbd_opt_set_meta_context. */
+/* See also unit test 250 in the various language ports. */

 #include <config.h>

diff --git a/golang/Makefile.am b/golang/Makefile.am
index 6adf6ae8..0808a639 100644
--- a/golang/Makefile.am
+++ b/golang/Makefile.am
@@ -36,6 +36,8 @@ source_files = \
        libnbd_230_opt_info_test.go \
        libnbd_240_opt_list_meta_test.go \
        libnbd_245_opt_list_meta_queries_test.go \
+       libnbd_250_opt_set_meta_test.go \
+       libnbd_255_opt_set_meta_queries_test.go \
        libnbd_300_get_size_test.go \
        libnbd_400_pread_test.go \
        libnbd_405_pread_structured_test.go \
diff --git a/golang/libnbd_250_opt_set_meta_test.go 
b/golang/libnbd_250_opt_set_meta_test.go
new file mode 100644
index 00000000..1740d83e
--- /dev/null
+++ b/golang/libnbd_250_opt_set_meta_test.go
@@ -0,0 +1,276 @@
+/* libnbd golang tests
+ * Copyright (C) 2013-2022 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package libnbd
+
+import "testing"
+
+var set_count uint
+var set_seen bool
+
+func setmetaf(user_data int, name string) int {
+       if user_data != 42 {
+               panic("expected user_data == 42")
+       }
+       set_count++
+       if (name == context_base_allocation) {
+               set_seen = true
+       }
+       return 0
+}
+
+func Test250OptSetMeta(t *testing.T) {
+       /* First process, with structured replies. Get into negotiating state. 
*/
+       h, err := Create()
+       if err != nil {
+               t.Fatalf("could not create handle: %s", err)
+       }
+       defer h.Close()
+
+       err = h.SetOptMode(true)
+       if err != nil {
+               t.Fatalf("could not set opt mode: %s", err)
+       }
+
+       err = h.ConnectCommand([]string{
+               "nbdkit", "-s", "--exit-with-parent", "-v",
+               "memory", "size=1M",
+       })
+       if err != nil {
+               t.Fatalf("could not connect: %s", err)
+       }
+
+       /* No contexts negotiated yet; CanMeta should be error if any requested 
*/
+       sr, err := h.GetStructuredRepliesNegotiated()
+       if err != nil {
+               t.Fatalf("could not check structured replies negotiated: %s", 
err)
+       }
+       if !sr {
+               t.Fatalf("unexpected structured replies state")
+       }
+       meta, err := h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+       err = h.AddMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not request add_meta_context: %s", err)
+       }
+       _, err = h.CanMetaContext(context_base_allocation)
+       if err == nil {
+               t.Fatalf("expected error")
+       }
+
+       /* FIXME: Once OptStructuredReply exists, check that set before
+        * SR fails server-side, then enable SR for rest of process.
+        */
+
+       /* nbdkit does not match wildcard for SET, even though it does for LIST 
*/
+       set_count = 0
+       set_seen = false
+       err = h.ClearMetaContexts()
+       if err != nil {
+               t.Fatalf("could not request clear_meta_contexts: %s", err)
+       }
+       err = h.AddMetaContext("base:")
+       if err != nil {
+               t.Fatalf("could not request add_meta_context: %s", err)
+       }
+       r, err := h.OptSetMetaContext(func(name string) int {
+               return setmetaf(42, name)
+       })
+       if err != nil {
+               t.Fatalf("could not request opt_set_meta_context: %s", err)
+       }
+       if r != set_count || r != 0 || set_seen {
+               t.Fatalf("unexpected set_count after opt_set_meta_context")
+       }
+
+       /* Negotiating with no contexts is not an error, but selects nothing */
+       set_count = 0
+       set_seen = false
+       err = h.ClearMetaContexts()
+       if err != nil {
+               t.Fatalf("could not request clear_meta_contexts: %s", err)
+       }
+       r, err = h.OptSetMetaContext(func(name string) int {
+               return setmetaf(42, name)
+       })
+       if err != nil {
+               t.Fatalf("could not request opt_set_meta_context: %s", err)
+       }
+       if r != set_count || r != 0 || set_seen {
+               t.Fatalf("unexpected set_count after opt_set_meta_context")
+       }
+       meta, err = h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       /* Request 2 with expectation of 1; with SetRequestMetaContext off */
+       set_count = 0
+       set_seen = false
+       err = h.AddMetaContext("x-nosuch:context")
+       if err != nil {
+               t.Fatalf("could not request add_meta_context: %s", err)
+       }
+       err = h.AddMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not request add_meta_context: %s", err)
+       }
+       err = h.SetRequestMetaContext(false)
+       if err != nil {
+               t.Fatalf("could not set_request_meta_context: %s", err)
+       }
+       r, err = h.OptSetMetaContext(func(name string) int {
+               return setmetaf(42, name)
+       })
+       if err != nil {
+               t.Fatalf("could not request opt_set_meta_context: %s", err)
+       }
+       if r != 1 || r != set_count || !set_seen {
+               t.Fatalf("unexpected set_count after opt_set_meta_context")
+       }
+       meta, err = h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if !meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       /* Transition to transmission phase; our last set should remain active 
*/
+       err = h.ClearMetaContexts()
+       if err != nil {
+               t.Fatalf("could not request clear_meta_contexts: %s", err)
+       }
+       err = h.AddMetaContext("x-nosuch:context")
+       if err != nil {
+               t.Fatalf("could not request add_meta_context: %s", err)
+       }
+       err = h.OptGo()
+       if err != nil {
+               t.Fatalf("could not request opt_go: %s", err)
+       }
+       meta, err = h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if !meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       /* Now too late to set; but should not lose earlier state */
+       set_count = 0
+       set_seen = false
+       _, err = h.OptSetMetaContext(func(name string) int {
+               return setmetaf(42, name)
+       })
+       if err == nil {
+               t.Fatalf("expected error")
+       }
+       if set_count != 0 || set_seen {
+               t.Fatalf("unexpected set_count after opt_set_meta_context")
+       }
+       meta, err = h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if !meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       err = h.Shutdown(nil)
+       if err != nil {
+               t.Fatalf("could not request shutdown: %s", err)
+       }
+
+       /* Second process, this time without structured replies server-side. */
+       h, err = Create()
+       if err != nil {
+               t.Fatalf("could not create handle: %s", err)
+       }
+       defer h.Close()
+
+       err = h.SetOptMode(true)
+       if err != nil {
+               t.Fatalf("could not set opt mode: %s", err)
+       }
+
+       err = h.AddMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not request add_meta_context: %s", err)
+       }
+
+       err = h.ConnectCommand([]string{
+               "nbdkit", "-s", "--exit-with-parent", "-v",
+               "memory", "size=1M", "--no-sr",
+       })
+       if err != nil {
+               t.Fatalf("could not connect: %s", err)
+       }
+
+       sr, err = h.GetStructuredRepliesNegotiated()
+       if err != nil {
+               t.Fatalf("could not check structured replies negotiated: %s", 
err)
+       }
+       if sr {
+               t.Fatalf("unexpected structured replies state")
+       }
+
+       /* Expect server-side failure here */
+       set_count = 0
+       set_seen = false
+       _, err = h.OptSetMetaContext(func(name string) int {
+               return setmetaf(42, name)
+       })
+       if err == nil {
+               t.Fatalf("expected error")
+       }
+       if set_count != 0 || set_seen {
+               t.Fatalf("unexpected set_count after opt_set_meta_context")
+       }
+       _, err = h.CanMetaContext(context_base_allocation)
+       if err == nil {
+               t.Fatalf("expected error")
+       }
+
+       /* Even though CanMeta fails after failed SET, it returns 0 after go */
+       err = h.OptGo()
+       if err != nil {
+               t.Fatalf("could not request opt_go: %s", err)
+       }
+       meta, err = h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       err = h.Shutdown(nil)
+       if err != nil {
+               t.Fatalf("could not request shutdown: %s", err)
+       }
+}
diff --git a/golang/libnbd_255_opt_set_meta_queries_test.go 
b/golang/libnbd_255_opt_set_meta_queries_test.go
new file mode 100644
index 00000000..96b37d76
--- /dev/null
+++ b/golang/libnbd_255_opt_set_meta_queries_test.go
@@ -0,0 +1,141 @@
+/* libnbd golang tests
+ * Copyright (C) 2013-2022 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package libnbd
+
+import "testing"
+
+var setq_count uint
+var setq_seen bool
+
+func setmetaqf(user_data int, name string) int {
+       if user_data != 42 {
+               panic("expected user_data == 42")
+       }
+       setq_count++
+       if (name == context_base_allocation) {
+               setq_seen = true
+       }
+       return 0
+}
+
+func Test255OptSetMetaQueries(t *testing.T) {
+       /* Get into negotiating state. */
+       h, err := Create()
+       if err != nil {
+               t.Fatalf("could not create handle: %s", err)
+       }
+       defer h.Close()
+
+       err = h.SetOptMode(true)
+       if err != nil {
+               t.Fatalf("could not set opt mode: %s", err)
+       }
+
+       err = h.ConnectCommand([]string{
+               "nbdkit", "-s", "--exit-with-parent", "-v",
+               "memory", "size=1M",
+       })
+       if err != nil {
+               t.Fatalf("could not connect: %s", err)
+       }
+
+       /* nbdkit does not match wildcard for SET, even though it does for LIST 
*/
+       setq_count = 0
+       setq_seen = false
+       r, err := h.OptSetMetaContextQueries([]string{"base:"},
+               func(name string) int {
+                       return setmetaqf(42, name)
+               })
+       if err != nil {
+               t.Fatalf("could not request opt_set_meta_context_queries: %s", 
err)
+       }
+       if r != setq_count || r != 0 || setq_seen {
+               t.Fatalf("unexpected count after opt_set_meta_context_queries")
+       }
+
+       /* Negotiating with no contexts is not an error, but selects nothing.
+        * An explicit empty list overrides a non-empty implicit list.
+        */
+       setq_count = 0
+       setq_seen = false
+       err = h.AddMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not request add_meta_context: %s", err)
+       }
+       r, err = h.OptSetMetaContextQueries([]string{}, func(name string) int {
+               return setmetaqf(42, name)
+       })
+       if err != nil {
+               t.Fatalf("could not request opt_set_meta_context_queries: %s", 
err)
+       }
+       if r != setq_count || r != 0 || setq_seen {
+               t.Fatalf("unexpected set_count after 
opt_set_meta_context_queries")
+       }
+       meta, err := h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       /* Request 2 with expectation of 1; with SetRequestMetaContext off */
+       setq_count = 0
+       setq_seen = false
+       r, err = h.OptSetMetaContextQueries([]string{
+               "x-nosuch:context", context_base_allocation},
+               func(name string) int {
+               return setmetaqf(42, name)
+               })
+       if err != nil {
+               t.Fatalf("could not request opt_set_meta_context_queries: %s", 
err)
+       }
+       if r != 1 || r != setq_count || !setq_seen {
+               t.Fatalf("unexpected set_count after 
opt_set_meta_context_queries")
+       }
+       meta, err = h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if !meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       /* Transition to transmission phase; our last set should remain active 
*/
+       err = h.SetRequestMetaContext(false)
+       if err != nil {
+               t.Fatalf("could not set_request_meta_context: %s", err)
+       }
+       err = h.OptGo()
+       if err != nil {
+               t.Fatalf("could not request opt_go: %s", err)
+       }
+       meta, err = h.CanMetaContext(context_base_allocation)
+       if err != nil {
+               t.Fatalf("could not check can meta context: %s", err)
+       }
+       if !meta {
+               t.Fatalf("unexpected can meta context state")
+       }
+
+       err = h.Shutdown(nil)
+       if err != nil {
+               t.Fatalf("could not request shutdown: %s", err)
+       }
+}
-- 
2.37.3

_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to