testing/common/goldens.go (135 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package common import ( "bufio" "os" "path/filepath" "strings" "testing" ) type GoldenTestCase struct { Name string Input string GSQLWant string PSQLWant string } type parseStatus int const ( parsingInput parseStatus = iota parsingGSQL parsingPSQL ) const ( goldenTestCaseNamePrefix = "--" goldenGoogleSQLExpectation = "-- GoogleSQL" goldenPostgreSQLExpectation = "-- PostgreSQL" goldenTestCaseEndOfTest = "==" ) func GoldenTestCasesFrom(t testing.TB, dir string) []GoldenTestCase { t.Helper() var tests []GoldenTestCase entries, err := os.ReadDir(dir) if err != nil { t.Fatalf("error when reading golden tests from dir %s: %s", dir, err) return nil } for _, entry := range entries { if !entry.IsDir() { path := filepath.Join(dir, entry.Name()) ts := goldenTestCasesFromFile(t, path) if err != nil { return nil } tests = append(tests, ts...) } } return tests } func goldenTestCasesFromFile(t testing.TB, filePath string) []GoldenTestCase { t.Helper() file, err := os.Open(filePath) dirName := filepath.Base(filepath.Dir(filePath)) fileName := filepath.Base(filePath) if err != nil { t.Fatalf("error when reading golden tests from path %s: %s", filePath, err) return nil } defer file.Close() var testCases []GoldenTestCase var testName, inputSchema, GSQLWant, PSQLWant strings.Builder parsingStatus := parsingInput lineNum := 0 scanner := bufio.NewScanner(file) for scanner.Scan() { lineNum++ line := scanner.Text() // First line should be the test case name if testName.Len() == 0 { testName.WriteString(dirName) testName.WriteString("/") testName.WriteString(fileName) testName.WriteString("/") testName.WriteString(strings.Trim(strings.ReplaceAll(line, goldenTestCaseNamePrefix, ""), "")) continue } // Beginning of GoogleSQL expectation if line == goldenGoogleSQLExpectation { if GSQLWant.Len() != 0 { t.Fatal("bad format: Duplicated GoogleSQL definition in test case") return nil } parsingStatus = parsingGSQL continue } // Beginning of PostgreSQL expectation if line == goldenPostgreSQLExpectation { if PSQLWant.Len() != 0 { t.Fatal("bad format: Duplicated PostgreSQL definition in test case") return nil } parsingStatus = parsingPSQL continue } // End of a test case if line == goldenTestCaseEndOfTest { if inputSchema.Len() == 0 { t.Fatalf("bad format: Invalid test case at line %d, missing source schema", lineNum) return nil } if GSQLWant.Len() == 0 { t.Fatalf("bad format: Invalid test case at line %d, missing expected GoogleSQL schema", lineNum) return nil } if PSQLWant.Len() == 0 { t.Fatalf("bad format: Invalid test case at line %d, missing expected PostgreSQL schema", lineNum) return nil } testCases = append(testCases, GoldenTestCase{ Name: testName.String(), Input: inputSchema.String(), GSQLWant: strings.TrimRight(GSQLWant.String(), "\n"), PSQLWant: strings.TrimRight(PSQLWant.String(), "\n")}) parsingStatus = parsingInput testName.Reset() inputSchema.Reset() GSQLWant.Reset() PSQLWant.Reset() continue } // Test body switch parsingStatus { case parsingInput: inputSchema.WriteString(line) inputSchema.WriteString("\n") case parsingGSQL: GSQLWant.WriteString(line) GSQLWant.WriteString("\n") case parsingPSQL: PSQLWant.WriteString(line) PSQLWant.WriteString("\n") } } // Test case not finished if parsingStatus != parsingInput { t.Fatalf("bad format: Invalid test case at line %d", lineNum) return nil } if err := scanner.Err(); err != nil { t.Fatalf("bad format: Error when scanning golden test file %s: %s", filePath, err) return nil } return testCases }