2025-10-15 10:12:44 +03:00

146 lines
3.5 KiB
Go

package parser
import (
"io/fs"
"iter"
"os"
"path/filepath"
"testing"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/fixtures"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs"
"gotest.tools/v3/assert"
)
func BenchmarkParse(b *testing.B) {
jsdocModes := []struct {
name string
mode ast.JSDocParsingMode
}{
{"tsc", ast.JSDocParsingModeParseForTypeErrors},
{"server", ast.JSDocParsingModeParseAll},
}
for _, f := range fixtures.BenchFixtures {
b.Run(f.Name(), func(b *testing.B) {
f.SkipIfNotExist(b)
fileName := tspath.GetNormalizedAbsolutePath(f.Path(), "/")
path := tspath.ToPath(fileName, "/", osvfs.FS().UseCaseSensitiveFileNames())
sourceText := f.ReadFile(b)
scriptKind := core.GetScriptKindFromFileName(fileName)
for _, jsdoc := range jsdocModes {
b.Run(jsdoc.name, func(b *testing.B) {
jsdocMode := jsdoc.mode
opts := ast.SourceFileParseOptions{
FileName: fileName,
Path: path,
JSDocParsingMode: jsdocMode,
}
for b.Loop() {
ParseSourceFile(opts, sourceText, scriptKind)
}
})
}
})
}
}
type parsableFile struct {
path string
name string
}
func allParsableFiles(tb testing.TB, root string) iter.Seq[parsableFile] {
tb.Helper()
return func(yield func(parsableFile) bool) {
tb.Helper()
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || tspath.TryGetExtensionFromPath(path) == "" {
return nil
}
testName, err := filepath.Rel(root, path)
if err != nil {
return err
}
testName = filepath.ToSlash(testName)
if !yield(parsableFile{path, testName}) {
return filepath.SkipAll
}
return nil
})
assert.NilError(tb, err)
}
}
func FuzzParser(f *testing.F) {
repo.SkipIfNoTypeScriptSubmodule(f)
tests := []string{
"src",
"scripts",
"Herebyfile.mjs",
// "tests/cases",
}
var extensions collections.Set[string]
for _, es := range tspath.AllSupportedExtensionsWithJson {
for _, e := range es {
extensions.Add(e)
}
}
for _, test := range tests {
root := filepath.Join(repo.TypeScriptSubmodulePath, test)
for file := range allParsableFiles(f, root) {
sourceText, err := os.ReadFile(file.path)
assert.NilError(f, err)
extension := tspath.TryGetExtensionFromPath(file.path)
f.Add(extension, string(sourceText), int(core.ScriptTargetESNext), int(ast.JSDocParsingModeParseAll))
}
}
f.Fuzz(func(t *testing.T, extension string, sourceText string, scriptTarget_ int, jsdocParsingMode_ int) {
scriptTarget := core.ScriptTarget(scriptTarget_)
jsdocParsingMode := ast.JSDocParsingMode(jsdocParsingMode_)
if !extensions.Has(extension) {
t.Skip()
}
if scriptTarget < core.ScriptTargetNone || scriptTarget > core.ScriptTargetLatest {
t.Skip()
}
if jsdocParsingMode < ast.JSDocParsingModeParseAll || jsdocParsingMode > ast.JSDocParsingModeParseNone {
t.Skip()
}
fileName := "/index" + extension
path := tspath.Path(fileName)
opts := ast.SourceFileParseOptions{
FileName: fileName,
Path: path,
JSDocParsingMode: jsdocParsingMode,
}
ParseSourceFile(opts, sourceText, core.GetScriptKindFromFileName(fileName))
})
}