1// Copyright 2019 The Hugo Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package helpers 15 16import ( 17 "fmt" 18 "reflect" 19 "strings" 20 "testing" 21 22 "github.com/gohugoio/hugo/config" 23 24 "github.com/gohugoio/hugo/common/loggers" 25 26 qt "github.com/frankban/quicktest" 27 "github.com/spf13/afero" 28) 29 30func TestResolveMarkup(t *testing.T) { 31 c := qt.New(t) 32 cfg := config.New() 33 spec, err := NewContentSpec(cfg, loggers.NewErrorLogger(), afero.NewMemMapFs(), nil) 34 c.Assert(err, qt.IsNil) 35 36 for i, this := range []struct { 37 in string 38 expect string 39 }{ 40 {"md", "markdown"}, 41 {"markdown", "markdown"}, 42 {"mdown", "markdown"}, 43 {"asciidocext", "asciidocext"}, 44 {"adoc", "asciidocext"}, 45 {"ad", "asciidocext"}, 46 {"rst", "rst"}, 47 {"pandoc", "pandoc"}, 48 {"pdc", "pandoc"}, 49 {"mmark", "mmark"}, 50 {"html", "html"}, 51 {"htm", "html"}, 52 {"org", "org"}, 53 {"excel", ""}, 54 } { 55 result := spec.ResolveMarkup(this.in) 56 if result != this.expect { 57 t.Errorf("[%d] got %s but expected %s", i, result, this.expect) 58 } 59 } 60} 61 62func TestFirstUpper(t *testing.T) { 63 for i, this := range []struct { 64 in string 65 expect string 66 }{ 67 {"foo", "Foo"}, 68 {"foo bar", "Foo bar"}, 69 {"Foo Bar", "Foo Bar"}, 70 {"", ""}, 71 {"å", "Å"}, 72 } { 73 result := FirstUpper(this.in) 74 if result != this.expect { 75 t.Errorf("[%d] got %s but expected %s", i, result, this.expect) 76 } 77 } 78} 79 80func TestHasStringsPrefix(t *testing.T) { 81 for i, this := range []struct { 82 s []string 83 prefix []string 84 expect bool 85 }{ 86 {[]string{"a"}, []string{"a"}, true}, 87 {[]string{}, []string{}, true}, 88 {[]string{"a", "b", "c"}, []string{"a", "b"}, true}, 89 {[]string{"d", "a", "b", "c"}, []string{"a", "b"}, false}, 90 {[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, true}, 91 {[]string{"abra", "ca"}, []string{"abra", "ca", "dabra"}, false}, 92 } { 93 result := HasStringsPrefix(this.s, this.prefix) 94 if result != this.expect { 95 t.Fatalf("[%d] got %t but expected %t", i, result, this.expect) 96 } 97 } 98} 99 100func TestHasStringsSuffix(t *testing.T) { 101 for i, this := range []struct { 102 s []string 103 suffix []string 104 expect bool 105 }{ 106 {[]string{"a"}, []string{"a"}, true}, 107 {[]string{}, []string{}, true}, 108 {[]string{"a", "b", "c"}, []string{"b", "c"}, true}, 109 {[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, false}, 110 {[]string{"abra", "ca", "dabra"}, []string{"ca", "dabra"}, true}, 111 } { 112 result := HasStringsSuffix(this.s, this.suffix) 113 if result != this.expect { 114 t.Fatalf("[%d] got %t but expected %t", i, result, this.expect) 115 } 116 } 117} 118 119var containsTestText = (`На берегу пустынных волн 120Стоял он, дум великих полн, 121И вдаль глядел. Пред ним широко 122Река неслася; бедный чёлн 123По ней стремился одиноко. 124По мшистым, топким берегам 125Чернели избы здесь и там, 126Приют убогого чухонца; 127И лес, неведомый лучам 128В тумане спрятанного солнца, 129Кругом шумел. 130 131Τη γλώσσα μου έδωσαν ελληνική 132το σπίτι φτωχικό στις αμμουδιές του Ομήρου. 133Μονάχη έγνοια η γλώσσα μου στις αμμουδιές του Ομήρου. 134 135από το Άξιον Εστί 136του Οδυσσέα Ελύτη 137 138Sîne klâwen durh die wolken sint geslagen, 139er stîget ûf mit grôzer kraft, 140ich sih in grâwen tägelîch als er wil tagen, 141den tac, der im geselleschaft 142erwenden wil, dem werden man, 143den ich mit sorgen în verliez. 144ich bringe in hinnen, ob ich kan. 145sîn vil manegiu tugent michz leisten hiez. 146`) 147 148var containsBenchTestData = []struct { 149 v1 string 150 v2 []byte 151 expect bool 152}{ 153 {"abc", []byte("a"), true}, 154 {"abc", []byte("b"), true}, 155 {"abcdefg", []byte("efg"), true}, 156 {"abc", []byte("d"), false}, 157 {containsTestText, []byte("стремился"), true}, 158 {containsTestText, []byte(containsTestText[10:80]), true}, 159 {containsTestText, []byte(containsTestText[100:111]), true}, 160 {containsTestText, []byte(containsTestText[len(containsTestText)-100 : len(containsTestText)-10]), true}, 161 {containsTestText, []byte(containsTestText[len(containsTestText)-20:]), true}, 162 {containsTestText, []byte("notfound"), false}, 163} 164 165// some corner cases 166var containsAdditionalTestData = []struct { 167 v1 string 168 v2 []byte 169 expect bool 170}{ 171 {"", nil, false}, 172 {"", []byte("a"), false}, 173 {"a", []byte(""), false}, 174 {"", []byte(""), false}, 175} 176 177func TestSliceToLower(t *testing.T) { 178 t.Parallel() 179 tests := []struct { 180 value []string 181 expected []string 182 }{ 183 {[]string{"a", "b", "c"}, []string{"a", "b", "c"}}, 184 {[]string{"a", "B", "c"}, []string{"a", "b", "c"}}, 185 {[]string{"A", "B", "C"}, []string{"a", "b", "c"}}, 186 } 187 188 for _, test := range tests { 189 res := SliceToLower(test.value) 190 for i, val := range res { 191 if val != test.expected[i] { 192 t.Errorf("Case mismatch. Expected %s, got %s", test.expected[i], res[i]) 193 } 194 } 195 } 196} 197 198func TestReaderContains(t *testing.T) { 199 c := qt.New(t) 200 for i, this := range append(containsBenchTestData, containsAdditionalTestData...) { 201 result := ReaderContains(strings.NewReader(this.v1), this.v2) 202 if result != this.expect { 203 t.Errorf("[%d] got %t but expected %t", i, result, this.expect) 204 } 205 } 206 207 c.Assert(ReaderContains(nil, []byte("a")), qt.Equals, false) 208 c.Assert(ReaderContains(nil, nil), qt.Equals, false) 209} 210 211func TestGetTitleFunc(t *testing.T) { 212 title := "somewhere over the rainbow" 213 c := qt.New(t) 214 215 c.Assert(GetTitleFunc("go")(title), qt.Equals, "Somewhere Over The Rainbow") 216 c.Assert(GetTitleFunc("chicago")(title), qt.Equals, "Somewhere over the Rainbow") 217 c.Assert(GetTitleFunc("Chicago")(title), qt.Equals, "Somewhere over the Rainbow") 218 c.Assert(GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow") 219 c.Assert(GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow") 220 c.Assert(GetTitleFunc("")(title), qt.Equals, "Somewhere Over the Rainbow") 221 c.Assert(GetTitleFunc("unknown")(title), qt.Equals, "Somewhere Over the Rainbow") 222} 223 224func BenchmarkReaderContains(b *testing.B) { 225 b.ResetTimer() 226 for i := 0; i < b.N; i++ { 227 for i, this := range containsBenchTestData { 228 result := ReaderContains(strings.NewReader(this.v1), this.v2) 229 if result != this.expect { 230 b.Errorf("[%d] got %t but expected %t", i, result, this.expect) 231 } 232 } 233 } 234} 235 236func TestUniqueStrings(t *testing.T) { 237 in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"} 238 output := UniqueStrings(in) 239 expected := []string{"a", "b", "c", "", "d"} 240 if !reflect.DeepEqual(output, expected) { 241 t.Errorf("Expected %#v, got %#v\n", expected, output) 242 } 243} 244 245func TestUniqueStringsReuse(t *testing.T) { 246 in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"} 247 output := UniqueStringsReuse(in) 248 expected := []string{"a", "b", "c", "", "d"} 249 if !reflect.DeepEqual(output, expected) { 250 t.Errorf("Expected %#v, got %#v\n", expected, output) 251 } 252} 253 254func TestUniqueStringsSorted(t *testing.T) { 255 c := qt.New(t) 256 in := []string{"a", "a", "b", "c", "b", "", "a", "", "d"} 257 output := UniqueStringsSorted(in) 258 expected := []string{"", "a", "b", "c", "d"} 259 c.Assert(output, qt.DeepEquals, expected) 260 c.Assert(UniqueStringsSorted(nil), qt.IsNil) 261} 262 263func TestFindAvailablePort(t *testing.T) { 264 c := qt.New(t) 265 addr, err := FindAvailablePort() 266 c.Assert(err, qt.IsNil) 267 c.Assert(addr, qt.Not(qt.IsNil)) 268 c.Assert(addr.Port > 0, qt.Equals, true) 269} 270 271func TestFastMD5FromFile(t *testing.T) { 272 fs := afero.NewMemMapFs() 273 274 if err := afero.WriteFile(fs, "small.txt", []byte("abc"), 0777); err != nil { 275 t.Fatal(err) 276 } 277 278 if err := afero.WriteFile(fs, "small2.txt", []byte("abd"), 0777); err != nil { 279 t.Fatal(err) 280 } 281 282 if err := afero.WriteFile(fs, "bigger.txt", []byte(strings.Repeat("a bc d e", 100)), 0777); err != nil { 283 t.Fatal(err) 284 } 285 286 if err := afero.WriteFile(fs, "bigger2.txt", []byte(strings.Repeat("c d e f g", 100)), 0777); err != nil { 287 t.Fatal(err) 288 } 289 290 c := qt.New(t) 291 292 sf1, err := fs.Open("small.txt") 293 c.Assert(err, qt.IsNil) 294 sf2, err := fs.Open("small2.txt") 295 c.Assert(err, qt.IsNil) 296 297 bf1, err := fs.Open("bigger.txt") 298 c.Assert(err, qt.IsNil) 299 bf2, err := fs.Open("bigger2.txt") 300 c.Assert(err, qt.IsNil) 301 302 defer sf1.Close() 303 defer sf2.Close() 304 defer bf1.Close() 305 defer bf2.Close() 306 307 m1, err := MD5FromFileFast(sf1) 308 c.Assert(err, qt.IsNil) 309 c.Assert(m1, qt.Equals, "e9c8989b64b71a88b4efb66ad05eea96") 310 311 m2, err := MD5FromFileFast(sf2) 312 c.Assert(err, qt.IsNil) 313 c.Assert(m2, qt.Not(qt.Equals), m1) 314 315 m3, err := MD5FromFileFast(bf1) 316 c.Assert(err, qt.IsNil) 317 c.Assert(m3, qt.Not(qt.Equals), m2) 318 319 m4, err := MD5FromFileFast(bf2) 320 c.Assert(err, qt.IsNil) 321 c.Assert(m4, qt.Not(qt.Equals), m3) 322 323 m5, err := MD5FromReader(bf2) 324 c.Assert(err, qt.IsNil) 325 c.Assert(m5, qt.Not(qt.Equals), m4) 326} 327 328func BenchmarkMD5FromFileFast(b *testing.B) { 329 fs := afero.NewMemMapFs() 330 331 for _, full := range []bool{false, true} { 332 b.Run(fmt.Sprintf("full=%t", full), func(b *testing.B) { 333 for i := 0; i < b.N; i++ { 334 b.StopTimer() 335 if err := afero.WriteFile(fs, "file.txt", []byte(strings.Repeat("1234567890", 2000)), 0777); err != nil { 336 b.Fatal(err) 337 } 338 f, err := fs.Open("file.txt") 339 if err != nil { 340 b.Fatal(err) 341 } 342 b.StartTimer() 343 if full { 344 if _, err := MD5FromReader(f); err != nil { 345 b.Fatal(err) 346 } 347 } else { 348 if _, err := MD5FromFileFast(f); err != nil { 349 b.Fatal(err) 350 } 351 } 352 f.Close() 353 } 354 }) 355 } 356} 357 358func BenchmarkUniqueStrings(b *testing.B) { 359 input := []string{"a", "b", "d", "e", "d", "h", "a", "i"} 360 361 b.Run("Safe", func(b *testing.B) { 362 for i := 0; i < b.N; i++ { 363 result := UniqueStrings(input) 364 if len(result) != 6 { 365 b.Fatal(fmt.Sprintf("invalid count: %d", len(result))) 366 } 367 } 368 }) 369 370 b.Run("Reuse slice", func(b *testing.B) { 371 b.StopTimer() 372 inputs := make([][]string, b.N) 373 for i := 0; i < b.N; i++ { 374 inputc := make([]string, len(input)) 375 copy(inputc, input) 376 inputs[i] = inputc 377 } 378 b.StartTimer() 379 for i := 0; i < b.N; i++ { 380 inputc := inputs[i] 381 382 result := UniqueStringsReuse(inputc) 383 if len(result) != 6 { 384 b.Fatal(fmt.Sprintf("invalid count: %d", len(result))) 385 } 386 } 387 }) 388 389 b.Run("Reuse slice sorted", func(b *testing.B) { 390 b.StopTimer() 391 inputs := make([][]string, b.N) 392 for i := 0; i < b.N; i++ { 393 inputc := make([]string, len(input)) 394 copy(inputc, input) 395 inputs[i] = inputc 396 } 397 b.StartTimer() 398 for i := 0; i < b.N; i++ { 399 inputc := inputs[i] 400 401 result := UniqueStringsSorted(inputc) 402 if len(result) != 6 { 403 b.Fatal(fmt.Sprintf("invalid count: %d", len(result))) 404 } 405 } 406 }) 407} 408 409func TestHashString(t *testing.T) { 410 c := qt.New(t) 411 412 c.Assert(HashString("a", "b"), qt.Equals, "2712570657419664240") 413 c.Assert(HashString("ab"), qt.Equals, "590647783936702392") 414} 415