github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/logictest/main/main.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "bytes" 19 "encoding/csv" 20 "encoding/json" 21 "flag" 22 "fmt" 23 "os" 24 25 "github.com/dolthub/sqllogictest/go/logictest" 26 27 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/logictest/dolt" 28 ) 29 30 var resultFormat = flag.String("r", "json", "format of parsed results") 31 32 // Runs all sqllogictest test files (or directories containing them) given as arguments. 33 // Usage: $command (run|parse) [version] [file1.test dir1/ dir2/] 34 // In run mode, runs the tests and prints results to stdout. 35 // In parse mode, parses test results from the file given and prints them to STDOUT in a format to be imported by dolt. 36 func main() { 37 flag.Parse() 38 args := flag.Args() 39 40 if len(args) < 1 { 41 panic("Usage: logictest (run|parse) [version] file1 file2 ...") 42 } 43 44 if args[0] == "run" { 45 h := &dolt.DoltHarness{} 46 logictest.RunTestFiles(h, args[1:]...) 47 } else if args[0] == "parse" { 48 if len(args) < 3 { 49 panic("Usage: logictest [-r(csv|json)] parse <version> (file | dir/)") 50 } 51 parseTestResults(args[1], args[2]) 52 } else { 53 panic("Unrecognized command " + args[0]) 54 } 55 } 56 57 func parseTestResults(version, f string) { 58 entries, err := logictest.ParseResultFile(f) 59 if err != nil { 60 panic(err) 61 } 62 63 records := make([]*DoltResultRecord, len(entries)) 64 for i, e := range entries { 65 records[i] = NewDoltRecordResult(e, version) 66 } 67 68 if *resultFormat == "csv" { 69 err := writeResultsCsv(records) 70 if err != nil { 71 panic(err) 72 } 73 } else { 74 b, err := JSONMarshal(records) 75 if err != nil { 76 panic(err) 77 } 78 79 _, err = os.Stdout.Write(b) 80 if err != nil { 81 panic(err) 82 } 83 } 84 } 85 86 // Custom json marshalling function is necessary to avoid escaping <, > and & to html unicode escapes 87 func JSONMarshal(records []*DoltResultRecord) ([]byte, error) { 88 rows := &TestResultArray{Rows: records} 89 buffer := &bytes.Buffer{} 90 encoder := json.NewEncoder(buffer) 91 encoder.SetEscapeHTML(false) 92 err := encoder.Encode(rows) 93 return buffer.Bytes(), err 94 } 95 96 func NewDoltRecordResult(e *logictest.ResultLogEntry, version string) *DoltResultRecord { 97 var result string 98 switch e.Result { 99 case logictest.Ok: 100 result = "ok" 101 case logictest.NotOk: 102 result = "not ok" 103 case logictest.Skipped: 104 result = "skipped" 105 case logictest.Timeout: 106 result = "timeout" 107 case logictest.DidNotRun: 108 result = "did not run" 109 } 110 return &DoltResultRecord{ 111 Version: version, 112 TestFile: e.TestFile, 113 LineNum: e.LineNum, 114 Query: e.Query, 115 Duration: e.Duration.Milliseconds(), 116 Result: result, 117 ErrorMessage: e.ErrorMessage, 118 } 119 } 120 121 type TestResultArray struct { 122 Rows []*DoltResultRecord `json:"rows"` 123 } 124 125 type DoltResultRecord struct { 126 Version string `json:"version"` 127 TestFile string `json:"test_file"` 128 LineNum int `json:"line_num"` 129 Query string `json:"query_string"` 130 Duration int64 `json:"duration"` 131 Result string `json:"result"` 132 ErrorMessage string `json:"error_message,omitempty"` 133 } 134 135 // fromResultCsvHeaders returns supported csv headers for a Result 136 func fromResultCsvHeaders() []string { 137 return []string{ 138 "version", 139 "test_file", 140 "line_num", 141 "query_string", 142 "duration", 143 "result", 144 "error_message", 145 } 146 } 147 148 // fromHeaderColumnValue returns the value from the DoltResultRecord for the given 149 // header field 150 func fromHeaderColumnValue(h string, r *DoltResultRecord) (string, error) { 151 var val string 152 switch h { 153 case "version": 154 val = r.Version 155 case "test_file": 156 val = r.TestFile 157 case "line_num": 158 val = fmt.Sprintf("%d", r.LineNum) 159 case "query_string": 160 val = r.Query 161 case "duration": 162 val = fmt.Sprintf("%d", r.Duration) 163 case "result": 164 val = r.Result 165 case "error_message": 166 val = r.ErrorMessage 167 default: 168 return "", fmt.Errorf("unsupported header field") 169 } 170 return val, nil 171 } 172 173 // writeResultsCsv writes []*DoltResultRecord to stdout in csv format 174 func writeResultsCsv(results []*DoltResultRecord) (err error) { 175 csvWriter := csv.NewWriter(os.Stdout) 176 177 // write header 178 headers := fromResultCsvHeaders() 179 if err := csvWriter.Write(headers); err != nil { 180 return err 181 } 182 183 // write rows 184 for _, r := range results { 185 row := make([]string, 0) 186 for _, field := range headers { 187 val, err := fromHeaderColumnValue(field, r) 188 if err != nil { 189 return err 190 } 191 row = append(row, val) 192 } 193 err = csvWriter.Write(row) 194 if err != nil { 195 return err 196 } 197 } 198 199 csvWriter.Flush() 200 if err := csvWriter.Error(); err != nil { 201 return err 202 } 203 return 204 }