This proves that the stat counters increment as desired, as well as proving that our RUInt32 generator type works.
This commit is also a showcase of whether I can do 64-bit math in various languages (C's terseness in 'a == b + (c > d)' is annoying to replicate in languages that don't like playing fast and loose with types). :) --- python/t/620-stats.py | 77 +++++++++++++++ ocaml/tests/Makefile.am | 1 + ocaml/tests/test_620_stats.ml | 77 +++++++++++++++ golang/Makefile.am | 3 +- golang/libnbd_620_stats.go | 181 ++++++++++++++++++++++++++++++++++ 5 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 python/t/620-stats.py create mode 100644 ocaml/tests/test_620_stats.ml create mode 100644 golang/libnbd_620_stats.go diff --git a/python/t/620-stats.py b/python/t/620-stats.py new file mode 100644 index 00000000..62f8443f --- /dev/null +++ b/python/t/620-stats.py @@ -0,0 +1,77 @@ +# 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 + +h = nbd.NBD() + +# Pre-connection, stats start out at 0 +bs0 = h.stats_bytes_sent() +cs0 = h.stats_chunks_sent() +br0 = h.stats_bytes_received() +cr0 = h.stats_chunks_received() + +assert bs0 == 0 +assert cs0 == 0 +assert br0 == 0 +assert cr0 == 0 + +# Connection performs handshaking, which increments stats. +# The number of bytes/chunks here may grow over time as more features get +# automatically negotiated, so merely check that they are non-zero. +h.connect_command(["nbdkit", "-s", "--exit-with-parent", "null"]) + +bs1 = h.stats_bytes_sent() +cs1 = h.stats_chunks_sent() +br1 = h.stats_bytes_received() +cr1 = h.stats_chunks_received() + +assert cs1 > 0 +assert bs1 > cs1 +assert cr1 > 0 +assert br1 > cr1 + +# A flush command should be one chunk out, one chunk back (even if +# structured replies are in use) +h.flush() + +bs2 = h.stats_bytes_sent() +cs2 = h.stats_chunks_sent() +br2 = h.stats_bytes_received() +cr2 = h.stats_chunks_received() + +assert bs2 == bs1 + 28 +assert cs2 == cs1 + 1 +assert br2 == br1 + 16 # assumes nbdkit uses simple reply +assert cr2 == cr1 + 1 + +# Stats are still readable after the connection closes; we don't know if +# the server sent reply bytes to our NBD_CMD_DISC, so don't insist on it. +h.shutdown() + +bs3 = h.stats_bytes_sent() +cs3 = h.stats_chunks_sent() +br3 = h.stats_bytes_received() +cr3 = h.stats_chunks_received() + +assert bs3 > bs2 +assert cs3 == cs2 + 1 +assert br3 >= br2 +assert cr3 == cr2 + (br3 > br2) + +# Try to trigger garbage collection of h +h = None diff --git a/ocaml/tests/Makefile.am b/ocaml/tests/Makefile.am index 22cefb4d..c4751ad3 100644 --- a/ocaml/tests/Makefile.am +++ b/ocaml/tests/Makefile.am @@ -42,6 +42,7 @@ ML_TESTS = \ test_590_aio_copy.ml \ test_600_debug_callback.ml \ test_610_exception.ml \ + test_620_stats.ml \ $(NULL) EXTRA_DIST = $(ML_TESTS) diff --git a/ocaml/tests/test_620_stats.ml b/ocaml/tests/test_620_stats.ml new file mode 100644 index 00000000..648096fd --- /dev/null +++ b/ocaml/tests/test_620_stats.ml @@ -0,0 +1,77 @@ +(* 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 () = + let nbd = NBD.create () in + + (* Pre-connection, stats start out at 0 *) + let bs0 = NBD.stats_bytes_sent nbd in + let cs0 = NBD.stats_chunks_sent nbd in + let br0 = NBD.stats_bytes_received nbd in + let cr0 = NBD.stats_chunks_received nbd in + assert (bs0 = 0L); + assert (cs0 = 0L); + assert (br0 = 0L); + assert (cr0 = 0L); + + (* Connection performs handshaking, which increments stats. + * The number of bytes/chunks here may grow over time as more features get + * automatically negotiated, so merely check that they are non-zero. + *) + NBD.connect_command nbd ["nbdkit"; "-s"; "--exit-with-parent"; "null"]; + + let bs1 = NBD.stats_bytes_sent nbd in + let cs1 = NBD.stats_chunks_sent nbd in + let br1 = NBD.stats_bytes_received nbd in + let cr1 = NBD.stats_chunks_received nbd in + assert (cs1 > 0L); + assert (bs1 > cs1); + assert (cr1 > 0L); + assert (br1 > cr1); + + (* A flush command should be one chunk out, one chunk back (even if + * structured replies are in use) + *) + NBD.flush nbd; + + let bs2 = NBD.stats_bytes_sent nbd in + let cs2 = NBD.stats_chunks_sent nbd in + let br2 = NBD.stats_bytes_received nbd in + let cr2 = NBD.stats_chunks_received nbd in + assert (bs2 = (Int64.add bs1 28L)); + assert (cs2 = (Int64.succ cs1)); + assert (br2 = (Int64.add br1 16L)); (* assumes nbdkit uses simple reply *) + assert (cr2 = (Int64.succ cr1)); + + (* Stats are still readable after the connection closes; we don't know if + * the server sent reply bytes to our NBD_CMD_DISC, so don't insist on it. + *) + NBD.shutdown nbd; + + let bs3 = NBD.stats_bytes_sent nbd in + let cs3 = NBD.stats_chunks_sent nbd in + let br3 = NBD.stats_bytes_received nbd in + let cr3 = NBD.stats_chunks_received nbd in + let fudge = if cr2 = cr3 then 0L else 1L in + assert (bs3 > bs2); + assert (cs3 = (Int64.succ cs2)); + assert (br3 >= br2); + assert (cr3 = (Int64.add cr2 fudge)) + +let () = Gc.compact () diff --git a/golang/Makefile.am b/golang/Makefile.am index f170cbc4..765382d4 100644 --- a/golang/Makefile.am +++ b/golang/Makefile.am @@ -1,5 +1,5 @@ # nbd client library in userspace -# Copyright (C) 2013-2020 Red Hat Inc. +# 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 @@ -45,6 +45,7 @@ source_files = \ libnbd_590_aio_copy_test.go \ libnbd_600_debug_callback_test.go \ libnbd_610_error_test.go \ + libnbd_620_stats.go \ $(NULL) generator_built = \ diff --git a/golang/libnbd_620_stats.go b/golang/libnbd_620_stats.go new file mode 100644 index 00000000..8d566198 --- /dev/null +++ b/golang/libnbd_620_stats.go @@ -0,0 +1,181 @@ +/* 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" + +func Test620Stats(t *testing.T) { + h, err := Create() + if err != nil { + t.Fatalf("could not create handle: %s", err) + } + defer h.Close() + + /* Pre-connection, stats start out at 0 */ + bs0, err := h.StatsBytesSent() + if err != nil { + t.Fatalf("%s", err) + } + cs0, err := h.StatsChunksSent() + if err != nil { + t.Fatalf("%s", err) + } + br0, err := h.StatsBytesReceived() + if err != nil { + t.Fatalf("%s", err) + } + cr0, err := h.StatsChunksReceived() + if err != nil { + t.Fatalf("%s", err) + } + + if bs0 != 0 { + t.Fatalf("unexpected value for bs0") + } + if cs0 != 0 { + t.Fatalf("unexpected value for cs0") + } + if br0 != 0 { + t.Fatalf("unexpected value for br0") + } + if cr0 != 0 { + t.Fatalf("unexpected value for cr0") + } + + /* Connection performs handshaking, which increments stats. + * The number of bytes/chunks here may grow over time as more features + * get automatically negotiated, so merely check that they are non-zero. + */ + err = h.ConnectCommand([]string{ + "nbdkit", "-s", "--exit-with-parent", "null", + }) + if err != nil { + t.Fatalf("%s", err) + } + + bs1, err := h.StatsBytesSent() + if err != nil { + t.Fatalf("%s", err) + } + cs1, err := h.StatsChunksSent() + if err != nil { + t.Fatalf("%s", err) + } + br1, err := h.StatsBytesReceived() + if err != nil { + t.Fatalf("%s", err) + } + cr1, err := h.StatsChunksReceived() + if err != nil { + t.Fatalf("%s", err) + } + + if cs1 == 0 { + t.Fatalf("unexpected value for cs1") + } + if bs1 <= cs1 { + t.Fatalf("unexpected value for bs1") + } + if cr1 == 0 { + t.Fatalf("unexpected value for cr1") + } + if br1 <= cr1 { + t.Fatalf("unexpected value for br1") + } + + /* A flush command should be one chunk out, one chunk back (even if + * structured replies are in use) + */ + err = h.Flush(nil) + if err != nil { + t.Fatalf("%s", err) + } + + bs2, err := h.StatsBytesSent() + if err != nil { + t.Fatalf("%s", err) + } + cs2, err := h.StatsChunksSent() + if err != nil { + t.Fatalf("%s", err) + } + br2, err := h.StatsBytesReceived() + if err != nil { + t.Fatalf("%s", err) + } + cr2, err := h.StatsChunksReceived() + if err != nil { + t.Fatalf("%s", err) + } + + if bs2 != bs1 + 28 { + t.Fatalf("unexpected value for bs2") + } + if cs2 != cs1 + 1 { + t.Fatalf("unexpected value for cs2") + } + if br2 != br1 + 16 { /* assumes nbdkit uses simple reply */ + t.Fatalf("unexpected value for br2") + } + if cr2 != cr1 + 1 { + t.Fatalf("unexpected value for cr2") + } + + /* Stats are still readable after the connection closes; we don't know if + * the server sent reply bytes to our NBD_CMD_DISC, so don't insist on it. + */ + err = h.Shutdown(nil) + if err != nil { + t.Fatalf("%s", err) + } + + bs3, err := h.StatsBytesSent() + if err != nil { + t.Fatalf("%s", err) + } + cs3, err := h.StatsChunksSent() + if err != nil { + t.Fatalf("%s", err) + } + br3, err := h.StatsBytesReceived() + if err != nil { + t.Fatalf("%s", err) + } + cr3, err := h.StatsChunksReceived() + if err != nil { + t.Fatalf("%s", err) + } + slop := uint64(1) + if br2 == br3 { + slop = uint64(0) + } + + if bs3 <= bs2 { + t.Fatalf("unexpected value for bs3") + } + if cs3 != cs2 + 1 { + t.Fatalf("unexpected value for cs3") + } + if br3 < br2 { + t.Fatalf("unexpected value for br3") + } + if cr3 != cr2 + slop { + t.Fatalf("unexpected value for cr3") + } +} -- 2.37.2 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs