"""IPVarch test cases. Each case is a dict: name : id shown on pass/fail args : argv passed after the binary name exit : expected exit code (default 0) must : substrings required in stdout ('stderr:' prefix targets stderr) must_not : substrings forbidden in stdout validator : key of a structural validator run on stdout (see runner.py) repeat : run the case this many times (for random/statistical checks) """ def case(name, args, exit=0, must=None, must_not=None, validator=None, repeat=1): return { "name": name, "args": args, "exit": exit, "must": must or [], "must_not": must_not or [], "validator": validator, "repeat": repeat, } GROUPS = [ ("info: IPv4", [ case("public class A", ["8.8.8.8"], must=["Address: 8.8.8.8", "Type: IPv4", "Class: A", "Scope: Public", "Octets: 8, 8, 8, 8"]), case("public class C", ["198.140.10.1"], must=["Class: C", "Scope: Public"]), case("private class C", ["192.168.1.1"], must=["Class: C", "Scope: Private (RFC 1918)", "Octets: 192, 168, 1, 1"]), case("private class A", ["10.0.0.1"], must=["Class: A", "Scope: Private (RFC 1918)"]), case("private class B", ["172.16.254.1"], must=["Class: B", "Scope: Private (RFC 1918)"]), case("class D multicast", ["224.0.0.1"], must=["Class: D", "Scope: Multicast"]), case("class E reserved", ["240.0.0.1"], must=["Class: E", "Scope: Reserved"]), case("loopback", ["127.0.0.1"], must=["Scope: Loopback"]), case("this-network zero", ["0.0.0.0"], must=["Class: 0 (reserved)", "Scope: Reserved (this network)"]), case("class B public", ["172.32.0.1"], must=["Class: B", "Scope: Public"]), case("link-local APIPA", ["169.254.9.9"], must=["Scope: Link-local (APIPA)"]), ]), ("info: IPv6", [ case("documentation 2001:db8", ["2001:db8::1"], must=["Scope: Documentation (2001:db8::/32)"]), case("documentation 3fff", ["3fff:ffff::1"], must=["Scope: Public (global unicast)"]), case("documentation 2620:4f", ["2620:4f:0:0:0:0:0:1"], must=["Scope: Public (global unicast)"]), case("plain global unicast", ["2610:0:0:0:0:0:0:1"], must=["Address: 2610::1", "Type: IPv6", "Groups: 2610 0000 0000 0000 0000 0000 0000 0001"]), case("link-local", ["fe80::1"], must=["Address: fe80::1", "Scope: Link-local (fe80::/10)"]), case("loopback", ["::1"], must=["Address: ::1", "Scope: Loopback (::1)", "Groups: 0000 0000 0000 0000 0000 0000 0000 0001"]), case("unspecified", ["::"], must=["Address: ::", "Scope: Unspecified (::)", "Groups: 0000 0000 0000 0000 0000 0000 0000 0000"]), case("multicast", ["ff02::1"], must=["Scope: Multicast (ff00::/8)"]), case("unique local", ["fd12::1"], must=["Scope: Private (unique local, fc00::/7)"]), case("embedded IPv4 tail", ["2001:db8::192.168.1.1"], must=["Address: 2001:db8::c0a8:101", "Groups: 2001 0db8 0000 0000 0000 0000 c0a8 0101"]), case("IPv4-mapped", ["::ffff:192.168.1.1"], must=["Address: ::ffff:c0a8:101", "Scope: IPv4-mapped (::ffff:0:0/96)"]), case("no classes in IPv6", ["::1"], must=["Class: n/a (IPv6 has no address classes)"]), ]), ("canonicalization round-trips", [ case("expanded form compresses", ["2001:0db8:0000:0000:0000:0000:0000:0001"], must=["Address: 2001:db8::1"]), case("expanded octets normalize", ["2001:0db8:0000:0000:0000:0000:0000:0001"], must=["Groups: 2001 0db8 0000 0000 0000 0000 0000 0001"]), case("run of zeros compressed", ["2001:db8:0:0:0:0:2:1"], must=["Address: 2001:db8::2:1"]), case("longest run wins", ["2001:0:0:1:0:0:0:1"], must=["Address: 2001:0:0:1::1"]), case("run of one not compressed", ["1:2:3:4:5:6:0:8"], must=["Address: 1:2:3:4:5:6:0:8"]), case("full uncompressed round-trip", ["1:2:3:4:5:6:7:8"], must=["Address: 1:2:3:4:5:6:7:8"]), case("embedded IPv4 expanded head", ["0:0:0:0:0:ffff:1.2.3.4"], must=["Address: ::ffff:102:304"]), case("leading zero group", ["0db8:0000:0000:0000:0000:0000:0000:0001"], must=["Address: db8::1"]), ]), ("network plan", [ case("/24 plan", ["--plan-network", "192.168.1.0/24"], must=["IPVarch Network Plan - 192.168.1.0 /24", "255.255.255.0 /24", "192.168.1.255", "192.168.1.1 - 192.168.1.254", "254", "192.168.1.1", "192.168.1.2", "192.168.1.100 - 192.168.1.254"], validator="plan"), case("/8 plan", ["--plan-network", "10.0.0.0/8"], must=["IPVarch Network Plan - 10.0.0.0 /8", "255.0.0.0 /8", "10.255.255.255", "10.0.0.1 - 10.255.255.254", "16777214", "10.0.0.100 - 10.0.0.254"], validator="plan"), case("/16 plan", ["--plan-network", "172.18.0.0/16"], must=["IPVarch Network Plan - 172.18.0.0 /16", "255.255.0.0 /16", "172.18.255.255", "172.18.0.1 - 172.18.255.254", "65534", "172.18.0.100 - 172.18.0.254"], validator="plan"), case("/32 single-host plan", ["--plan-network", "192.168.1.7/32"], must=["IPVarch Network Plan - 192.168.1.7 /32", "255.255.255.255 /32", "n/a (single host)", "192.168.1.7", "1", "disabled (single-host network)"], validator="plan"), case("gateway is first usable", ["--plan-network", "192.168.1.0/24"], validator="plan-gateway"), ]), ("random networks", [ case("random private /8", ["--random-ipv4network", "private", "/8"], must=["Network address", "DHCP pool"], validator="random-private", repeat=3), case("random private /16", ["--random-ipv4network", "private", "/16"], validator="random-private", repeat=10), case("random private /24", ["--random-ipv4network", "private", "/24"], validator="random-private", repeat=20), case("random private /32", ["--random-ipv4network", "private", "/32"], validator="random-private", repeat=5), case("random public /24", ["--random-ipv4network", "public", "/24"], validator="random-public", repeat=20), case("random public /32", ["--random-ipv4network", "public", "/32"], validator="random-public", repeat=5), ]), ("invalid inputs", [ case("octet too big", ["256.1.1.1"], exit=1, must=["stderr:ipvarch: error:"]), case("too few octets", ["1.2.3"], exit=1, must=["stderr:ipvarch: error:"]), case("trailing dot", ["1.2.3.4."], exit=1), case("empty octet", ["1..2.3"], exit=1), case("four-digit octet", ["1.2.3.0001"], exit=1), case("too many groups", ["1:2:3:4:5:6:7:8:9"], exit=1), case("double ::", ["::1::2"], exit=1), case("trailing colon", ["1:2:3:4::5:"], exit=1), case("colon then garbage", [":::g:1"], exit=1), case("hex overflow group", ["::fffff"], exit=1), case("embedded IPv4 in head", ["1.2.3.4::"], exit=1), case("embedded IPv4 not last", ["::1.2.3.4:5"], exit=1), case("garbage text", ["not an ip at all"], exit=1, must_not=["Address:"]), case("info mode rejects mask", ["192.168.1.1/24"], exit=1, must=["stderr:--plan-network"]), case("plan missing argument", ["--plan-network"], exit=1), case("plan unsupported prefix", ["--plan-network", "192.168.1.0/12"], exit=1, must=["stderr:/8 /16 /24 /32"]), case("plan host bits set", ["--plan-network", "10.5.0.182/8"], exit=1, must=["stderr:10.0.0.0/8"]), case("plan host bits set /16", ["--plan-network", "192.168.1.0/16"], exit=1, must=["stderr:192.168.0.0/16"]), case("plan zero network", ["--plan-network", "0.0.0.0/8"], exit=1), case("plan multicast network", ["--plan-network", "224.0.0.0/24"], exit=1), case("plan reserved network", ["--plan-network", "250.0.0.0/8"], exit=1), case("random bad scope", ["--random-ipv4network", "residential", "/24"], exit=1), case("random bad prefix", ["--random-ipv4network", "private", "/12"], exit=1), case("random missing argument", ["--random-ipv4network", "private"], exit=1), ]), ("edge & boundaries", [ case("limited broadcast", ["255.255.255.255"], must=["Class: E", "Scope: Reserved"]), case("leading zeros accepted", ["010.000.000.001"], must=["Address: 10.0.0.1", "Octets: 10, 0, 0, 1"]), case(":: with tail-only IPv4", ["::1.2.3.4"], must=["Address: ::102:304"]), case("all-zero long form", ["0:0:0:0:0:0:0:0"], must=["Address: ::"]), case("full hex range", ["fedc:ba98:7654:3210:fedc:ba98:7654:3210"], must=["Address: fedc:ba98:7654:3210:fedc:ba98:7654:3210"]), case("79-char garbage (buffer boundary)", ["a" * 79], exit=1), case("63-hex-group garbage", ["1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16"], exit=1), case("upper-case IPv6 accepted", ["2001:0DB8::1"], must=["Address: 2001:db8::1"]), ]), ("usage", [ case("-h", ["-h"], must=["Usage:", "IPVarch"]), case("--help", ["--help"], must=["Usage:"]), case("help", ["help"], must=["Usage:"]), case("no arguments", [], exit=1, must=["Usage:"]), case("usage examples are plannable", ["--help"], validator="usage-examples"), ]), ]