github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/helpers/general_test.go (about)

     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  
    14  package helpers
    15  
    16  import (
    17  	"fmt"
    18  	"reflect"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/gohugoio/hugo/config"
    24  
    25  	"github.com/gohugoio/hugo/common/loggers"
    26  
    27  	qt "github.com/frankban/quicktest"
    28  	"github.com/spf13/afero"
    29  )
    30  
    31  func TestResolveMarkup(t *testing.T) {
    32  	c := qt.New(t)
    33  	cfg := config.New()
    34  	spec, err := NewContentSpec(cfg, loggers.NewErrorLogger(), afero.NewMemMapFs(), nil)
    35  	c.Assert(err, qt.IsNil)
    36  
    37  	for i, this := range []struct {
    38  		in     string
    39  		expect string
    40  	}{
    41  		{"md", "markdown"},
    42  		{"markdown", "markdown"},
    43  		{"mdown", "markdown"},
    44  		{"asciidocext", "asciidocext"},
    45  		{"adoc", "asciidocext"},
    46  		{"ad", "asciidocext"},
    47  		{"rst", "rst"},
    48  		{"pandoc", "pandoc"},
    49  		{"pdc", "pandoc"},
    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  
    62  func TestDistinctLoggerDoesNotLockOnWarningPanic(t *testing.T) {
    63  	// Testing to make sure logger mutex doesn't lock if warnings cause panics.
    64  	// func Warnf() of DistinctLogger is defined in general.go
    65  	l := NewDistinctLogger(loggers.NewWarningLogger())
    66  
    67  	// Set PanicOnWarning to true to reproduce issue 9380
    68  	// Ensure global variable loggers.PanicOnWarning is reset to old value after test
    69  	if loggers.PanicOnWarning == false {
    70  		loggers.PanicOnWarning = true
    71  		defer func() {
    72  			loggers.PanicOnWarning = false
    73  		}()
    74  	}
    75  
    76  	// Establish timeout in case a lock occurs:
    77  	timeIsUp := make(chan bool)
    78  	timeOutSeconds := 1
    79  	go func() {
    80  		time.Sleep(time.Second * time.Duration(timeOutSeconds))
    81  		timeIsUp <- true
    82  	}()
    83  
    84  	// Attempt to run multiple logging threads in parallel
    85  	counterC := make(chan int)
    86  	goroutines := 5
    87  
    88  	for i := 0; i < goroutines; i++ {
    89  		go func() {
    90  			defer func() {
    91  				// Intentional panic successfully recovered - notify counter channel
    92  				recover()
    93  				counterC <- 1
    94  			}()
    95  
    96  			l.Warnf("Placeholder template message: %v", "In this test, logging a warning causes a panic.")
    97  		}()
    98  	}
    99  
   100  	// All goroutines should complete before timeout
   101  	var counter int
   102  	for {
   103  		select {
   104  		case <-counterC:
   105  			counter++
   106  			if counter == goroutines {
   107  				return
   108  			}
   109  		case <-timeIsUp:
   110  			t.Errorf("Unable to log warnings with --panicOnWarning within alloted time of: %v seconds. Investigate possible mutex locking on panic in distinct warning logger.", timeOutSeconds)
   111  			return
   112  		}
   113  	}
   114  }
   115  
   116  func TestFirstUpper(t *testing.T) {
   117  	for i, this := range []struct {
   118  		in     string
   119  		expect string
   120  	}{
   121  		{"foo", "Foo"},
   122  		{"foo bar", "Foo bar"},
   123  		{"Foo Bar", "Foo Bar"},
   124  		{"", ""},
   125  		{"å", "Å"},
   126  	} {
   127  		result := FirstUpper(this.in)
   128  		if result != this.expect {
   129  			t.Errorf("[%d] got %s but expected %s", i, result, this.expect)
   130  		}
   131  	}
   132  }
   133  
   134  func TestHasStringsPrefix(t *testing.T) {
   135  	for i, this := range []struct {
   136  		s      []string
   137  		prefix []string
   138  		expect bool
   139  	}{
   140  		{[]string{"a"}, []string{"a"}, true},
   141  		{[]string{}, []string{}, true},
   142  		{[]string{"a", "b", "c"}, []string{"a", "b"}, true},
   143  		{[]string{"d", "a", "b", "c"}, []string{"a", "b"}, false},
   144  		{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, true},
   145  		{[]string{"abra", "ca"}, []string{"abra", "ca", "dabra"}, false},
   146  	} {
   147  		result := HasStringsPrefix(this.s, this.prefix)
   148  		if result != this.expect {
   149  			t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
   150  		}
   151  	}
   152  }
   153  
   154  func TestHasStringsSuffix(t *testing.T) {
   155  	for i, this := range []struct {
   156  		s      []string
   157  		suffix []string
   158  		expect bool
   159  	}{
   160  		{[]string{"a"}, []string{"a"}, true},
   161  		{[]string{}, []string{}, true},
   162  		{[]string{"a", "b", "c"}, []string{"b", "c"}, true},
   163  		{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, false},
   164  		{[]string{"abra", "ca", "dabra"}, []string{"ca", "dabra"}, true},
   165  	} {
   166  		result := HasStringsSuffix(this.s, this.suffix)
   167  		if result != this.expect {
   168  			t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
   169  		}
   170  	}
   171  }
   172  
   173  var containsTestText = (`На берегу пустынных волн
   174  Стоял он, дум великих полн,
   175  И вдаль глядел. Пред ним широко
   176  Река неслася; бедный чёлн
   177  По ней стремился одиноко.
   178  По мшистым, топким берегам
   179  Чернели избы здесь и там,
   180  Приют убогого чухонца;
   181  И лес, неведомый лучам
   182  В тумане спрятанного солнца,
   183  Кругом шумел.
   184  
   185  Τη γλώσσα μου έδωσαν ελληνική
   186  το σπίτι φτωχικό στις αμμουδιές του Ομήρου.
   187  Μονάχη έγνοια η γλώσσα μου στις αμμουδιές του Ομήρου.
   188  
   189  από το Άξιον Εστί
   190  του Οδυσσέα Ελύτη
   191  
   192  Sîne klâwen durh die wolken sint geslagen,
   193  er stîget ûf mit grôzer kraft,
   194  ich sih in grâwen tägelîch als er wil tagen,
   195  den tac, der im geselleschaft
   196  erwenden wil, dem werden man,
   197  den ich mit sorgen în verliez.
   198  ich bringe in hinnen, ob ich kan.
   199  sîn vil manegiu tugent michz leisten hiez.
   200  `)
   201  
   202  var containsBenchTestData = []struct {
   203  	v1     string
   204  	v2     []byte
   205  	expect bool
   206  }{
   207  	{"abc", []byte("a"), true},
   208  	{"abc", []byte("b"), true},
   209  	{"abcdefg", []byte("efg"), true},
   210  	{"abc", []byte("d"), false},
   211  	{containsTestText, []byte("стремился"), true},
   212  	{containsTestText, []byte(containsTestText[10:80]), true},
   213  	{containsTestText, []byte(containsTestText[100:111]), true},
   214  	{containsTestText, []byte(containsTestText[len(containsTestText)-100 : len(containsTestText)-10]), true},
   215  	{containsTestText, []byte(containsTestText[len(containsTestText)-20:]), true},
   216  	{containsTestText, []byte("notfound"), false},
   217  }
   218  
   219  // some corner cases
   220  var containsAdditionalTestData = []struct {
   221  	v1     string
   222  	v2     []byte
   223  	expect bool
   224  }{
   225  	{"", nil, false},
   226  	{"", []byte("a"), false},
   227  	{"a", []byte(""), false},
   228  	{"", []byte(""), false},
   229  }
   230  
   231  func TestSliceToLower(t *testing.T) {
   232  	t.Parallel()
   233  	tests := []struct {
   234  		value    []string
   235  		expected []string
   236  	}{
   237  		{[]string{"a", "b", "c"}, []string{"a", "b", "c"}},
   238  		{[]string{"a", "B", "c"}, []string{"a", "b", "c"}},
   239  		{[]string{"A", "B", "C"}, []string{"a", "b", "c"}},
   240  	}
   241  
   242  	for _, test := range tests {
   243  		res := SliceToLower(test.value)
   244  		for i, val := range res {
   245  			if val != test.expected[i] {
   246  				t.Errorf("Case mismatch. Expected %s, got %s", test.expected[i], res[i])
   247  			}
   248  		}
   249  	}
   250  }
   251  
   252  func TestReaderContains(t *testing.T) {
   253  	c := qt.New(t)
   254  	for i, this := range append(containsBenchTestData, containsAdditionalTestData...) {
   255  		result := ReaderContains(strings.NewReader(this.v1), this.v2)
   256  		if result != this.expect {
   257  			t.Errorf("[%d] got %t but expected %t", i, result, this.expect)
   258  		}
   259  	}
   260  
   261  	c.Assert(ReaderContains(nil, []byte("a")), qt.Equals, false)
   262  	c.Assert(ReaderContains(nil, nil), qt.Equals, false)
   263  }
   264  
   265  func TestGetTitleFunc(t *testing.T) {
   266  	title := "somewhere over the rainbow"
   267  	c := qt.New(t)
   268  
   269  	c.Assert(GetTitleFunc("go")(title), qt.Equals, "Somewhere Over The Rainbow")
   270  	c.Assert(GetTitleFunc("chicago")(title), qt.Equals, "Somewhere over the Rainbow")
   271  	c.Assert(GetTitleFunc("Chicago")(title), qt.Equals, "Somewhere over the Rainbow")
   272  	c.Assert(GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow")
   273  	c.Assert(GetTitleFunc("ap")(title), qt.Equals, "Somewhere Over the Rainbow")
   274  	c.Assert(GetTitleFunc("")(title), qt.Equals, "Somewhere Over the Rainbow")
   275  	c.Assert(GetTitleFunc("unknown")(title), qt.Equals, "Somewhere Over the Rainbow")
   276  }
   277  
   278  func BenchmarkReaderContains(b *testing.B) {
   279  	b.ResetTimer()
   280  	for i := 0; i < b.N; i++ {
   281  		for i, this := range containsBenchTestData {
   282  			result := ReaderContains(strings.NewReader(this.v1), this.v2)
   283  			if result != this.expect {
   284  				b.Errorf("[%d] got %t but expected %t", i, result, this.expect)
   285  			}
   286  		}
   287  	}
   288  }
   289  
   290  func TestUniqueStrings(t *testing.T) {
   291  	in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"}
   292  	output := UniqueStrings(in)
   293  	expected := []string{"a", "b", "c", "", "d"}
   294  	if !reflect.DeepEqual(output, expected) {
   295  		t.Errorf("Expected %#v, got %#v\n", expected, output)
   296  	}
   297  }
   298  
   299  func TestUniqueStringsReuse(t *testing.T) {
   300  	in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"}
   301  	output := UniqueStringsReuse(in)
   302  	expected := []string{"a", "b", "c", "", "d"}
   303  	if !reflect.DeepEqual(output, expected) {
   304  		t.Errorf("Expected %#v, got %#v\n", expected, output)
   305  	}
   306  }
   307  
   308  func TestUniqueStringsSorted(t *testing.T) {
   309  	c := qt.New(t)
   310  	in := []string{"a", "a", "b", "c", "b", "", "a", "", "d"}
   311  	output := UniqueStringsSorted(in)
   312  	expected := []string{"", "a", "b", "c", "d"}
   313  	c.Assert(output, qt.DeepEquals, expected)
   314  	c.Assert(UniqueStringsSorted(nil), qt.IsNil)
   315  }
   316  
   317  func TestFindAvailablePort(t *testing.T) {
   318  	c := qt.New(t)
   319  	addr, err := FindAvailablePort()
   320  	c.Assert(err, qt.IsNil)
   321  	c.Assert(addr, qt.Not(qt.IsNil))
   322  	c.Assert(addr.Port > 0, qt.Equals, true)
   323  }
   324  
   325  func TestFastMD5FromFile(t *testing.T) {
   326  	fs := afero.NewMemMapFs()
   327  
   328  	if err := afero.WriteFile(fs, "small.txt", []byte("abc"), 0777); err != nil {
   329  		t.Fatal(err)
   330  	}
   331  
   332  	if err := afero.WriteFile(fs, "small2.txt", []byte("abd"), 0777); err != nil {
   333  		t.Fatal(err)
   334  	}
   335  
   336  	if err := afero.WriteFile(fs, "bigger.txt", []byte(strings.Repeat("a bc d e", 100)), 0777); err != nil {
   337  		t.Fatal(err)
   338  	}
   339  
   340  	if err := afero.WriteFile(fs, "bigger2.txt", []byte(strings.Repeat("c d e f g", 100)), 0777); err != nil {
   341  		t.Fatal(err)
   342  	}
   343  
   344  	c := qt.New(t)
   345  
   346  	sf1, err := fs.Open("small.txt")
   347  	c.Assert(err, qt.IsNil)
   348  	sf2, err := fs.Open("small2.txt")
   349  	c.Assert(err, qt.IsNil)
   350  
   351  	bf1, err := fs.Open("bigger.txt")
   352  	c.Assert(err, qt.IsNil)
   353  	bf2, err := fs.Open("bigger2.txt")
   354  	c.Assert(err, qt.IsNil)
   355  
   356  	defer sf1.Close()
   357  	defer sf2.Close()
   358  	defer bf1.Close()
   359  	defer bf2.Close()
   360  
   361  	m1, err := MD5FromFileFast(sf1)
   362  	c.Assert(err, qt.IsNil)
   363  	c.Assert(m1, qt.Equals, "e9c8989b64b71a88b4efb66ad05eea96")
   364  
   365  	m2, err := MD5FromFileFast(sf2)
   366  	c.Assert(err, qt.IsNil)
   367  	c.Assert(m2, qt.Not(qt.Equals), m1)
   368  
   369  	m3, err := MD5FromFileFast(bf1)
   370  	c.Assert(err, qt.IsNil)
   371  	c.Assert(m3, qt.Not(qt.Equals), m2)
   372  
   373  	m4, err := MD5FromFileFast(bf2)
   374  	c.Assert(err, qt.IsNil)
   375  	c.Assert(m4, qt.Not(qt.Equals), m3)
   376  
   377  	m5, err := MD5FromReader(bf2)
   378  	c.Assert(err, qt.IsNil)
   379  	c.Assert(m5, qt.Not(qt.Equals), m4)
   380  }
   381  
   382  func BenchmarkMD5FromFileFast(b *testing.B) {
   383  	fs := afero.NewMemMapFs()
   384  
   385  	for _, full := range []bool{false, true} {
   386  		b.Run(fmt.Sprintf("full=%t", full), func(b *testing.B) {
   387  			for i := 0; i < b.N; i++ {
   388  				b.StopTimer()
   389  				if err := afero.WriteFile(fs, "file.txt", []byte(strings.Repeat("1234567890", 2000)), 0777); err != nil {
   390  					b.Fatal(err)
   391  				}
   392  				f, err := fs.Open("file.txt")
   393  				if err != nil {
   394  					b.Fatal(err)
   395  				}
   396  				b.StartTimer()
   397  				if full {
   398  					if _, err := MD5FromReader(f); err != nil {
   399  						b.Fatal(err)
   400  					}
   401  				} else {
   402  					if _, err := MD5FromFileFast(f); err != nil {
   403  						b.Fatal(err)
   404  					}
   405  				}
   406  				f.Close()
   407  			}
   408  		})
   409  	}
   410  }
   411  
   412  func BenchmarkUniqueStrings(b *testing.B) {
   413  	input := []string{"a", "b", "d", "e", "d", "h", "a", "i"}
   414  
   415  	b.Run("Safe", func(b *testing.B) {
   416  		for i := 0; i < b.N; i++ {
   417  			result := UniqueStrings(input)
   418  			if len(result) != 6 {
   419  				b.Fatal(fmt.Sprintf("invalid count: %d", len(result)))
   420  			}
   421  		}
   422  	})
   423  
   424  	b.Run("Reuse slice", func(b *testing.B) {
   425  		b.StopTimer()
   426  		inputs := make([][]string, b.N)
   427  		for i := 0; i < b.N; i++ {
   428  			inputc := make([]string, len(input))
   429  			copy(inputc, input)
   430  			inputs[i] = inputc
   431  		}
   432  		b.StartTimer()
   433  		for i := 0; i < b.N; i++ {
   434  			inputc := inputs[i]
   435  
   436  			result := UniqueStringsReuse(inputc)
   437  			if len(result) != 6 {
   438  				b.Fatal(fmt.Sprintf("invalid count: %d", len(result)))
   439  			}
   440  		}
   441  	})
   442  
   443  	b.Run("Reuse slice sorted", func(b *testing.B) {
   444  		b.StopTimer()
   445  		inputs := make([][]string, b.N)
   446  		for i := 0; i < b.N; i++ {
   447  			inputc := make([]string, len(input))
   448  			copy(inputc, input)
   449  			inputs[i] = inputc
   450  		}
   451  		b.StartTimer()
   452  		for i := 0; i < b.N; i++ {
   453  			inputc := inputs[i]
   454  
   455  			result := UniqueStringsSorted(inputc)
   456  			if len(result) != 6 {
   457  				b.Fatal(fmt.Sprintf("invalid count: %d", len(result)))
   458  			}
   459  		}
   460  	})
   461  }
   462  
   463  func TestHashString(t *testing.T) {
   464  	c := qt.New(t)
   465  
   466  	c.Assert(HashString("a", "b"), qt.Equals, "2712570657419664240")
   467  	c.Assert(HashString("ab"), qt.Equals, "590647783936702392")
   468  }