site_benchmark_new_test.go - hugo - [fork] hugo port for 9front
 (HTM) git clone https://git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       site_benchmark_new_test.go (14616B)
       ---
            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 hugolib
           15 
           16 import (
           17         "fmt"
           18         "math/rand"
           19         "path"
           20         "path/filepath"
           21         "strconv"
           22         "strings"
           23         "testing"
           24 
           25         "github.com/gohugoio/hugo/resources/page"
           26 
           27         qt "github.com/frankban/quicktest"
           28 )
           29 
           30 type siteBenchmarkTestcase struct {
           31         name   string
           32         create func(t testing.TB) *sitesBuilder
           33         check  func(s *sitesBuilder)
           34 }
           35 
           36 func getBenchmarkSiteDeepContent(b testing.TB) *sitesBuilder {
           37         pageContent := func(size int) string {
           38                 return getBenchmarkTestDataPageContentForMarkdown(size, false, "", benchmarkMarkdownSnippets)
           39         }
           40 
           41         sb := newTestSitesBuilder(b).WithConfigFile("toml", `
           42 baseURL = "https://example.com"
           43 
           44 [languages]
           45 [languages.en]
           46 weight=1
           47 contentDir="content/en"
           48 [languages.fr]
           49 weight=2
           50 contentDir="content/fr"
           51 [languages.no]
           52 weight=3
           53 contentDir="content/no"
           54 [languages.sv]
           55 weight=4
           56 contentDir="content/sv"
           57                         
           58 `)
           59 
           60         createContent := func(dir, name string) {
           61                 sb.WithContent(filepath.Join("content", dir, name), pageContent(1))
           62         }
           63 
           64         createBundledFiles := func(dir string) {
           65                 sb.WithContent(filepath.Join("content", dir, "data.json"), `{ "hello": "world" }`)
           66                 for i := 1; i <= 3; i++ {
           67                         sb.WithContent(filepath.Join("content", dir, fmt.Sprintf("page%d.md", i)), pageContent(1))
           68                 }
           69         }
           70 
           71         for _, lang := range []string{"en", "fr", "no", "sv"} {
           72                 for level := 1; level <= 5; level++ {
           73                         sectionDir := path.Join(lang, strings.Repeat("section/", level))
           74                         createContent(sectionDir, "_index.md")
           75                         createBundledFiles(sectionDir)
           76                         for i := 1; i <= 3; i++ {
           77                                 leafBundleDir := path.Join(sectionDir, fmt.Sprintf("bundle%d", i))
           78                                 createContent(leafBundleDir, "index.md")
           79                                 createBundledFiles(path.Join(leafBundleDir, "assets1"))
           80                                 createBundledFiles(path.Join(leafBundleDir, "assets1", "assets2"))
           81                         }
           82                 }
           83         }
           84 
           85         return sb
           86 }
           87 
           88 func getBenchmarkTestDataPageContentForMarkdown(size int, toml bool, category, markdown string) string {
           89         base := `---
           90 title: "My Page"
           91 %s
           92 ---
           93 
           94 My page content.
           95 `
           96         if toml {
           97                 base = `+++
           98 title="My Page"
           99 %s
          100 +++
          101 
          102 My page content.
          103 `
          104         }
          105 
          106         var categoryKey string
          107         if category != "" {
          108                 categoryKey = fmt.Sprintf("categories: [%s]", category)
          109                 if toml {
          110                         categoryKey = fmt.Sprintf("categories=[%s]", category)
          111                 }
          112         }
          113         base = fmt.Sprintf(base, categoryKey)
          114 
          115         return base + strings.Repeat(markdown, size)
          116 }
          117 
          118 const benchmarkMarkdownSnippets = `
          119 
          120 ## Links
          121 
          122 
          123 This is [an example](http://example.com/ "Title") inline link.
          124 
          125 [This link](http://example.net/) has no title attribute.
          126 
          127 This is [Relative](/all-is-relative).
          128 
          129 See my [About](/about/) page for details. 
          130 `
          131 
          132 func getBenchmarkSiteTestCases() []siteBenchmarkTestcase {
          133         pageContentWithCategory := func(size int, category string) string {
          134                 return getBenchmarkTestDataPageContentForMarkdown(size, false, category, benchmarkMarkdownSnippets)
          135         }
          136 
          137         pageContent := func(size int) string {
          138                 return getBenchmarkTestDataPageContentForMarkdown(size, false, "", benchmarkMarkdownSnippets)
          139         }
          140 
          141         config := `
          142 baseURL = "https://example.com"
          143 `
          144 
          145         benchmarks := []siteBenchmarkTestcase{
          146                 {
          147                         "Bundle with image", func(b testing.TB) *sitesBuilder {
          148                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", config)
          149                                 sb.WithContent("content/blog/mybundle/index.md", pageContent(1))
          150                                 sb.WithSunset("content/blog/mybundle/sunset1.jpg")
          151 
          152                                 return sb
          153                         },
          154                         func(s *sitesBuilder) {
          155                                 s.AssertFileContent("public/blog/mybundle/index.html", "/blog/mybundle/sunset1.jpg")
          156                                 s.CheckExists("public/blog/mybundle/sunset1.jpg")
          157                         },
          158                 },
          159                 {
          160                         "Bundle with JSON file", func(b testing.TB) *sitesBuilder {
          161                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", config)
          162                                 sb.WithContent("content/blog/mybundle/index.md", pageContent(1))
          163                                 sb.WithContent("content/blog/mybundle/mydata.json", `{ "hello": "world" }`)
          164 
          165                                 return sb
          166                         },
          167                         func(s *sitesBuilder) {
          168                                 s.AssertFileContent("public/blog/mybundle/index.html", "Resources: application/json: /blog/mybundle/mydata.json")
          169                                 s.CheckExists("public/blog/mybundle/mydata.json")
          170                         },
          171                 },
          172                 {
          173                         "Tags and categories", func(b testing.TB) *sitesBuilder {
          174                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", `
          175 title = "Tags and Cats"
          176 baseURL = "https://example.com"
          177 
          178 `)
          179 
          180                                 const pageTemplate = `
          181 ---
          182 title: "Some tags and cats"
          183 categories: ["caGR", "cbGR"]
          184 tags: ["taGR", "tbGR"]
          185 ---
          186 
          187 Some content.
          188                         
          189 `
          190                                 for i := 1; i <= 100; i++ {
          191                                         content := strings.Replace(pageTemplate, "GR", strconv.Itoa(i/3), -1)
          192                                         sb.WithContent(fmt.Sprintf("content/page%d.md", i), content)
          193                                 }
          194 
          195                                 return sb
          196                         },
          197                         func(s *sitesBuilder) {
          198                                 s.AssertFileContent("public/page3/index.html", "/page3/|Permalink: https://example.com/page3/")
          199                                 s.AssertFileContent("public/tags/ta3/index.html", "a3")
          200                         },
          201                 },
          202                 {
          203                         "Canonify URLs", func(b testing.TB) *sitesBuilder {
          204                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", `
          205 title = "Canon"
          206 baseURL = "https://example.com"
          207 canonifyURLs = true
          208 
          209 `)
          210                                 for i := 1; i <= 100; i++ {
          211                                         sb.WithContent(fmt.Sprintf("content/page%d.md", i), pageContent(i))
          212                                 }
          213 
          214                                 return sb
          215                         },
          216                         func(s *sitesBuilder) {
          217                                 s.AssertFileContent("public/page8/index.html", "https://example.com/about/")
          218                         },
          219                 },
          220 
          221                 {
          222                         "Deep content tree", func(b testing.TB) *sitesBuilder {
          223                                 return getBenchmarkSiteDeepContent(b)
          224                         },
          225                         func(s *sitesBuilder) {
          226                                 s.CheckExists("public/blog/mybundle/index.html")
          227                                 s.Assert(len(s.H.Sites), qt.Equals, 4)
          228                                 s.Assert(len(s.H.Sites[0].RegularPages()), qt.Equals, len(s.H.Sites[1].RegularPages()))
          229                                 s.Assert(len(s.H.Sites[0].RegularPages()), qt.Equals, 30)
          230                         },
          231                 },
          232                 {
          233                         "TOML front matter", func(b testing.TB) *sitesBuilder {
          234                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", config)
          235                                 for i := 1; i <= 200; i++ {
          236                                         content := getBenchmarkTestDataPageContentForMarkdown(1, true, "\"a\", \"b\", \"c\"", benchmarkMarkdownSnippets)
          237                                         sb.WithContent(fmt.Sprintf("content/p%d.md", i), content)
          238                                 }
          239 
          240                                 return sb
          241                         },
          242                         func(s *sitesBuilder) {
          243                         },
          244                 },
          245                 {
          246                         "Many HTML templates", func(b testing.TB) *sitesBuilder {
          247                                 pageTemplateTemplate := `
          248 <!DOCTYPE html>
          249 <html>
          250   <head>
          251     <meta charset="utf-8">
          252     <title>{{ if not .IsPage }}{{ .Title }}{{ else }}{{ printf "Site: %s" site.Title }}{{ end }}</title>
          253     <style>
          254      body {
          255        margin: 3rem;
          256      }
          257     </style>
          258   </head>
          259   <body>
          260     <div class="page">{{ .Content }}</div>
          261     <ul>
          262     {{ with .Pages }}
          263     {{ range . }}
          264     <li><a href="{{ .RelPermalink }}">{{ .LinkTitle }} {{ if not .IsNode }} (Page){{ end }}</a></li>
          265     {{ end }}
          266     {{ end }}
          267     </ul>
          268   </body>
          269 </html>
          270 `
          271 
          272                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", `
          273 baseURL = "https://example.com"
          274 
          275 [languages]
          276 [languages.en]
          277 weight=1
          278 contentDir="content/en"
          279 [languages.fr]
          280 weight=2
          281 contentDir="content/fr"
          282 [languages.no]
          283 weight=3
          284 contentDir="content/no"
          285 [languages.sv]
          286 weight=4
          287 contentDir="content/sv"
          288                         
          289 `)
          290 
          291                                 createContent := func(dir, name string) {
          292                                         sb.WithContent(filepath.Join("content", dir, name), pageContent(1))
          293                                 }
          294 
          295                                 for _, lang := range []string{"en", "fr", "no", "sv"} {
          296                                         sb.WithTemplatesAdded(fmt.Sprintf("_default/single.%s.html", lang), pageTemplateTemplate)
          297                                         sb.WithTemplatesAdded(fmt.Sprintf("_default/list.%s.html", lang), pageTemplateTemplate)
          298 
          299                                         for level := 1; level <= 5; level++ {
          300                                                 sectionDir := path.Join(lang, strings.Repeat("section/", level))
          301                                                 createContent(sectionDir, "_index.md")
          302                                                 for i := 1; i <= 3; i++ {
          303                                                         leafBundleDir := path.Join(sectionDir, fmt.Sprintf("bundle%d", i))
          304                                                         createContent(leafBundleDir, "index.md")
          305                                                 }
          306                                         }
          307                                 }
          308 
          309                                 return sb
          310                         },
          311                         func(s *sitesBuilder) {
          312                                 s.CheckExists("public/blog/mybundle/index.html")
          313                                 s.Assert(len(s.H.Sites), qt.Equals, 4)
          314                                 s.Assert(len(s.H.Sites[0].RegularPages()), qt.Equals, len(s.H.Sites[1].RegularPages()))
          315                                 s.Assert(len(s.H.Sites[0].RegularPages()), qt.Equals, 15)
          316                         },
          317                 },
          318                 {
          319                         "Page collections", func(b testing.TB) *sitesBuilder {
          320                                 pageTemplateTemplate := `
          321 {{ if .IsNode }}
          322 {{ len .Paginator.Pages }}
          323 {{ end }}
          324 {{ len .Sections }}
          325 {{ len .Pages }}
          326 {{ len .RegularPages }}
          327 {{ len .Resources }}
          328 {{ len site.RegularPages }}
          329 {{ len site.Pages }}
          330 {{ with .NextInSection }}Next in section: {{ .RelPermalink }}{{ end }}
          331 {{ with .PrevInSection }}Prev in section: {{ .RelPermalink }}{{ end }}
          332 {{ with .Next }}Next: {{ .RelPermalink }}{{ end }}
          333 {{ with .Prev }}Prev: {{ .RelPermalink }}{{ end }}
          334 `
          335 
          336                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", `
          337 baseURL = "https://example.com"
          338 
          339 [languages]
          340 [languages.en]
          341 weight=1
          342 contentDir="content/en"
          343 [languages.fr]
          344 weight=2
          345 contentDir="content/fr"
          346 [languages.no]
          347 weight=3
          348 contentDir="content/no"
          349 [languages.sv]
          350 weight=4
          351 contentDir="content/sv"
          352                         
          353 `)
          354 
          355                                 sb.WithTemplates("index.html", pageTemplateTemplate)
          356                                 sb.WithTemplates("_default/single.html", pageTemplateTemplate)
          357                                 sb.WithTemplates("_default/list.html", pageTemplateTemplate)
          358 
          359                                 r := rand.New(rand.NewSource(99))
          360 
          361                                 createContent := func(dir, name string) {
          362                                         var content string
          363                                         if strings.Contains(name, "_index") {
          364                                                 content = pageContent(1)
          365                                         } else {
          366                                                 content = pageContentWithCategory(1, fmt.Sprintf("category%d", r.Intn(5)+1))
          367                                         }
          368 
          369                                         sb.WithContent(filepath.Join("content", dir, name), content)
          370                                 }
          371 
          372                                 createBundledFiles := func(dir string) {
          373                                         sb.WithContent(filepath.Join("content", dir, "data.json"), `{ "hello": "world" }`)
          374                                         for i := 1; i <= 3; i++ {
          375                                                 sb.WithContent(filepath.Join("content", dir, fmt.Sprintf("page%d.md", i)), pageContent(1))
          376                                         }
          377                                 }
          378 
          379                                 for _, lang := range []string{"en", "fr", "no", "sv"} {
          380                                         for level := 1; level <= r.Intn(5)+1; level++ {
          381                                                 sectionDir := path.Join(lang, strings.Repeat("section/", level))
          382                                                 createContent(sectionDir, "_index.md")
          383                                                 createBundledFiles(sectionDir)
          384                                                 for i := 1; i <= r.Intn(20)+1; i++ {
          385                                                         leafBundleDir := path.Join(sectionDir, fmt.Sprintf("bundle%d", i))
          386                                                         createContent(leafBundleDir, "index.md")
          387                                                         createBundledFiles(path.Join(leafBundleDir, "assets1"))
          388                                                         createBundledFiles(path.Join(leafBundleDir, "assets1", "assets2"))
          389                                                 }
          390                                         }
          391                                 }
          392 
          393                                 return sb
          394                         },
          395                         func(s *sitesBuilder) {
          396                                 s.CheckExists("public/blog/mybundle/index.html")
          397                                 s.Assert(len(s.H.Sites), qt.Equals, 4)
          398                                 s.Assert(len(s.H.Sites[0].RegularPages()), qt.Equals, 26)
          399                         },
          400                 },
          401                 {
          402                         "List terms", func(b testing.TB) *sitesBuilder {
          403                                 pageTemplateTemplate := `
          404 <ul>
          405     {{ range (.GetTerms "categories") }}
          406         <li><a href="{{ .Permalink }}">{{ .LinkTitle }}</a></li>
          407    {{ end }}
          408 </ul>
          409 `
          410 
          411                                 sb := newTestSitesBuilder(b).WithConfigFile("toml", `
          412 baseURL = "https://example.com"
          413 `)
          414 
          415                                 sb.WithTemplates("_default/single.html", pageTemplateTemplate)
          416                                 sb.WithTemplates("_default/list.html", "List")
          417 
          418                                 r := rand.New(rand.NewSource(99))
          419 
          420                                 createContent := func(dir, name string) {
          421                                         var content string
          422                                         if strings.Contains(name, "_index") {
          423                                                 // Empty
          424                                         } else {
          425                                                 content = pageContentWithCategory(1, fmt.Sprintf("category%d", r.Intn(5)+1))
          426                                         }
          427                                         sb.WithContent(filepath.Join("content", dir, name), content)
          428                                 }
          429 
          430                                 for level := 1; level <= r.Intn(5)+1; level++ {
          431                                         sectionDir := path.Join(strings.Repeat("section/", level))
          432                                         createContent(sectionDir, "_index.md")
          433                                         for i := 1; i <= r.Intn(33); i++ {
          434                                                 leafBundleDir := path.Join(sectionDir, fmt.Sprintf("bundle%d", i))
          435                                                 createContent(leafBundleDir, "index.md")
          436                                         }
          437                                 }
          438 
          439                                 return sb
          440                         },
          441                         func(s *sitesBuilder) {
          442                                 s.AssertFileContent("public/section/bundle8/index.html", `<a href="https://example.com/categories/category1/">`)
          443                                 s.Assert(len(s.H.Sites), qt.Equals, 1)
          444                                 s.Assert(len(s.H.Sites[0].RegularPages()), qt.Equals, 35)
          445                         },
          446                 },
          447         }
          448 
          449         return benchmarks
          450 }
          451 
          452 // Run the benchmarks below as tests. Mostly useful when adding new benchmark
          453 // variants.
          454 func TestBenchmarkSite(b *testing.T) {
          455         benchmarks := getBenchmarkSiteTestCases()
          456         for _, bm := range benchmarks {
          457                 if bm.name != "Deep content tree" {
          458                         continue
          459                 }
          460                 b.Run(bm.name, func(b *testing.T) {
          461                         s := bm.create(b)
          462 
          463                         err := s.BuildE(BuildCfg{})
          464                         if err != nil {
          465                                 b.Fatal(err)
          466                         }
          467                         bm.check(s)
          468                 })
          469         }
          470 }
          471 
          472 func TestBenchmarkSiteDeepContentEdit(t *testing.T) {
          473         b := getBenchmarkSiteDeepContent(t).Running()
          474         b.Build(BuildCfg{})
          475 
          476         p := b.H.Sites[0].RegularPages()[12]
          477 
          478         b.EditFiles(p.File().Filename(), fmt.Sprintf(`---
          479 title: %s
          480 ---
          481 
          482 Edited!!`, p.Title()))
          483 
          484         counters := &buildCounters{}
          485 
          486         b.Build(BuildCfg{testCounters: counters})
          487 
          488         // We currently rebuild all the language versions of the same content file.
          489         // We could probably optimize that case, but it's not trivial.
          490         b.Assert(int(counters.contentRenderCounter.Load()), qt.Equals, 4)
          491         b.AssertFileContent("public"+p.RelPermalink()+"index.html", "Edited!!")
          492 }
          493 
          494 func BenchmarkSiteNew(b *testing.B) {
          495         rnd := rand.New(rand.NewSource(32))
          496         benchmarks := getBenchmarkSiteTestCases()
          497         for _, edit := range []bool{true, false} {
          498                 for _, bm := range benchmarks {
          499                         name := bm.name
          500                         if edit {
          501                                 name = "Edit_" + name
          502                         } else {
          503                                 name = "Regular_" + name
          504                         }
          505                         b.Run(name, func(b *testing.B) {
          506                                 sites := make([]*sitesBuilder, b.N)
          507                                 for i := 0; i < b.N; i++ {
          508                                         sites[i] = bm.create(b)
          509                                         if edit {
          510                                                 sites[i].Running()
          511                                         }
          512                                 }
          513 
          514                                 b.ResetTimer()
          515                                 for i := 0; i < b.N; i++ {
          516                                         if edit {
          517                                                 b.StopTimer()
          518                                         }
          519                                         s := sites[i]
          520                                         err := s.BuildE(BuildCfg{})
          521                                         if err != nil {
          522                                                 b.Fatal(err)
          523                                         }
          524                                         bm.check(s)
          525 
          526                                         if edit {
          527                                                 if edit {
          528                                                         b.StartTimer()
          529                                                 }
          530                                                 // Edit a random page in a random language.
          531                                                 pages := s.H.Sites[rnd.Intn(len(s.H.Sites))].Pages()
          532                                                 var p page.Page
          533                                                 count := 0
          534                                                 for {
          535                                                         count++
          536                                                         if count > 100 {
          537                                                                 panic("infinite loop")
          538                                                         }
          539                                                         p = pages[rnd.Intn(len(pages))]
          540                                                         if p.File() != nil {
          541                                                                 break
          542                                                         }
          543                                                 }
          544 
          545                                                 s.EditFiles(p.File().Filename(), fmt.Sprintf(`---
          546 title: %s
          547 ---
          548 
          549 Edited!!`, p.Title()))
          550 
          551                                                 err := s.BuildE(BuildCfg{})
          552                                                 if err != nil {
          553                                                         b.Fatal(err)
          554                                                 }
          555                                         }
          556                                 }
          557                         })
          558                 }
          559         }
          560 }