From 31566067c80c0026478ed009841772b940c05cfc Mon Sep 17 00:00:00 2001 From: Rui Azevedo Date: Thu, 1 Aug 2024 15:59:55 +0100 Subject: [PATCH 1/4] refactor, Extract the code that returns the files in a repository for a language into a helper, for reusability Part of #300 --- language/golang/language.go | 23 +---------------------- language/java/language.go | 21 +-------------------- language/language.go | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 42 deletions(-) diff --git a/language/golang/language.go b/language/golang/language.go index 450a51e5..e0358e51 100644 --- a/language/golang/language.go +++ b/language/golang/language.go @@ -2,14 +2,12 @@ package golang import ( "context" - "os" "path/filepath" "regexp" "strconv" "strings" pkgerrors "github.com/pkg/errors" - "github.com/zimmski/osutil" "github.com/symflower/eval-dev-quality/language" "github.com/symflower/eval-dev-quality/log" @@ -38,26 +36,7 @@ func (l *Language) Name() (id string) { // Files returns a list of relative file paths of the repository that should be evaluated. func (l *Language) Files(logger *log.Logger, repositoryPath string) (filePaths []string, err error) { - repositoryPath, err = filepath.Abs(repositoryPath) - if err != nil { - return nil, pkgerrors.WithStack(err) - } - - fs, err := osutil.FilesRecursive(repositoryPath) - if err != nil { - return nil, pkgerrors.WithStack(err) - } - - repositoryPath = repositoryPath + string(os.PathSeparator) - for _, f := range fs { - if !strings.HasSuffix(f, ".go") { - continue - } - - filePaths = append(filePaths, strings.TrimPrefix(f, repositoryPath)) - } - - return filePaths, nil + return language.Files(logger, l, repositoryPath) } // ImportPath returns the import path of the given source file. diff --git a/language/java/language.go b/language/java/language.go index 33b28419..8698a5e9 100644 --- a/language/java/language.go +++ b/language/java/language.go @@ -40,26 +40,7 @@ func (l *Language) Name() (id string) { // Files returns a list of relative file paths of the repository that should be evaluated. func (l *Language) Files(logger *log.Logger, repositoryPath string) (filePaths []string, err error) { - repositoryPath, err = filepath.Abs(repositoryPath) - if err != nil { - return nil, pkgerrors.WithStack(err) - } - - fs, err := osutil.FilesRecursive(repositoryPath) - if err != nil { - return nil, pkgerrors.WithStack(err) - } - - repositoryPath = repositoryPath + string(os.PathSeparator) - for _, f := range fs { - if !strings.HasSuffix(f, ".java") { - continue - } - - filePaths = append(filePaths, strings.TrimPrefix(f, repositoryPath)) - } - - return filePaths, nil + return language.Files(logger, l, repositoryPath) } // ImportPath returns the import path of the given source file. diff --git a/language/language.go b/language/language.go index 6ca51505..26feef88 100644 --- a/language/language.go +++ b/language/language.go @@ -5,9 +5,11 @@ import ( "os" "path/filepath" "sort" + "strings" "time" pkgerrors "github.com/pkg/errors" + "github.com/zimmski/osutil" "github.com/symflower/eval-dev-quality/log" ) @@ -81,6 +83,30 @@ func RepositoriesForLanguage(language Language, testdataPath string) (relativeRe return relativeRepositoryPaths, nil } +// Files returns a list of relative file paths of the repository that should be evaluated. +func Files(logger *log.Logger, language Language, repositoryPath string) (filePaths []string, err error) { + repositoryPath, err = filepath.Abs(repositoryPath) + if err != nil { + return nil, pkgerrors.WithStack(err) + } + + fs, err := osutil.FilesRecursive(repositoryPath) + if err != nil { + return nil, pkgerrors.WithStack(err) + } + + repositoryPath = repositoryPath + string(os.PathSeparator) + for _, f := range fs { + if !strings.HasSuffix(f, language.DefaultFileExtension()) { + continue + } + + filePaths = append(filePaths, strings.TrimPrefix(f, repositoryPath)) + } + + return filePaths, nil +} + // TestResult holds the result of running tests. type TestResult struct { TestsTotal uint From af333f15045dfc651c97ed28aa612ca22f73b662 Mon Sep 17 00:00:00 2001 From: Rui Azevedo Date: Thu, 1 Aug 2024 16:22:17 +0100 Subject: [PATCH 2/4] refactor, Use the default language file extension and test file suffix functions, for consistency Part of #300 --- language/golang/language.go | 2 +- language/java/language.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/language/golang/language.go b/language/golang/language.go index e0358e51..9c6a6bb6 100644 --- a/language/golang/language.go +++ b/language/golang/language.go @@ -46,7 +46,7 @@ func (l *Language) ImportPath(projectRootPath string, filePath string) (importPa // TestFilePath returns the file path of a test file given the corresponding file path of the test's source file. func (l *Language) TestFilePath(projectRootPath string, filePath string) (testFilePath string) { - return strings.TrimSuffix(filePath, ".go") + "_test.go" + return strings.TrimSuffix(filePath, l.DefaultFileExtension()) + l.DefaultTestFileSuffix() } // TestFramework returns the human-readable name of the test framework that should be used. diff --git a/language/java/language.go b/language/java/language.go index 8698a5e9..8f27c782 100644 --- a/language/java/language.go +++ b/language/java/language.go @@ -62,7 +62,7 @@ func (l *Language) TestFilePath(projectRootPath string, filePath string) (testFi filePath = filePath[:l] + t + filePath[l+len(t):] } - return strings.TrimSuffix(filePath, ".java") + "Test.java" + return strings.TrimSuffix(filePath, l.DefaultFileExtension()) + l.DefaultTestFileSuffix() } // TestFramework returns the human-readable name of the test framework that should be used. From 4684a6f9542f8b0b03ea9f97e09170ed292a4144 Mon Sep 17 00:00:00 2001 From: Rui Azevedo Date: Fri, 2 Aug 2024 10:09:21 +0100 Subject: [PATCH 3/4] Plain repository for Ruby Part of #300 --- testdata/ruby/plain/lib/plain.rb | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 testdata/ruby/plain/lib/plain.rb diff --git a/testdata/ruby/plain/lib/plain.rb b/testdata/ruby/plain/lib/plain.rb new file mode 100644 index 00000000..4a018e2c --- /dev/null +++ b/testdata/ruby/plain/lib/plain.rb @@ -0,0 +1,2 @@ +def plain +end From c7ae56a472c05fa4b9f875631fb927065f0118ca Mon Sep 17 00:00:00 2001 From: Rui Azevedo Date: Fri, 2 Aug 2024 08:46:24 +0100 Subject: [PATCH 4/4] Introduce the Ruby language Part of #300 --- language/ruby/language.go | 74 ++++++++++++++++++++++++++++++++++ language/ruby/language_test.go | 64 +++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 language/ruby/language.go create mode 100644 language/ruby/language_test.go diff --git a/language/ruby/language.go b/language/ruby/language.go new file mode 100644 index 00000000..b4fb12f8 --- /dev/null +++ b/language/ruby/language.go @@ -0,0 +1,74 @@ +package ruby + +import ( + "path/filepath" + "strings" + + "github.com/symflower/eval-dev-quality/language" + "github.com/symflower/eval-dev-quality/log" +) + +// Language holds a Ruby language to evaluate a repository. +type Language struct{} + +func init() { + language.Register(&Language{}) +} + +var _ language.Language = (*Language)(nil) + +// ID returns the unique ID of this language. +func (l *Language) ID() (id string) { + return "ruby" +} + +// Name is the prose name of this language. +func (l *Language) Name() (id string) { + return "Ruby" +} + +// Files returns a list of relative file paths of the repository that should be evaluated. +func (l *Language) Files(logger *log.Logger, repositoryPath string) (filePaths []string, err error) { + return language.Files(logger, l, repositoryPath) +} + +// ImportPath returns the import path of the given source file. +func (l *Language) ImportPath(projectRootPath string, filePath string) (importPath string) { + return "../lib/" + strings.TrimSuffix(filepath.Base(filePath), l.DefaultFileExtension()) +} + +// TestFilePath returns the file path of a test file given the corresponding file path of the test's source file. +func (l *Language) TestFilePath(projectRootPath string, filePath string) (testFilePath string) { + filePath = strings.ReplaceAll(filePath, "lib", "test") + + return strings.TrimSuffix(filePath, l.DefaultFileExtension()) + l.DefaultTestFileSuffix() +} + +// TestFramework returns the human-readable name of the test framework that should be used. +func (l *Language) TestFramework() (testFramework string) { + return "Minitest" +} + +// DefaultFileExtension returns the default file extension. +func (l *Language) DefaultFileExtension() string { + return ".rb" +} + +// DefaultTestFileSuffix returns the default test file suffix. +func (l *Language) DefaultTestFileSuffix() string { + return "_test.rb" +} + +// ExecuteTests invokes the language specific testing on the given repository. +func (l *Language) ExecuteTests(logger *log.Logger, repositoryPath string) (testResult *language.TestResult, problems []error, err error) { + logger.Panic("not implemented") + + return testResult, problems, nil +} + +// Mistakes builds a Ruby repository and returns the list of mistakes found. +func (l *Language) Mistakes(logger *log.Logger, repositoryPath string) (mistakes []string, err error) { + logger.Panic("not implemented") + + return nil, nil +} diff --git a/language/ruby/language_test.go b/language/ruby/language_test.go new file mode 100644 index 00000000..9c7375b7 --- /dev/null +++ b/language/ruby/language_test.go @@ -0,0 +1,64 @@ +package ruby + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLanguageTestFilePath(t *testing.T) { + type testCase struct { + Name string + + ProjectRootPath string + FilePath string + + ExpectedTestFilePath string + } + + validate := func(t *testing.T, tc *testCase) { + t.Run(tc.Name, func(t *testing.T) { + ruby := Language{} + actualTestFilePath := ruby.TestFilePath(tc.ProjectRootPath, tc.FilePath) + + assert.Equal(t, tc.ExpectedTestFilePath, actualTestFilePath) + }) + } + + validate(t, &testCase{ + Name: "Source file", + + FilePath: filepath.Join("testdata", "ruby", "plain", "lib", "plain.rb"), + + ExpectedTestFilePath: filepath.Join("testdata", "ruby", "plain", "test", "plain_test.rb"), + }) +} + +func TestLanguageImportPath(t *testing.T) { + type testCase struct { + Name string + + ProjectRootPath string + FilePath string + + ExpectedImportPath string + } + + validate := func(t *testing.T, tc *testCase) { + t.Run(tc.Name, func(t *testing.T) { + ruby := Language{} + actualImportPath := ruby.ImportPath(tc.ProjectRootPath, tc.FilePath) + + assert.Equal(t, tc.ExpectedImportPath, actualImportPath) + }) + } + + validate(t, &testCase{ + Name: "Source file", + + FilePath: filepath.Join("testdata", "ruby", "plain", "lib", "plain.rb"), + + ExpectedImportPath: "../lib/plain", + }) +}