// Copyright (c) 2015, Daniel Martí // See LICENSE for licensing information package xurls import ( "fmt" "regexp" "testing" ) type testCase struct { in string want interface{} } func wantStr(in string, want interface{}) string { switch x := want.(type) { case string: return x case bool: if x { return in } } return "" } func doTest(t *testing.T, name string, re *regexp.Regexp, cases []testCase) { for i, c := range cases { t.Run(fmt.Sprintf("%s/%03d", name, i), func(t *testing.T) { want := wantStr(c.in, c.want) for _, surround := range []string{"", "\n"} { in := surround + c.in + surround got := re.FindString(in) if got != want { t.Errorf(`FindString(%q) got %q, want %q`, in, got, want) } } }) } } var constantTestCases = []testCase{ {``, nil}, {` `, nil}, {`:`, nil}, {`::`, nil}, {`:::`, nil}, {`::::`, nil}, {`.`, nil}, {`..`, nil}, {`...`, nil}, {`1.1`, nil}, {`.1.`, nil}, {`1.1.1`, nil}, {`1:1`, nil}, {`:1:`, nil}, {`1:1:1`, nil}, {`://`, nil}, {`foo`, nil}, {`foo:`, nil}, {`mailto:`, nil}, {`foo://`, nil}, {`http://`, nil}, {`http:// foo`, nil}, {`http:// foo`, nil}, {`:foo`, nil}, {`://foo`, nil}, {`foorandom:bar`, nil}, {`foo.randombar`, nil}, {`zzz.`, nil}, {`.zzz`, nil}, {`zzz.zzz`, nil}, {`/some/path`, nil}, {`rel/path`, nil}, {`localhost`, nil}, {`com`, nil}, {`.com`, nil}, {`com.`, nil}, {`http`, nil}, {`http://foo`, true}, {`http://FOO`, true}, {`http://FAÀ`, true}, {`https://localhost`, true}, {`mailto:foo`, true}, {`MAILTO:foo`, true}, {`sms:123`, true}, {`xmpp:foo@bar`, true}, {`bitcoin:Addr23?amount=1&message=foo`, true}, {`http://foo.com`, true}, {`http://foo.co.uk`, true}, {`http://foo.random`, true}, {` http://foo.com/bar `, `http://foo.com/bar`}, {` http://foo.com/bar more`, `http://foo.com/bar`}, {``, `http://foo.com/bar`}, {`more`, `http://foo.com/bar`}, {`.http://foo.com/bar.`, `http://foo.com/bar`}, {`.http://foo.com/bar.more`, `http://foo.com/bar.more`}, {`,http://foo.com/bar,`, `http://foo.com/bar`}, {`,http://foo.com/bar,more`, `http://foo.com/bar,more`}, {`*http://foo.com/bar*`, `http://foo.com/bar`}, {`*http://foo.com/bar*more`, `http://foo.com/bar*more`}, {`_http://foo.com/bar_`, `http://foo.com/bar_`}, {`_http://foo.com/bar_more`, `http://foo.com/bar_more`}, {`(http://foo.com/bar)`, `http://foo.com/bar`}, {`(http://foo.com/bar)more`, `http://foo.com/bar`}, {`[http://foo.com/bar]`, `http://foo.com/bar`}, {`[http://foo.com/bar]more`, `http://foo.com/bar`}, {`'http://foo.com/bar'`, `http://foo.com/bar`}, {`'http://foo.com/bar'more`, `http://foo.com/bar'more`}, {`"http://foo.com/bar"`, `http://foo.com/bar`}, {`"http://foo.com/bar"more`, `http://foo.com/bar"more`}, {`http://a.b/a0/-+_&~*%=#@.,:;'?![]()a`, true}, {`http://a.b/a0/$€¥`, true}, {`http://✪foo.bar/pa✪th©more`, true}, {`http://foo.bar/path/`, true}, {`http://foo.bar/path-`, true}, {`http://foo.bar/path+`, true}, {`http://foo.bar/path&`, true}, {`http://foo.bar/path~`, true}, {`http://foo.bar/path%`, true}, {`http://foo.bar/path=`, true}, {`http://foo.bar/path#`, true}, {`http://foo.bar/path.`, `http://foo.bar/path`}, {`http://foo.bar/path,`, `http://foo.bar/path`}, {`http://foo.bar/path:`, `http://foo.bar/path`}, {`http://foo.bar/path;`, `http://foo.bar/path`}, {`http://foo.bar/path'`, `http://foo.bar/path`}, {`http://foo.bar/path?`, `http://foo.bar/path`}, {`http://foo.bar/path!`, `http://foo.bar/path`}, {`http://foo.bar/path@`, `http://foo.bar/path`}, {`http://foo.bar/path|`, `http://foo.bar/path`}, {`http://foo.bar/path|more`, `http://foo.bar/path`}, {`http://foo.bar/path<`, `http://foo.bar/path`}, {`http://foo.bar/path`, `foo.com/bar`}, {`more`, `foo.com/bar`}, {`,foo.com/bar.`, `foo.com/bar`}, {`,foo.com/bar.more`, `foo.com/bar.more`}, {`,foo.com/bar,`, `foo.com/bar`}, {`,foo.com/bar,more`, `foo.com/bar,more`}, {`(foo.com/bar)`, `foo.com/bar`}, {`"foo.com/bar'`, `foo.com/bar`}, {`"foo.com/bar'more`, `foo.com/bar'more`}, {`"foo.com/bar"`, `foo.com/bar`}, {`what is foo.com?`, `foo.com`}, {`the foo.com!`, `foo.com`}, {`foo@bar`, nil}, {`foo@bar.a`, nil}, {`foo@bar.com`, "bar.com"}, {`foo@sub.bar.com`, "sub.bar.com"}, {`foo@中国.中国`, "中国.中国"}, }) doTest(t, "Strict2", Strict(), []testCase{ {`http:// foo.com`, nil}, {`foo.a`, nil}, {`foo.com`, nil}, {`foo.com/`, nil}, {`1.1.1.1`, nil}, {`3ffe:2a00:100:7031::1`, nil}, {`test.foo.com:8080/path`, nil}, {`foo@bar.com`, nil}, }) } func TestStrictMatchingSchemeError(t *testing.T) { for _, c := range []struct { exp string wantErr bool }{ {`http://`, false}, {`https?://`, false}, {`http://|mailto:`, false}, {`http://(`, true}, } { _, err := StrictMatchingScheme(c.exp) if c.wantErr && err == nil { t.Errorf(`StrictMatchingScheme("%s") did not error as expected`, c.exp) } else if !c.wantErr && err != nil { t.Errorf(`StrictMatchingScheme("%s") unexpectedly errored`, c.exp) } } } func TestStrictMatchingScheme(t *testing.T) { strictMatching, _ := StrictMatchingScheme("http://|ftps?://|mailto:") doTest(t, "StrictMatchingScheme", strictMatching, []testCase{ {`foo.com`, nil}, {`foo@bar.com`, nil}, {`http://foo`, true}, {`Http://foo`, true}, {`https://foo`, nil}, {`ftp://foo`, true}, {`ftps://foo`, true}, {`mailto:foo`, true}, {`MAILTO:foo`, true}, {`sms:123`, nil}, }) } func TestStrictMatchingSchemeAny(t *testing.T) { strictMatching, _ := StrictMatchingScheme(AnyScheme) doTest(t, "StrictMatchingScheme", strictMatching, []testCase{ {`http://foo`, true}, {`git+https://foo`, true}, {`randomtexthttp://foo.bar/etc`, true}, {`mailto:foo`, true}, }) } func bench(b *testing.B, re *regexp.Regexp, str string) { for i := 0; i < b.N; i++ { re.FindAllString(str, -1) } } func BenchmarkStrictEmpty(b *testing.B) { bench(b, Strict(), "foo") } func BenchmarkStrictSingle(b *testing.B) { bench(b, Strict(), "http://foo.foo foo.com") } func BenchmarkStrictMany(b *testing.B) { bench(b, Strict(), ` foo bar http://foo.foo foo.com bitcoin:address ftp:// xmpp:foo@bar.com`) } func BenchmarkRelaxedEmpty(b *testing.B) { bench(b, Relaxed(), "foo") } func BenchmarkRelaxedSingle(b *testing.B) { bench(b, Relaxed(), "http://foo.foo foo.com") } func BenchmarkRelaxedMany(b *testing.B) { bench(b, Relaxed(), ` foo bar http://foo.foo foo.com bitcoin:address ftp:// xmpp:foo@bar.com`) }