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  }