# # # add_file "unix/tester-check-net.c" # content [bb758c2b16c6a1978a5a96f4ddd204b6feac6e37] # # add_file "win32/tester-check-net.c" # content [939b4453fe11bc0e1883797f0052ca45f760ea1a] # # patch "Makefile.am" # from [f38db1b6891b304f75527eb30853aa570ff15b4d] # to [4eba20c4f0654b122564fe6845edbc7f5cd55f18] # # patch "debian/changelog" # from [8df684e2d19b17b984d67b3657d756dae8f14159] # to [89118785c13abb11285cb1315e6c16276f896dec] # # patch "lua-testsuite.lua" # from [41e44b694e1d68e32ba5bf9bca044a11f48f1028] # to [670f9ca900ae63d2091e68af4561a1870bfc2a6f] # # patch "tests/common/netsync.lua" # from [3e28b68bf122aefa0f55f5d3dfb80338f55bba2b] # to [3740674f76b3efe676df404047e219b604bc3be5] # ============================================================ --- unix/tester-check-net.c bb758c2b16c6a1978a5a96f4ddd204b6feac6e37 +++ unix/tester-check-net.c bb758c2b16c6a1978a5a96f4ddd204b6feac6e37 @@ -0,0 +1,283 @@ +/* Copyright (C) 2008 Zack Weinberg + + This program is made available under the GNU GPL version 2.0 or + greater. See the accompanying file COPYING for details. + + This program is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. */ + +/* The test suite runs this program to decide whether or not to include + network tests. It determines whether it is possible to create a + listening socket on a randomly chosen port on the loopback interface, + connect to that socket from another process, and ping-pong a byte. + + Will exit successfully, with no output, if everything works; otherwise, + will exit unsuccessfully and produce diagnostics on stderr. */ + +#include "config.h" +#if defined HAVE_SOCKET && defined HAVE_NETINET_IN_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static int synchronizer[2]; +static const char *who; +static unsigned short port; + +static void sigalrm(int unused) +{ + fprintf(stderr, "%s: timeout\n", who); + exit(1); +} + +static void prep_timeout(const char *w) +{ + who = w; + signal(SIGALRM, sigalrm); + alarm(5); +} + +/* "b_or_c" should be either "bind" or "connect". Conveniently, they have + the same signature. */ +static int get_socket(int (*b_or_c)(int, const struct sockaddr *, socklen_t)) +{ + int sfd; + + /* try IPv4 first */ + { + struct sockaddr_in sin; + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + sfd = socket(PF_INET, SOCK_STREAM, 0); + if (sfd >= 0) + { + if (b_or_c(sfd, (struct sockaddr *)&sin, sizeof sin) == 0) + return sfd; + close(sfd); + } + } + + /* if that didn't work, and we have library support for it, try IPv6 too */ +#ifdef AF_INET6 + { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(port); + sin6.sin6_addr = in6addr_loopback; + + sfd = socket(PF_INET6, SOCK_STREAM, 0); + if (sfd >= 0) + { + if (b_or_c(sfd, (struct sockaddr *)&sin6, sizeof sin6) == 0) + return sfd; + close(sfd); + } + } +#endif + + fprintf(stderr, "socket/connect/bind: %s\n", strerror(errno)); + return -1; +} + +static int server(void) +{ + int sfd, cfd, n; + char buf; + + prep_timeout("server"); + + sfd = get_socket(bind); + if (sfd < 0) + return 1; + + if (listen(sfd, 1)) + { + fprintf(stderr, "server: listen: %s\n", strerror(errno)); + close(sfd); + return 1; + } + + /* Client process may proceed. */ + n = write(synchronizer[1], "x", 1); + if (n != 1) + { + fprintf(stderr, "server: semaphore write: %s\n", + n == 0 ? "unexpected EOF" : strerror(errno)); + close(sfd); + return 1; + } + + cfd = accept(sfd, 0, 0); /* don't care _who_ connects */ + if (cfd < 0) + { + fprintf(stderr, "server: accept: %s\n", strerror(errno)); + close(sfd); + return 1; + } + + n = read(cfd, &buf, 1); + if (n != 1) + { + fprintf(stderr, "server: socket read: %s\n", + n == 0 ? "unexpected EOF" : strerror(errno)); + close(cfd); + close(sfd); + return 1; + } + if (buf != 'x') + { + fprintf(stderr, "server: socket read: got '%c' exp 'x'\n", buf); + close(cfd); + close(sfd); + return 1; + } + n = write(cfd, "x", 1); + if (n != 1) + { + fprintf(stderr, "server: socket write: %s\n", + n == 0 ? "unexpected EOF" : strerror(errno)); + close(cfd); + close(sfd); + return 1; + } + + close(cfd); + close(sfd); + return 0; +} + +static int client(void) +{ + int sfd, n; + char buf; + + prep_timeout("client"); + + /* wait for server setup */ + n = read(synchronizer[0], &buf, 1); + if (n != 1) + { + fprintf(stderr, "client: semaphore read: %s\n", + n == 0 ? "unexpected EOF" : strerror(errno)); + return 1; + } + + sfd = get_socket(connect); + if (sfd < 0) + return 1; + + n = write(sfd, "x", 1); + if (n != 1) + { + fprintf(stderr, "client: socket write: %s\n", + n == 0 ? "unexpected EOF" : strerror(errno)); + close(sfd); + return 1; + } + + n = read(sfd, &buf, 1); + if (n != 1) + { + fprintf(stderr, "client: socket read: %s\n", + n == 0 ? "unexpected EOF" : strerror(errno)); + close(sfd); + return 1; + } + if (buf != 'x') + { + fprintf(stderr, "client: socket read: got '%c' exp 'x'\n", buf); + close(sfd); + return 1; + } + + close(sfd); + return 0; +} + +int main(void) +{ + pid_t child, p; + int status; + + /* Pick a random port in the high half of the range, thus + unlikely to be used for anything. */ + srand(time(0)); + do + { + port = rand(); + } + while (port < 32767); + + if (pipe(synchronizer)) + { + fprintf(stderr, "setup: pipe: %s\n", strerror(errno)); + return 2; + } + + child = fork(); + if (child < 0) + { + fprintf(stderr, "setup: fork: %s\n", strerror(errno)); + return 2; + } + + if (child == 0) + return client(); + + if (server()) + return 1; + + p = wait(&status); + if (p < 0) + { + fprintf(stderr, "teardown: wait: %s\n", strerror(errno)); + return 2; + } + if (p != child) + { + fprintf(stderr, "teardown: unexpected child %d != %d\n", p, child); + return 2; + } + if (!WIFEXITED(status)) + { + fprintf(stderr, "teardown: child crash, status %d\n", status); + return 2; + } + + return WEXITSTATUS(status); +} + +#else /* no socket, or no netinet/in.h */ + +int main(void) +{ + fprintf(stderr, "socket headers are missing, cannot test networking\n"); + return 1; +} + +#endif + +/* + Local Variables: + mode: C + fill-column: 76 + c-file-style: "gnu" + indent-tabs-mode: nil + End: + vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: + */ ============================================================ --- win32/tester-check-net.c 939b4453fe11bc0e1883797f0052ca45f760ea1a +++ win32/tester-check-net.c 939b4453fe11bc0e1883797f0052ca45f760ea1a @@ -0,0 +1,6 @@ +/* Stub - needs implementing. See lua-testsuite.lua (very end) for rationale, + and unix/tester-check-net.c for behavior to emulate. */ +int main(void) +{ + return 0; +} ============================================================ --- Makefile.am f38db1b6891b304f75527eb30853aa570ff15b4d +++ Makefile.am 4eba20c4f0654b122564fe6845edbc7f5cd55f18 @@ -454,11 +454,13 @@ if WIN32_PLATFORM lib3rdparty_a_CPPFLAGS += -DWIN32 lib3rdparty_a_SOURCES += botan/es_capi.cpp botan/es_win32.cpp tester_SOURCES += win32/tester-plaf.cc + check_net_SOURCES = win32/tester-check-net.c else libplatform_a_SOURCES += $(UNIX_PLATFORM_SOURCES) mtn_SOURCES += unix/main.cc lib3rdparty_a_SOURCES += botan/es_dev.cpp tester_SOURCES += unix/tester-plaf.cc + check_net_SOURCES = unix/tester-check-net.c endif if MISSING_INET_PTON @@ -648,9 +650,9 @@ run_%_tests: Makefile %_tests.status: run_%_tests %-testsuite.lua tester$(EXEEXT) FORCE +./run_$*_tests -unit_tests.status : unit_tester -lua_tests.status : mtn -check_PROGRAMS = unit_tester tester +unit_tests.status : unit_tester$(EXEEXT) +lua_tests.status : mtn$(EXEEXT) check_net$(EXEEXT) +check_PROGRAMS = unit_tester tester check_net # We want the tests re-run even if the .status files already exist. # .PHONY does not work for that (bad interaction with pattern rules), ============================================================ --- debian/changelog 8df684e2d19b17b984d67b3657d756dae8f14159 +++ debian/changelog 89118785c13abb11285cb1315e6c16276f896dec @@ -1,5 +1,8 @@ monotone (0.40-2) unstable; urgency=low monotone (0.40-2) unstable; urgency=low + * Automatically skip all tests involving netsync if it is impossible + to use TCP on the loopback interface. (Closes: #474280) + * Install HTML documentation in /usr/share/doc/monotone/html, not /usr/share/doc/html. (Closes: #480072) * Make /usr/share/doc/monotone-server and /usr/share/doc/monotone-doc @@ -9,7 +12,7 @@ monotone (0.40-2) unstable; urgency=low * Register monotone manual with doc-base. * Improve description of monotone-doc package. - -- Zack Weinberg Sat, 24 May 2008 09:30:07 -0400 + -- Zack Weinberg Sun, 25 May 2008 00:10:11 -0400 monotone (0.40-1) unstable; urgency=low ============================================================ --- lua-testsuite.lua 41e44b694e1d68e32ba5bf9bca044a11f48f1028 +++ lua-testsuite.lua 670f9ca900ae63d2091e68af4561a1870bfc2a6f @@ -1,6 +1,7 @@ monotone_path = nil #!./tester monotone_path = nil +no_network_tests = false function safe_mtn(...) if monotone_path == nil then @@ -357,6 +358,36 @@ function prepare_to_run_tests (P) end unlogged_remove(d) + -- Several tests require the ability to run a network server on + -- the loopback interface, and connect to it from another process + -- on this computer. Unlike the above, we just skip those tests + -- (loudly) if we can't do that. Verifying that this is possible + -- requires a helper program. + + local checknet = getpathof("check_net") + if checknet ~= nil then + writefile_q("in", nil) + prepare_redirect("in", "out", "err") + local status = execute(checknet) + local out = readfile_q("out") + local err = readfile_q("err") + + if status == 0 and err == "" and out == "" then + logfile:write("check_net: Can use the loopback interface.\n") + else + logfile:write(string.format("check_net: failed with status %d\n".. + "stdout:\n%s\nstderr:\n%s\n", + status, out, err)) + P("warning: network unavailable, skipping network server tests\n") + no_network_tests = true + end + else + P("warning: check_net helper is missing, skipping network server tests\n") + no_network_tests = true + end + + -- Record the full version of monotone under test in the logfile. + monotone_path = getpathof("mtn") if monotone_path == nil then monotone_path = "mtn" end set_env("mtn", monotone_path) ============================================================ --- tests/common/netsync.lua 3e28b68bf122aefa0f55f5d3dfb80338f55bba2b +++ tests/common/netsync.lua 3740674f76b3efe676df404047e219b604bc3be5 @@ -1,3 +1,7 @@ +-- Conveniently, all network tests load this file, so this skip_if +-- suffices to skip all network tests if the network is unavailable +-- (see lua-testsuite.lua and [platform]/tester-check-net.c). +skip_if(no_network_tests) function mtn2(...) return mtn("--db=test2.db", "--keydir=keys2", unpack(arg))