1. package lexers_test
    
  2. 
    
  3. import (
    
  4. 	"bytes"
    
  5. 	"fmt"
    
  6. 	"io"
    
  7. 	"os"
    
  8. 	"path/filepath"
    
  9. 	"strconv"
    
  10. 	"strings"
    
  11. 	"testing"
    
  12. 
    
  13. 	"github.com/alecthomas/assert/v2"
    
  14. 	"github.com/alecthomas/repr"
    
  15. 
    
  16. 	"github.com/alecthomas/chroma/v2"
    
  17. 	"github.com/alecthomas/chroma/v2/formatters"
    
  18. 	"github.com/alecthomas/chroma/v2/lexers"
    
  19. 	"github.com/alecthomas/chroma/v2/styles"
    
  20. )
    
  21. 
    
  22. func TestCompileAllRegexes(t *testing.T) {
    
  23. 	for _, lexer := range lexers.GlobalLexerRegistry.Lexers {
    
  24. 		it, err := lexer.Tokenise(nil, "")
    
  25. 		assert.NoError(t, err, "%s failed", lexer.Config().Name)
    
  26. 		err = formatters.NoOp.Format(io.Discard, styles.SwapOff, it)
    
  27. 		assert.NoError(t, err, "%s failed", lexer.Config().Name)
    
  28. 	}
    
  29. }
    
  30. 
    
  31. func TestGet(t *testing.T) {
    
  32. 	t.Run("ByName", func(t *testing.T) {
    
  33. 		assert.True(t, lexers.Get("xml") == lexers.GlobalLexerRegistry.Get("XML"))
    
  34. 	})
    
  35. 	t.Run("ByAlias", func(t *testing.T) {
    
  36. 		assert.True(t, lexers.Get("as") == lexers.GlobalLexerRegistry.Get("Actionscript"))
    
  37. 	})
    
  38. 	t.Run("ViaFilename", func(t *testing.T) {
    
  39. 		expected := lexers.Get("XML")
    
  40. 		actual := lexers.GlobalLexerRegistry.Get("test.svg")
    
  41. 		assert.Equal(t,
    
  42. 			repr.String(expected.Config(), repr.Indent("  ")),
    
  43. 			repr.String(actual.Config(), repr.Indent("  ")))
    
  44. 	})
    
  45. }
    
  46. 
    
  47. func TestGlobs(t *testing.T) {
    
  48. 	filename := "main.go"
    
  49. 	for _, lexer := range lexers.GlobalLexerRegistry.Lexers {
    
  50. 		config := lexer.Config()
    
  51. 		for _, glob := range config.Filenames {
    
  52. 			_, err := filepath.Match(glob, filename)
    
  53. 			assert.NoError(t, err)
    
  54. 		}
    
  55. 		for _, glob := range config.AliasFilenames {
    
  56. 			_, err := filepath.Match(glob, filename)
    
  57. 			assert.NoError(t, err)
    
  58. 		}
    
  59. 	}
    
  60. }
    
  61. 
    
  62. func BenchmarkGet(b *testing.B) {
    
  63. 	for i := 0; i < b.N; i++ {
    
  64. 		lexers.Get("go")
    
  65. 	}
    
  66. }
    
  67. 
    
  68. func FileTest(t *testing.T, lexer chroma.Lexer, sourceFile, expectedFilename string) {
    
  69. 	t.Helper()
    
  70. 	t.Run(lexer.Config().Name+"/"+sourceFile, func(t *testing.T) {
    
  71. 		// Read and tokenise source text.
    
  72. 		sourceBytes, err := os.ReadFile(sourceFile)
    
  73. 		assert.NoError(t, err)
    
  74. 		actualTokens, err := chroma.Tokenise(lexer, nil, string(sourceBytes))
    
  75. 		assert.NoError(t, err)
    
  76. 
    
  77. 		// Check for error tokens early
    
  78. 		for _, token := range actualTokens {
    
  79. 			if token.Type == chroma.Error {
    
  80. 				t.Logf("Found Error token in lexer %s output: %s", lexer.Config().Name, repr.String(token))
    
  81. 			}
    
  82. 		}
    
  83. 
    
  84. 		// Use a bytes.Buffer to "render" the actual bytes
    
  85. 		var actualBytes bytes.Buffer
    
  86. 		err = formatters.JSON.Format(&actualBytes, nil, chroma.Literator(actualTokens...))
    
  87. 		assert.NoError(t, err)
    
  88. 
    
  89. 		expectedBytes, err := os.ReadFile(expectedFilename)
    
  90. 		assert.NoError(t, err)
    
  91. 
    
  92. 		// Check that the expected bytes are identical
    
  93. 		if !bytes.Equal(actualBytes.Bytes(), expectedBytes) {
    
  94. 			if os.Getenv("RECORD") == "true" {
    
  95. 				f, err := os.Create(expectedFilename)
    
  96. 				assert.NoError(t, err)
    
  97. 				_, err = f.Write(actualBytes.Bytes())
    
  98. 				assert.NoError(t, err)
    
  99. 				assert.NoError(t, f.Close())
    
  100. 			} else {
    
  101. 				// fail via an assertion of string values for diff output
    
  102. 				assert.Equal(t, string(expectedBytes), actualBytes.String())
    
  103. 			}
    
  104. 		}
    
  105. 	})
    
  106. }
    
  107. 
    
  108. // Test source files are in the form <key>.<key> and validation data is in the form <key>.<key>.expected.
    
  109. func TestLexers(t *testing.T) {
    
  110. 	files, err := os.ReadDir("testdata")
    
  111. 	assert.NoError(t, err)
    
  112. 
    
  113. 	for _, file := range files {
    
  114. 		// skip text analysis test files
    
  115. 		if file.Name() == "analysis" {
    
  116. 			continue
    
  117. 		}
    
  118. 
    
  119. 		if file.IsDir() {
    
  120. 			dirname := filepath.Join("testdata", file.Name())
    
  121. 			lexer := lexers.Get(file.Name())
    
  122. 			assert.NotZero(t, lexer)
    
  123. 
    
  124. 			subFiles, err := os.ReadDir(dirname)
    
  125. 			assert.NoError(t, err)
    
  126. 
    
  127. 			for _, subFile := range subFiles {
    
  128. 				ext := filepath.Ext(subFile.Name())[1:]
    
  129. 				if ext != "actual" {
    
  130. 					continue
    
  131. 				}
    
  132. 
    
  133. 				filename := filepath.Join(dirname, subFile.Name())
    
  134. 				expectedFilename := strings.TrimSuffix(filename, filepath.Ext(filename)) + ".expected"
    
  135. 
    
  136. 				lexer = chroma.Coalesce(lexer)
    
  137. 				FileTest(t, lexer, filename, expectedFilename)
    
  138. 			}
    
  139. 		} else {
    
  140. 			ext := filepath.Ext(file.Name())[1:]
    
  141. 			if ext != "actual" {
    
  142. 				continue
    
  143. 			}
    
  144. 
    
  145. 			base := strings.Split(strings.TrimSuffix(file.Name(), filepath.Ext(file.Name())), ".")[0]
    
  146. 			lexer := lexers.Get(base)
    
  147. 			assert.NotZero(t, lexer, base)
    
  148. 
    
  149. 			filename := filepath.Join("testdata", file.Name())
    
  150. 			expectedFilename := strings.TrimSuffix(filename, filepath.Ext(filename)) + ".expected"
    
  151. 
    
  152. 			lexer = chroma.Coalesce(lexer)
    
  153. 			FileTest(t, lexer, filename, expectedFilename)
    
  154. 		}
    
  155. 	}
    
  156. }
    
  157. 
    
  158. func FileTestAnalysis(t *testing.T, lexer chroma.Lexer, actualFilepath, expectedFilepath string) {
    
  159. 	t.Helper()
    
  160. 	t.Run(lexer.Config().Name+"/"+actualFilepath, func(t *testing.T) {
    
  161. 		expectedData, err := os.ReadFile(expectedFilepath)
    
  162. 		assert.NoError(t, err)
    
  163. 
    
  164. 		analyser, ok := lexer.(chroma.Analyser)
    
  165. 		assert.True(t, ok, "lexer %q does not set analyser", lexer.Config().Name)
    
  166. 
    
  167. 		data, err := os.ReadFile(actualFilepath)
    
  168. 		assert.NoError(t, err)
    
  169. 
    
  170. 		actual := analyser.AnalyseText(string(data))
    
  171. 		var actualData bytes.Buffer
    
  172. 		fmt.Fprintf(&actualData, "%s\n", strconv.FormatFloat(float64(actual), 'f', -1, 32))
    
  173. 
    
  174. 		if !bytes.Equal(expectedData, actualData.Bytes()) {
    
  175. 			if os.Getenv("RECORD") == "true" {
    
  176. 				f, err := os.Create(expectedFilepath)
    
  177. 				assert.NoError(t, err)
    
  178. 				_, err = f.Write(actualData.Bytes())
    
  179. 				assert.NoError(t, err)
    
  180. 				assert.NoError(t, f.Close())
    
  181. 			} else {
    
  182. 				assert.Equal(t, string(expectedData), actualData.String())
    
  183. 			}
    
  184. 		}
    
  185. 	})
    
  186. }
    
  187. 
    
  188. func TestLexersTextAnalyser(t *testing.T) {
    
  189. 	files, err := filepath.Glob("testdata/analysis/*.actual")
    
  190. 	assert.NoError(t, err)
    
  191. 
    
  192. 	for _, actualFilepath := range files {
    
  193. 		filename := filepath.Base(actualFilepath)
    
  194. 		baseFilename := strings.TrimSuffix(filename, filepath.Ext(filename))
    
  195. 		lexerName := strings.Split(baseFilename, ".")[0]
    
  196. 
    
  197. 		lexer := lexers.Get(lexerName)
    
  198. 		assert.NotZero(t, lexer, "no lexer found for name %q", lexerName)
    
  199. 
    
  200. 		expectedFilepath := "testdata/analysis/" + baseFilename + ".expected"
    
  201. 
    
  202. 		FileTestAnalysis(t, lexer, actualFilepath, expectedFilepath)
    
  203. 	}
    
  204. }