mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-01-11 09:59:59 +08:00
3c41a39252
There's a philosophy issue here around what is the Bind output meant to do. Since AFAIK we're not integrating into Bind's catalog zones or the like, we're just targeting the zonefiles, we're not in a position to do _anything_ relating to registrar options such as setting up DS glue. So at one level, enabling AutoDNSSEC for Bind is a lie. But without this, folks can't target a Bind zone as a secondary provider for their domain, to get debug dumps of the zone output, because the checks for "Can" block it. So I think this commit achieves a happy compromise: we write a comment into the Bind zonefile, indicating that DNSSEC was requested. Actually: we add support for arbitrary zone comments to be written into a zonefile via a slightly ugly "can be `nil`" parameter. We then write in a generation timestamp comment, and if AutoDNSSEC was requested we then write that in too.
531 lines
15 KiB
Go
531 lines
15 KiB
Go
package prettyzone
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"testing"
|
|
|
|
"github.com/StackExchange/dnscontrol/v2/models"
|
|
"github.com/miekg/dns"
|
|
"github.com/miekg/dns/dnsutil"
|
|
)
|
|
|
|
func parseAndRegen(t *testing.T, buf *bytes.Buffer, expected string) {
|
|
// Take a zonefile, parse it, then generate a zone. We should
|
|
// get back the same string.
|
|
// This is used after any WriteZoneFile test as an extra verification step.
|
|
|
|
// Parse the output:
|
|
var parsed []dns.RR
|
|
for x := range dns.ParseZone(buf, "bosun.org", "bosun.org.zone") {
|
|
if x.Error != nil {
|
|
log.Fatalf("Error in zonefile: %v", x.Error)
|
|
} else {
|
|
parsed = append(parsed, x.RR)
|
|
}
|
|
}
|
|
// Generate it back:
|
|
buf2 := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf2, parsed, "bosun.org", 99)
|
|
|
|
// Compare:
|
|
if buf2.String() != expected {
|
|
t.Fatalf("Regenerated zonefile does not match: got=(\n%v\n)\nexpected=(\n%v\n)\n", buf2.String(), expected)
|
|
}
|
|
}
|
|
|
|
func TestMostCommonTtl(t *testing.T) {
|
|
var records []dns.RR
|
|
var g, e uint32
|
|
r1, _ := dns.NewRR("bosun.org. 100 IN A 1.1.1.1")
|
|
r2, _ := dns.NewRR("bosun.org. 200 IN A 1.1.1.1")
|
|
r3, _ := dns.NewRR("bosun.org. 300 IN A 1.1.1.1")
|
|
r4, _ := dns.NewRR("bosun.org. 400 IN NS foo.bosun.org.")
|
|
r5, _ := dns.NewRR("bosun.org. 400 IN NS bar.bosun.org.")
|
|
|
|
// All records are TTL=100
|
|
records = nil
|
|
records, e = append(records, r1, r1, r1), 100
|
|
x := models.RRstoRCs(records, "bosun.org", 99)
|
|
g = mostCommonTTL(x)
|
|
if e != g {
|
|
t.Fatalf("expected %d; got %d\n", e, g)
|
|
}
|
|
|
|
// Mixture of TTLs with an obvious winner.
|
|
records = nil
|
|
records, e = append(records, r1, r2, r2), 200
|
|
g = mostCommonTTL(models.RRstoRCs(records, "bosun.org", 99))
|
|
if e != g {
|
|
t.Fatalf("expected %d; got %d\n", e, g)
|
|
}
|
|
|
|
// 3-way tie. Largest TTL should be used.
|
|
records = nil
|
|
records, e = append(records, r1, r2, r3), 300
|
|
g = mostCommonTTL(models.RRstoRCs(records, "bosun.org", 99))
|
|
if e != g {
|
|
t.Fatalf("expected %d; got %d\n", e, g)
|
|
}
|
|
|
|
// NS records are ignored.
|
|
records = nil
|
|
records, e = append(records, r1, r4, r5), 100
|
|
g = mostCommonTTL(models.RRstoRCs(records, "bosun.org", 99))
|
|
if e != g {
|
|
t.Fatalf("expected %d; got %d\n", e, g)
|
|
}
|
|
|
|
}
|
|
|
|
// func WriteZoneFile
|
|
|
|
func TestWriteZoneFileSimple(t *testing.T) {
|
|
r1, _ := dns.NewRR("bosun.org. 300 IN A 192.30.252.153")
|
|
r2, _ := dns.NewRR("bosun.org. 300 IN A 192.30.252.154")
|
|
r3, _ := dns.NewRR("www.bosun.org. 300 IN CNAME bosun.org.")
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, []dns.RR{r1, r2, r3}, "bosun.org", 99)
|
|
expected := `$TTL 300
|
|
@ IN A 192.30.252.153
|
|
IN A 192.30.252.154
|
|
www IN CNAME bosun.org.
|
|
`
|
|
if buf.String() != expected {
|
|
t.Log(buf.String())
|
|
t.Log(expected)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
|
|
parseAndRegen(t, buf, expected)
|
|
}
|
|
|
|
func TestWriteZoneFileSimpleTtl(t *testing.T) {
|
|
r1, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.153")
|
|
r2, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.154")
|
|
r3, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.155")
|
|
r4, _ := dns.NewRR("www.bosun.org. 300 IN CNAME bosun.org.")
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, []dns.RR{r1, r2, r3, r4}, "bosun.org", 99)
|
|
expected := `$TTL 100
|
|
@ IN A 192.30.252.153
|
|
IN A 192.30.252.154
|
|
IN A 192.30.252.155
|
|
www 300 IN CNAME bosun.org.
|
|
`
|
|
if buf.String() != expected {
|
|
t.Log(buf.String())
|
|
t.Log(expected)
|
|
t.Fatalf("Zone file does not match")
|
|
}
|
|
|
|
parseAndRegen(t, buf, expected)
|
|
}
|
|
|
|
func TestWriteZoneFileMx(t *testing.T) {
|
|
// sort by priority
|
|
r1, _ := dns.NewRR("aaa.bosun.org. IN MX 1 aaa.example.com.")
|
|
r2, _ := dns.NewRR("aaa.bosun.org. IN MX 5 aaa.example.com.")
|
|
r3, _ := dns.NewRR("aaa.bosun.org. IN MX 10 aaa.example.com.")
|
|
// same priority? sort by name
|
|
r4, _ := dns.NewRR("bbb.bosun.org. IN MX 10 ccc.example.com.")
|
|
r5, _ := dns.NewRR("bbb.bosun.org. IN MX 10 bbb.example.com.")
|
|
r6, _ := dns.NewRR("bbb.bosun.org. IN MX 10 aaa.example.com.")
|
|
// a mix
|
|
r7, _ := dns.NewRR("ccc.bosun.org. IN MX 40 zzz.example.com.")
|
|
r8, _ := dns.NewRR("ccc.bosun.org. IN MX 40 aaa.example.com.")
|
|
r9, _ := dns.NewRR("ccc.bosun.org. IN MX 1 ttt.example.com.")
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, []dns.RR{r1, r2, r3, r4, r5, r6, r7, r8, r9}, "bosun.org", 99)
|
|
if buf.String() != testdataZFMX {
|
|
t.Log(buf.String())
|
|
t.Log(testdataZFMX)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
parseAndRegen(t, buf, testdataZFMX)
|
|
}
|
|
|
|
var testdataZFMX = `$TTL 3600
|
|
aaa IN MX 1 aaa.example.com.
|
|
IN MX 5 aaa.example.com.
|
|
IN MX 10 aaa.example.com.
|
|
bbb IN MX 10 aaa.example.com.
|
|
IN MX 10 bbb.example.com.
|
|
IN MX 10 ccc.example.com.
|
|
ccc IN MX 1 ttt.example.com.
|
|
IN MX 40 aaa.example.com.
|
|
IN MX 40 zzz.example.com.
|
|
`
|
|
|
|
func TestWriteZoneFileSrv(t *testing.T) {
|
|
// exhibits explicit ttls and long name
|
|
r1, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 10 9999 foo.com.`)
|
|
r2, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 20 5050 foo.com.`)
|
|
r3, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 10 5050 foo.com.`)
|
|
r4, _ := dns.NewRR(`bosun.org. 300 IN SRV 20 10 5050 foo.com.`)
|
|
r5, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 10 5050 foo.com.`)
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, []dns.RR{r1, r2, r3, r4, r5}, "bosun.org", 99)
|
|
if buf.String() != testdataZFSRV {
|
|
t.Log(buf.String())
|
|
t.Log(testdataZFSRV)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
parseAndRegen(t, buf, testdataZFSRV)
|
|
}
|
|
|
|
var testdataZFSRV = `$TTL 300
|
|
@ IN SRV 10 10 5050 foo.com.
|
|
IN SRV 10 10 5050 foo.com.
|
|
IN SRV 10 20 5050 foo.com.
|
|
IN SRV 20 10 5050 foo.com.
|
|
IN SRV 10 10 9999 foo.com.
|
|
`
|
|
|
|
func TestWriteZoneFilePtr(t *testing.T) {
|
|
// exhibits explicit ttls and long name
|
|
r1, _ := dns.NewRR(`bosun.org. 300 IN PTR chell.bosun.org`)
|
|
r2, _ := dns.NewRR(`bosun.org. 300 IN PTR barney.bosun.org.`)
|
|
r3, _ := dns.NewRR(`bosun.org. 300 IN PTR alex.bosun.org.`)
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, []dns.RR{r1, r2, r3}, "bosun.org", 99)
|
|
if buf.String() != testdataZFPTR {
|
|
t.Log(buf.String())
|
|
t.Log(testdataZFPTR)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
parseAndRegen(t, buf, testdataZFPTR)
|
|
}
|
|
|
|
var testdataZFPTR = `$TTL 300
|
|
@ IN PTR alex.bosun.org.
|
|
IN PTR barney.bosun.org.
|
|
IN PTR chell.bosun.org.
|
|
`
|
|
|
|
func TestWriteZoneFileCaa(t *testing.T) {
|
|
// exhibits explicit ttls and long name
|
|
r1, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 issuewild ";"`)
|
|
r2, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 issue "letsencrypt.org"`)
|
|
r3, _ := dns.NewRR(`bosun.org. 300 IN CAA 1 iodef "http://example.com"`)
|
|
r4, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 iodef "https://example.com"`)
|
|
r5, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 iodef "https://example.net"`)
|
|
r6, _ := dns.NewRR(`bosun.org. 300 IN CAA 1 iodef "mailto:example.com"`)
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, []dns.RR{r1, r2, r3, r4, r5, r6}, "bosun.org", 99)
|
|
if buf.String() != testdataZFCAA {
|
|
t.Log(buf.String())
|
|
t.Log(testdataZFCAA)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
parseAndRegen(t, buf, testdataZFCAA)
|
|
}
|
|
|
|
var testdataZFCAA = `$TTL 300
|
|
@ IN CAA 1 iodef "http://example.com"
|
|
IN CAA 1 iodef "mailto:example.com"
|
|
IN CAA 0 iodef "https://example.com"
|
|
IN CAA 0 iodef "https://example.net"
|
|
IN CAA 0 issue "letsencrypt.org"
|
|
IN CAA 0 issuewild ";"
|
|
`
|
|
|
|
// Test 1 of each record type
|
|
|
|
func mustNewRR(s string) dns.RR {
|
|
r, err := dns.NewRR(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return r
|
|
}
|
|
|
|
func TestWriteZoneFileEach(t *testing.T) {
|
|
// Each rtype should be listed in this test exactly once.
|
|
// If an rtype has more than one variations, add a test like TestWriteZoneFileCaa to test each.
|
|
var d []dns.RR
|
|
// #rtype_variations
|
|
d = append(d, mustNewRR(`4.5 300 IN PTR y.bosun.org.`)) // Wouldn't actually be in this domain.
|
|
d = append(d, mustNewRR(`bosun.org. 300 IN A 1.2.3.4`))
|
|
d = append(d, mustNewRR(`bosun.org. 300 IN MX 1 bosun.org.`))
|
|
d = append(d, mustNewRR(`bosun.org. 300 IN TXT "my text"`))
|
|
d = append(d, mustNewRR(`bosun.org. 300 IN AAAA 4500:fe::1`))
|
|
d = append(d, mustNewRR(`bosun.org. 300 IN SRV 10 10 9999 foo.com.`))
|
|
d = append(d, mustNewRR(`bosun.org. 300 IN CAA 0 issue "letsencrypt.org"`))
|
|
d = append(d, mustNewRR(`_443._tcp.bosun.org. 300 IN TLSA 3 1 1 abcdef0`)) // Label must be _port._proto
|
|
d = append(d, mustNewRR(`sub.bosun.org. 300 IN NS bosun.org.`)) // Must be a label with no other records.
|
|
d = append(d, mustNewRR(`x.bosun.org. 300 IN CNAME bosun.org.`)) // Must be a label with no other records.
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, d, "bosun.org", 99)
|
|
if buf.String() != testdataZFEach {
|
|
t.Log(buf.String())
|
|
t.Log(testdataZFEach)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
parseAndRegen(t, buf, testdataZFEach)
|
|
}
|
|
|
|
var testdataZFEach = `$TTL 300
|
|
@ IN A 1.2.3.4
|
|
IN AAAA 4500:fe::1
|
|
IN MX 1 bosun.org.
|
|
IN SRV 10 10 9999 foo.com.
|
|
IN TXT "my text"
|
|
IN CAA 0 issue "letsencrypt.org"
|
|
4.5 IN PTR y.bosun.org.
|
|
_443._tcp IN TLSA 3 1 1 abcdef0
|
|
sub IN NS bosun.org.
|
|
x IN CNAME bosun.org.
|
|
`
|
|
|
|
func TestWriteZoneFileSynth(t *testing.T) {
|
|
r1, _ := dns.NewRR("bosun.org. 300 IN A 192.30.252.153")
|
|
r2, _ := dns.NewRR("bosun.org. 300 IN A 192.30.252.154")
|
|
r3, _ := dns.NewRR("www.bosun.org. 300 IN CNAME bosun.org.")
|
|
rsynm := &models.RecordConfig{Type: "R53_ALIAS", TTL: 300}
|
|
rsynm.SetLabel("myalias", "bosun.org")
|
|
rsynz := &models.RecordConfig{Type: "R53_ALIAS", TTL: 300}
|
|
rsynz.SetLabel("zalias", "bosun.org")
|
|
|
|
recs := models.RRstoRCs([]dns.RR{r1, r2, r3}, "bosun.org", 99)
|
|
recs = append(recs, rsynm)
|
|
recs = append(recs, rsynm)
|
|
recs = append(recs, rsynz)
|
|
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRC(buf, recs, "bosun.org", []string{"c1", "c2", "c3\nc4"})
|
|
expected := `$TTL 300
|
|
; c1
|
|
; c2
|
|
; c3
|
|
; c4
|
|
@ IN A 192.30.252.153
|
|
IN A 192.30.252.154
|
|
;myalias IN R53_ALIAS type= zone_id=
|
|
;myalias IN R53_ALIAS type= zone_id=
|
|
www IN CNAME bosun.org.
|
|
;zalias IN R53_ALIAS type= zone_id=
|
|
`
|
|
if buf.String() != expected {
|
|
t.Log(buf.String())
|
|
t.Log(expected)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
}
|
|
|
|
// Test sorting
|
|
|
|
func TestWriteZoneFileOrder(t *testing.T) {
|
|
var records []dns.RR
|
|
for i, td := range []string{
|
|
"@",
|
|
"@",
|
|
"@",
|
|
"stackoverflow.com.",
|
|
"*",
|
|
"foo",
|
|
"bar.foo",
|
|
"hip.foo",
|
|
"mup",
|
|
"a.mup",
|
|
"bzt.mup",
|
|
"aaa.bzt.mup",
|
|
"zzz.bzt.mup",
|
|
"nnn.mup",
|
|
"zt.mup",
|
|
"zap",
|
|
} {
|
|
name := dnsutil.AddOrigin(td, "stackoverflow.com.")
|
|
r, _ := dns.NewRR(fmt.Sprintf("%s 300 IN A 1.2.3.%d", name, i))
|
|
records = append(records, r)
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, records, "stackoverflow.com", 99)
|
|
// Compare
|
|
if buf.String() != testdataOrder {
|
|
t.Log("Found:")
|
|
t.Log(buf.String())
|
|
t.Log("Expected:")
|
|
t.Log(testdataOrder)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
parseAndRegen(t, buf, testdataOrder)
|
|
|
|
// Now shuffle the list many times and make sure it still works:
|
|
for iteration := 5; iteration > 0; iteration-- {
|
|
// Randomize the list:
|
|
perm := rand.Perm(len(records))
|
|
for i, v := range perm {
|
|
records[i], records[v] = records[v], records[i]
|
|
}
|
|
// Generate
|
|
buf := &bytes.Buffer{}
|
|
WriteZoneFileRR(buf, records, "stackoverflow.com", 99)
|
|
// Compare
|
|
if buf.String() != testdataOrder {
|
|
t.Log(buf.String())
|
|
t.Log(testdataOrder)
|
|
t.Fatalf("Zone file does not match.")
|
|
}
|
|
parseAndRegen(t, buf, testdataOrder)
|
|
}
|
|
}
|
|
|
|
var testdataOrder = `$TTL 300
|
|
@ IN A 1.2.3.0
|
|
IN A 1.2.3.1
|
|
IN A 1.2.3.2
|
|
IN A 1.2.3.3
|
|
* IN A 1.2.3.4
|
|
foo IN A 1.2.3.5
|
|
bar.foo IN A 1.2.3.6
|
|
hip.foo IN A 1.2.3.7
|
|
mup IN A 1.2.3.8
|
|
a.mup IN A 1.2.3.9
|
|
bzt.mup IN A 1.2.3.10
|
|
aaa.bzt.mup IN A 1.2.3.11
|
|
zzz.bzt.mup IN A 1.2.3.12
|
|
nnn.mup IN A 1.2.3.13
|
|
zt.mup IN A 1.2.3.14
|
|
zap IN A 1.2.3.15
|
|
`
|
|
|
|
// func formatLine
|
|
|
|
func TestFormatLine(t *testing.T) {
|
|
tests := []struct {
|
|
lengths []int
|
|
fields []string
|
|
expected string
|
|
}{
|
|
{[]int{2, 2, 0}, []string{"a", "b", "c"}, "a b c"},
|
|
{[]int{2, 2, 0}, []string{"aaaaa", "b", "c"}, "aaaaa b c"},
|
|
}
|
|
for _, ts := range tests {
|
|
actual := formatLine(ts.lengths, ts.fields)
|
|
if actual != ts.expected {
|
|
t.Errorf("\"%s\" != \"%s\"", actual, ts.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
// func zoneLabelLess
|
|
|
|
func TestZoneLabelLess(t *testing.T) {
|
|
/*
|
|
The zone should sort in prefix traversal order:
|
|
|
|
@
|
|
*
|
|
foo
|
|
bar.foo
|
|
hip.foo
|
|
mup
|
|
a.mup
|
|
bzt.mup
|
|
*.bzt.mup
|
|
1.bzt.mup
|
|
2.bzt.mup
|
|
10.bzt.mup
|
|
aaa.bzt.mup
|
|
zzz.bzt.mup
|
|
nnn.mup
|
|
zt.mup
|
|
zap
|
|
*/
|
|
|
|
var tests = []struct {
|
|
e1, e2 string
|
|
expected bool
|
|
}{
|
|
{"@", "@", false},
|
|
{"@", "*", true},
|
|
{"@", "b", true},
|
|
{"*", "@", false},
|
|
{"*", "*", false},
|
|
{"*", "b", true},
|
|
{"foo", "foo", false},
|
|
{"foo", "bar", false},
|
|
{"bar", "foo", true},
|
|
{"a.mup", "mup", false},
|
|
{"mup", "a.mup", true},
|
|
{"a.mup", "a.mup", false},
|
|
{"a.mup", "bzt.mup", true},
|
|
{"a.mup", "aa.mup", true},
|
|
{"zt.mup", "aaa.bzt.mup", false},
|
|
{"aaa.bzt.mup", "mup", false},
|
|
{"*.bzt.mup", "aaa.bzt.mup", true},
|
|
{"1.bzt.mup", "aaa.bzt.mup", true},
|
|
{"1.bzt.mup", "2.bzt.mup", true},
|
|
{"10.bzt.mup", "2.bzt.mup", false},
|
|
{"nnn.mup", "aaa.bzt.mup", false},
|
|
{`www\.miek.nl`, `www.miek.nl`, false},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
actual := zoneLabelLess(test.e1, test.e2)
|
|
if test.expected != actual {
|
|
t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
|
|
}
|
|
actual = zoneLabelLess(test.e2, test.e1)
|
|
// The reverse should work too:
|
|
var expected bool
|
|
if test.e1 == test.e2 {
|
|
expected = false
|
|
} else {
|
|
expected = !test.expected
|
|
}
|
|
if expected != actual {
|
|
t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestZoneRrtypeLess(t *testing.T) {
|
|
/*
|
|
In zonefiles we want to list SOAs, then NSs, then all others.
|
|
*/
|
|
|
|
var tests = []struct {
|
|
e1, e2 string
|
|
expected bool
|
|
}{
|
|
{"SOA", "SOA", false},
|
|
{"SOA", "A", true},
|
|
{"SOA", "TXT", true},
|
|
{"SOA", "NS", true},
|
|
{"NS", "SOA", false},
|
|
{"NS", "A", true},
|
|
{"NS", "TXT", true},
|
|
{"NS", "NS", false},
|
|
{"A", "SOA", false},
|
|
{"A", "A", false},
|
|
{"A", "TXT", true},
|
|
{"A", "NS", false},
|
|
{"MX", "SOA", false},
|
|
{"MX", "A", false},
|
|
{"MX", "TXT", true},
|
|
{"MX", "NS", false},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
actual := zoneRrtypeLess(test.e1, test.e2)
|
|
if test.expected != actual {
|
|
t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
|
|
}
|
|
actual = zoneRrtypeLess(test.e2, test.e1)
|
|
// The reverse should work too:
|
|
var expected bool
|
|
if test.e1 == test.e2 {
|
|
expected = false
|
|
} else {
|
|
expected = !test.expected
|
|
}
|
|
if expected != actual {
|
|
t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
|
|
}
|
|
}
|
|
}
|