code.gitea.io/gitea@v1.22.3/tests/integration/linguist_test.go (about)

     1  // Copyright 2024 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package integration
     5  
     6  import (
     7  	"context"
     8  	"net/url"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"code.gitea.io/gitea/models/db"
    14  	repo_model "code.gitea.io/gitea/models/repo"
    15  	"code.gitea.io/gitea/models/unittest"
    16  	user_model "code.gitea.io/gitea/models/user"
    17  	"code.gitea.io/gitea/modules/git"
    18  	"code.gitea.io/gitea/modules/indexer/stats"
    19  	"code.gitea.io/gitea/modules/queue"
    20  	repo_service "code.gitea.io/gitea/services/repository"
    21  	files_service "code.gitea.io/gitea/services/repository/files"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  )
    25  
    26  func TestLinguist(t *testing.T) {
    27  	onGiteaRun(t, func(t *testing.T, _ *url.URL) {
    28  		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
    29  
    30  		cppContent := "#include <iostream>\nint main() {\nstd::cout << \"Hello Gitea!\";\nreturn 0;\n}"
    31  		pyContent := "print(\"Hello Gitea!\")"
    32  		phpContent := "<?php\necho 'Hallo Welt';\n?>"
    33  		lockContent := "# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand."
    34  		mdContent := "markdown"
    35  
    36  		cases := []struct {
    37  			GitAttributesContent  string
    38  			FilesToAdd            []*files_service.ChangeRepoFile
    39  			ExpectedLanguageOrder []string
    40  		}{
    41  			// case 0
    42  			{
    43  				ExpectedLanguageOrder: []string{},
    44  			},
    45  			// case 1
    46  			{
    47  				FilesToAdd: []*files_service.ChangeRepoFile{
    48  					{
    49  						TreePath:      "cplusplus.cpp",
    50  						ContentReader: strings.NewReader(cppContent),
    51  					},
    52  					{
    53  						TreePath:      "python.py",
    54  						ContentReader: strings.NewReader(pyContent),
    55  					},
    56  					{
    57  						TreePath:      "php.php",
    58  						ContentReader: strings.NewReader(phpContent),
    59  					},
    60  				},
    61  				ExpectedLanguageOrder: []string{"C++", "PHP", "Python"},
    62  			},
    63  			// case 2
    64  			{
    65  				FilesToAdd: []*files_service.ChangeRepoFile{
    66  					{
    67  						TreePath:      ".cplusplus.cpp",
    68  						ContentReader: strings.NewReader(cppContent),
    69  					},
    70  					{
    71  						TreePath:      "python.py",
    72  						ContentReader: strings.NewReader(pyContent),
    73  					},
    74  					{
    75  						TreePath:      "vendor/php.php",
    76  						ContentReader: strings.NewReader(phpContent),
    77  					},
    78  				},
    79  				ExpectedLanguageOrder: []string{"Python"},
    80  			},
    81  			// case 3
    82  			{
    83  				GitAttributesContent: "*.cpp linguist-language=Go",
    84  				FilesToAdd: []*files_service.ChangeRepoFile{
    85  					{
    86  						TreePath:      "cplusplus.cpp",
    87  						ContentReader: strings.NewReader(cppContent),
    88  					},
    89  				},
    90  				ExpectedLanguageOrder: []string{"Go"},
    91  			},
    92  			// case 4
    93  			{
    94  				GitAttributesContent: "*.cpp gitlab-language=Go?parent=json",
    95  				FilesToAdd: []*files_service.ChangeRepoFile{
    96  					{
    97  						TreePath:      "cplusplus.cpp",
    98  						ContentReader: strings.NewReader(cppContent),
    99  					},
   100  				},
   101  				ExpectedLanguageOrder: []string{"Go"},
   102  			},
   103  			// case 5
   104  			{
   105  				GitAttributesContent: "*.cpp linguist-language=HTML gitlab-language=Go?parent=json",
   106  				FilesToAdd: []*files_service.ChangeRepoFile{
   107  					{
   108  						TreePath:      "cplusplus.cpp",
   109  						ContentReader: strings.NewReader(cppContent),
   110  					},
   111  				},
   112  				ExpectedLanguageOrder: []string{"HTML"},
   113  			},
   114  			// case 6
   115  			{
   116  				GitAttributesContent: "vendor/** linguist-vendored=false",
   117  				FilesToAdd: []*files_service.ChangeRepoFile{
   118  					{
   119  						TreePath:      "vendor/php.php",
   120  						ContentReader: strings.NewReader(phpContent),
   121  					},
   122  				},
   123  				ExpectedLanguageOrder: []string{"PHP"},
   124  			},
   125  			// case 7
   126  			{
   127  				GitAttributesContent: "*.cpp linguist-vendored=true\n*.py linguist-vendored\nvendor/** -linguist-vendored",
   128  				FilesToAdd: []*files_service.ChangeRepoFile{
   129  					{
   130  						TreePath:      "cplusplus.cpp",
   131  						ContentReader: strings.NewReader(cppContent),
   132  					},
   133  					{
   134  						TreePath:      "python.py",
   135  						ContentReader: strings.NewReader(pyContent),
   136  					},
   137  					{
   138  						TreePath:      "vendor/php.php",
   139  						ContentReader: strings.NewReader(phpContent),
   140  					},
   141  				},
   142  				ExpectedLanguageOrder: []string{"PHP"},
   143  			},
   144  			// case 8
   145  			{
   146  				GitAttributesContent: "poetry.lock linguist-language=Go",
   147  				FilesToAdd: []*files_service.ChangeRepoFile{
   148  					{
   149  						TreePath:      "poetry.lock",
   150  						ContentReader: strings.NewReader(lockContent),
   151  					},
   152  				},
   153  				ExpectedLanguageOrder: []string{"Go"},
   154  			},
   155  			// case 9
   156  			{
   157  				GitAttributesContent: "poetry.lock linguist-generated=false",
   158  				FilesToAdd: []*files_service.ChangeRepoFile{
   159  					{
   160  						TreePath:      "poetry.lock",
   161  						ContentReader: strings.NewReader(lockContent),
   162  					},
   163  				},
   164  				ExpectedLanguageOrder: []string{"TOML"},
   165  			},
   166  			// case 10
   167  			{
   168  				GitAttributesContent: "*.cpp -linguist-detectable",
   169  				FilesToAdd: []*files_service.ChangeRepoFile{
   170  					{
   171  						TreePath:      "cplusplus.cpp",
   172  						ContentReader: strings.NewReader(cppContent),
   173  					},
   174  				},
   175  				ExpectedLanguageOrder: []string{},
   176  			},
   177  			// case 11
   178  			{
   179  				GitAttributesContent: "*.md linguist-detectable",
   180  				FilesToAdd: []*files_service.ChangeRepoFile{
   181  					{
   182  						TreePath:      "test.md",
   183  						ContentReader: strings.NewReader(mdContent),
   184  					},
   185  				},
   186  				ExpectedLanguageOrder: []string{"Markdown"},
   187  			},
   188  			// case 12
   189  			{
   190  				GitAttributesContent: "test2.md linguist-detectable",
   191  				FilesToAdd: []*files_service.ChangeRepoFile{
   192  					{
   193  						TreePath:      "cplusplus.cpp",
   194  						ContentReader: strings.NewReader(cppContent),
   195  					},
   196  					{
   197  						TreePath:      "test.md",
   198  						ContentReader: strings.NewReader(mdContent),
   199  					},
   200  					{
   201  						TreePath:      "test2.md",
   202  						ContentReader: strings.NewReader(mdContent),
   203  					},
   204  				},
   205  				ExpectedLanguageOrder: []string{"C++", "Markdown"},
   206  			},
   207  			// case 13
   208  			{
   209  				GitAttributesContent: "README.md linguist-documentation=false",
   210  				FilesToAdd: []*files_service.ChangeRepoFile{
   211  					{
   212  						TreePath:      "README.md",
   213  						ContentReader: strings.NewReader(mdContent),
   214  					},
   215  				},
   216  				ExpectedLanguageOrder: []string{"Markdown"},
   217  			},
   218  		}
   219  
   220  		for i, c := range cases {
   221  			repo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
   222  				Name: "linguist-test",
   223  			})
   224  			assert.NoError(t, err)
   225  
   226  			files := []*files_service.ChangeRepoFile{
   227  				{
   228  					TreePath:      ".gitattributes",
   229  					ContentReader: strings.NewReader(c.GitAttributesContent),
   230  				},
   231  			}
   232  			files = append(files, c.FilesToAdd...)
   233  			for _, f := range files {
   234  				f.Operation = "create"
   235  			}
   236  
   237  			_, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, user, &files_service.ChangeRepoFilesOptions{
   238  				Files:     files,
   239  				OldBranch: repo.DefaultBranch,
   240  				NewBranch: repo.DefaultBranch,
   241  			})
   242  			assert.NoError(t, err)
   243  
   244  			assert.NoError(t, stats.UpdateRepoIndexer(repo))
   245  			assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 10*time.Second))
   246  
   247  			stats, err := repo_model.GetTopLanguageStats(db.DefaultContext, repo, len(c.FilesToAdd))
   248  			assert.NoError(t, err)
   249  
   250  			languages := make([]string, 0, len(stats))
   251  			for _, s := range stats {
   252  				languages = append(languages, s.Language)
   253  			}
   254  			assert.Equal(t, c.ExpectedLanguageOrder, languages, "case %d: unexpected language stats", i)
   255  
   256  			assert.NoError(t, repo_service.DeleteRepository(db.DefaultContext, user, repo, false))
   257  		}
   258  	})
   259  }