From 45a79a7748bc5f59907e0080b0bc2520c1bc81d1 Mon Sep 17 00:00:00 2001 From: Rui Azevedo Date: Wed, 31 Jul 2024 19:13:57 +0100 Subject: [PATCH] Check if the repository for the transpile task is valid before running the evaluation, so it is checked just once Closes #263 --- evaluate/task/repository.go | 2 + evaluate/task/task-transpile.go | 47 +++++++- evaluate/task/task-transpile_test.go | 173 +++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 5 deletions(-) diff --git a/evaluate/task/repository.go b/evaluate/task/repository.go index 45a9f8ac3..b996672cb 100644 --- a/evaluate/task/repository.go +++ b/evaluate/task/repository.go @@ -114,6 +114,8 @@ func (r *Repository) Validate(logger *log.Logger, language language.Language) (e switch taskIdentifier { case IdentifierCodeRepair: return validateCodeRepairRepository(logger, r.DataPath(), language) + case IdentifierTranspile: + return validateTranspileRepository(logger, r.DataPath(), language) case IdentifierWriteTests: return validateWriteTestsRepository(logger, r.DataPath(), language) } diff --git a/evaluate/task/task-transpile.go b/evaluate/task/task-transpile.go index 52a982578..723df55fd 100644 --- a/evaluate/task/task-transpile.go +++ b/evaluate/task/task-transpile.go @@ -173,14 +173,10 @@ func (t *TaskTranspile) Run(ctx evaltask.Context) (repositoryAssessment map[eval // unpackTranspilerPackage checks if the testdata repository for the transpilation task is well-formed and returns the path to the implementation file and also the path to the file that holds the stub. func (t *TaskTranspile) unpackTranspilerPackage(ctx evaltask.Context, fileLogger *log.Logger, originLanguage language.Language, packagePath string) (originFilePath string, stubFilePath string, err error) { packagePathAbsolute := filepath.Join(ctx.Repository.DataPath(), packagePath) - // Check if the package path has a directory called "implementation" with a source file in the language to transpile from. + files, err := originLanguage.Files(fileLogger, filepath.Join(packagePathAbsolute, "implementation")) if err != nil { return "", "", pkgerrors.WithStack(err) - } else if len(files) != 1 { - return "", "", pkgerrors.Errorf("package %q in repository %q must have an \"implementation\" directory with just one %s source file to transpile", packagePath, ctx.Repository.Name(), originLanguage.Name()) - } else if strings.HasSuffix(files[0], originLanguage.DefaultTestFileSuffix()) { - return "", "", pkgerrors.Errorf("package %q in repository %q must have an \"implementation\" directory with only a %s source file, but found a test file %q", packagePath, ctx.Repository.Name(), originLanguage.Name(), originFilePath) } originFilePath = filepath.Join("implementation", files[0]) @@ -191,3 +187,44 @@ func (t *TaskTranspile) unpackTranspilerPackage(ctx evaltask.Context, fileLogger return originFilePath, stubFilePath, nil } + +// validateTranspileRepository checks if the repository for the "transpile" task is well-formed. +func validateTranspileRepository(logger *log.Logger, repositoryPath string, destinationLanguage language.Language) (err error) { + logger.Printf("validating repository %q", repositoryPath) + + var originLanguage language.Language + if _, ok := destinationLanguage.(*golang.Language); ok { + originLanguage = &java.Language{} + } else { + originLanguage = &golang.Language{} + } + + packagePaths, err := repositoryOnlyHasPackages(repositoryPath) + if err != nil { + return err + } + + for _, packagePath := range packagePaths { + // Validate the implementation folder. + files, err := originLanguage.Files(logger, filepath.Join(packagePath, "implementation")) + if err != nil { + return pkgerrors.WithStack(err) + } else if len(files) != 1 { + return pkgerrors.Errorf("package %q must have an \"implementation\" directory with just one %s source file to transpile", packagePath, originLanguage.Name()) + } else if strings.HasSuffix(files[0], originLanguage.DefaultTestFileSuffix()) { + return pkgerrors.Errorf("package %q must have an \"implementation\" directory with only a %s source file, but found a test file %q", packagePath, originLanguage.Name(), files[0]) + } + + // Check if the package as one source file and one test file in the language we want to transpile to. + sourceFiles, testFiles, err := packagesSourceAndTestFiles(logger, packagePath, destinationLanguage) + if err != nil { + return err + } else if len(sourceFiles) != 1 { + return pkgerrors.Errorf("package %q must contain exactly one %s source file, but found %+v", packagePath, destinationLanguage.Name(), sourceFiles) + } else if len(testFiles) != 1 { + return pkgerrors.Errorf("package %q must contain exactly one %s test file, but found %+v", packagePath, destinationLanguage.Name(), testFiles) + } + } + + return nil +} diff --git a/evaluate/task/task-transpile_test.go b/evaluate/task/task-transpile_test.go index 5cdd9ec95..50e66bdc5 100644 --- a/evaluate/task/task-transpile_test.go +++ b/evaluate/task/task-transpile_test.go @@ -1,6 +1,7 @@ package task import ( + "os" "path/filepath" "testing" @@ -349,3 +350,175 @@ func TestTaskTranspileRun(t *testing.T) { } }) } + +func TestValidateTranspileRepository(t *testing.T) { + validate := func(t *testing.T, tc *tasktesting.TestCaseValidateRepository) { + tc.Validate(t, validateTranspileRepository) + } + + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Package does not contain implementation folder", + + Before: func(repositoryPath string) { + require.NoError(t, os.MkdirAll(filepath.Join(repositoryPath, "somePackage"), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("golang", "transpile"), + Language: &golang.Language{}, + + ExpectedErrorContains: "no such file or directory", + }) + t.Run("Go", func(t *testing.T) { + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Implementation folder contains multiple files", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "ClassA.java"), []byte(`content`), 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "ClassB.java"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("golang", "transpile"), + Language: &golang.Language{}, + + ExpectedErrorContains: "must have an \"implementation\" directory with just one Java source file to transpile", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Implementation folder contains a test file", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "ClassTest.java"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("golang", "transpile"), + Language: &golang.Language{}, + + ExpectedErrorContains: "must have an \"implementation\" directory with only a Java source file, but found a test file", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Package without source file", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "Class.java"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("golang", "transpile"), + Language: &golang.Language{}, + + ExpectedErrorContains: " must contain exactly one Go source file", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Package without test file", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "Class.java"), []byte(`content`), 0700)) + require.NoError(t, os.WriteFile(filepath.Join(repositoryPath, "somePackage", "file.go"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("golang", "transpile"), + Language: &golang.Language{}, + + ExpectedErrorContains: " must contain exactly one Go test file", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Well-formed", + + Before: func(repositoryPath string) { + require.NoError(t, osutil.MkdirAll(filepath.Join(repositoryPath, ".git"))) + require.NoError(t, os.WriteFile(filepath.Join(repositoryPath, ".git", "index"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("golang", "transpile"), + Language: &golang.Language{}, + }) + }) + t.Run("Java", func(t *testing.T) { + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Implementation folder contains multiple files", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "fileA.go"), []byte(`content`), 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "fileB.go"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("java", "transpile"), + Language: &java.Language{}, + + ExpectedErrorContains: "must have an \"implementation\" directory with just one Go source file to transpile", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Implementation folder contains a test file", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "file_test.go"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("java", "transpile"), + Language: &java.Language{}, + + ExpectedErrorContains: "must have an \"implementation\" directory with only a Go source file, but found a test file", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Package without source file", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "file.go"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("java", "transpile"), + Language: &java.Language{}, + + ExpectedErrorContains: " must contain exactly one Java source file", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Package without test file", + + Before: func(repositoryPath string) { + implementationPath := filepath.Join(repositoryPath, "somePackage", "implementation") + require.NoError(t, os.MkdirAll(implementationPath, 0700)) + require.NoError(t, os.WriteFile(filepath.Join(implementationPath, "file.go"), []byte(`content`), 0700)) + require.NoError(t, os.WriteFile(filepath.Join(repositoryPath, "somePackage", "Class.java"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("java", "transpile"), + Language: &java.Language{}, + + ExpectedErrorContains: " must contain exactly one Java test file", + }) + validate(t, &tasktesting.TestCaseValidateRepository{ + Name: "Well-formed", + + Before: func(repositoryPath string) { + require.NoError(t, osutil.MkdirAll(filepath.Join(repositoryPath, ".git"))) + require.NoError(t, os.WriteFile(filepath.Join(repositoryPath, ".git", "index"), []byte(`content`), 0700)) + }, + + TestdataPath: filepath.Join("..", "..", "testdata"), + RepositoryPath: filepath.Join("java", "transpile"), + Language: &java.Language{}, + }) + }) +}