diff --git a/glide.lock b/glide.lock index 679ff7b92fe7..6fb8337f5183 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 43bf5b9efcb1e998f16311fea749ec295c39564482781cdccb5724e57ecb5f24 -updated: 2018-04-20T09:53:00.832005531Z +hash: b0f00760777f6e6400e35e1b99f7cbc0e06762709cbb0f3f3f2c19a8f7b0f528 +updated: 2018-04-30T14:14:24.062881352Z imports: - name: cloud.google.com/go version: 3b1ae45394a234c385be014e9a488f2bb6eef821 @@ -34,7 +34,7 @@ imports: subpackages: - log - name: github.com/emicklei/go-restful-openapi - version: 60f3c22579aa3b998e4c6e902649760531ca03ca + version: 7712e8a6e8096e7103b96a400519d38fb1e348f8 - name: github.com/emicklei/go-restful-swagger12 version: 7524189396c68dc4b04d53852f9edc00f816b123 - name: github.com/evanphx/json-patch @@ -73,7 +73,7 @@ imports: subpackages: - lru - name: github.com/golang/mock - version: 8b2eeeb0ca5f56c78bec5efde9c4a21d9201126c + version: 87f106fccd7e3ced294a19e86bebd7846911690a subpackages: - gomock - name: github.com/golang/protobuf @@ -135,10 +135,6 @@ imports: version: 4abfceffa76a675ea43d133e49aad64c821d32bf subpackages: - conn -- name: github.com/kubevirt/qe-tools - version: 5891aede49ba383e4e53320ff46e6999dffde8c4 - subpackages: - - ginkgo-reporters - name: github.com/libvirt/libvirt-go version: 0822ea6d658d7a0deeaa2ce63ed3efd6306bee03 - name: github.com/mailru/easyjson @@ -268,7 +264,7 @@ imports: - unicode/norm - width - name: google.golang.org/appengine - version: 0a24098c0ec68416ec050f567f75df563d6b231e + version: 962cbd1200af94a5a35ba8d512e9f91271b4d01a subpackages: - internal - internal/app_identity @@ -286,12 +282,13 @@ imports: - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/ini.v1 - version: ace140f73450505f33e8b8418216792275ae82a7 + version: 6529cf7c58879c08d927016dde4477f18a0634cb - name: gopkg.in/yaml.v2 version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 - name: k8s.io/api version: acf347b865f29325eb61f4cd2df11e86e073a5ee subpackages: + - admission/v1beta1 - admissionregistration/v1alpha1 - admissionregistration/v1beta1 - apps/v1 @@ -490,6 +487,12 @@ imports: subpackages: - pkg/common - pkg/util/proto +- name: kubevirt.io/qe-tools + version: 13fc8573324cd2af42c88043b5a7c6e03bf39f95 + repo: https://github.com/kubevirt/qe-tools.git + subpackages: + - pkg/ginkgo-reporters + - pkg/polarion-xml testImports: - name: github.com/elazarl/goproxy version: 07b16b6e30fcac0ad8c0435548e743bcf2ca7e92 diff --git a/glide.yaml b/glide.yaml index 7c6f278e1421..784b28df8f4b 100644 --- a/glide.yaml +++ b/glide.yaml @@ -56,8 +56,9 @@ import: version: kubernetes-1.9.7 - package: github.com/ant31/crd-validation version: eabcf70a1bd73e9296fa0c5f57de604689200a1b -- package: github.com/kubevirt/qe-tools - version: v0.1.0 +- package: kubevirt.io/qe-tools + version: v0.1.1 + repo: https://github.com/kubevirt/qe-tools.git testImport: - package: github.com/elazarl/goproxy version: 07b16b6e30fcac0ad8c0435548e743bcf2ca7e92 diff --git a/tests/tests_suite_test.go b/tests/tests_suite_test.go index a2125683a433..2d92f7226dad 100644 --- a/tests/tests_suite_test.go +++ b/tests/tests_suite_test.go @@ -25,7 +25,7 @@ import ( "testing" - "github.com/kubevirt/qe-tools/ginkgo-reporters" + "kubevirt.io/qe-tools/pkg/ginkgo-reporters" "kubevirt.io/kubevirt/tests" ) diff --git a/vendor/github.com/emicklei/go-restful-openapi/README.md b/vendor/github.com/emicklei/go-restful-openapi/README.md index 0d4f3f0eb534..2e3a7345356d 100644 --- a/vendor/github.com/emicklei/go-restful-openapi/README.md +++ b/vendor/github.com/emicklei/go-restful-openapi/README.md @@ -14,6 +14,7 @@ - modelDescription - type (overrides the Go type String()) - enum +- readOnly See TestThatExtraTagsAreReadIntoModel for examples. @@ -22,4 +23,4 @@ See TestThatExtraTagsAreReadIntoModel for examples. - [go-restful](https://github.com/emicklei/go-restful) - [go-openapi](https://github.com/go-openapi/spec) -© 2017, ernestmicklei.com. MIT License. Contributions welcome. \ No newline at end of file +© 2017, ernestmicklei.com. MIT License. Contributions welcome. diff --git a/vendor/github.com/emicklei/go-restful-openapi/property_ext.go b/vendor/github.com/emicklei/go-restful-openapi/property_ext.go index 5aca3b65f122..eb91208bc57c 100644 --- a/vendor/github.com/emicklei/go-restful-openapi/property_ext.go +++ b/vendor/github.com/emicklei/go-restful-openapi/property_ext.go @@ -82,6 +82,16 @@ func setUniqueItems(prop *spec.Schema, field reflect.StructField) { } } +func setReadOnly(prop *spec.Schema, field reflect.StructField) { + tag := field.Tag.Get("readOnly") + switch tag { + case "true": + prop.ReadOnly = true + case "false": + prop.ReadOnly = false + } +} + func setPropertyMetadata(prop *spec.Schema, field reflect.StructField) { setDescription(prop, field) setDefaultValue(prop, field) @@ -90,4 +100,5 @@ func setPropertyMetadata(prop *spec.Schema, field reflect.StructField) { setMaximum(prop, field) setUniqueItems(prop, field) setType(prop, field) + setReadOnly(prop, field) } diff --git a/vendor/github.com/golang/mock/README.md b/vendor/github.com/golang/mock/README.md index e4387779065a..6a3c50b8770f 100644 --- a/vendor/github.com/golang/mock/README.md +++ b/vendor/github.com/golang/mock/README.md @@ -13,7 +13,7 @@ Once you have [installed Go][golang-install], run these commands to install the `gomock` package and the `mockgen` tool: go get github.com/golang/mock/gomock - go get github.com/golang/mock/mockgen + go install github.com/golang/mock/mockgen Documentation diff --git a/vendor/github.com/kubevirt/qe-tools/Makefile b/vendor/github.com/kubevirt/qe-tools/Makefile deleted file mode 100644 index c07ce8478e19..000000000000 --- a/vendor/github.com/kubevirt/qe-tools/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -deps-update: - glide cc && glide update --strip-vendor - hack/dep-prune.sh - -.PHONY: deps-update diff --git a/vendor/github.com/kubevirt/qe-tools/ginkgo-reporters/polarion_reporter.go b/vendor/github.com/kubevirt/qe-tools/ginkgo-reporters/polarion_reporter.go deleted file mode 100644 index a53ff82950a1..000000000000 --- a/vendor/github.com/kubevirt/qe-tools/ginkgo-reporters/polarion_reporter.go +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This file is part of the KubeVirt project - * - * 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. - * - * Copyright 2018 Red Hat, Inc. - * - */ - -package ginkgo_reporters - -import ( - "encoding/xml" - "flag" - "fmt" - "os" - "strings" - - "github.com/onsi/ginkgo/config" - "github.com/onsi/ginkgo/types" -) - -var Polarion = PolarionReporter{} - -func init() { - flag.BoolVar(&Polarion.Run, "polarion", false, "Run Polarion reporter") - flag.StringVar(&Polarion.projectId, "polarion-project-id", "", "Set the Polarion project ID") - flag.StringVar(&Polarion.filename, "polarion-report-file", "polarion.xml", "Set the Polarion report file path") -} - -type PolarionTestCases struct { - XMLName xml.Name `xml:"testcases"` - TestCases []PolarionTestCase `xml:"testcase"` - ProjectID string `xml:"project-id,attr"` -} - -type PolarionTestCase struct { - Title Title `xml:"title"` - Description Description `xml:"description"` - TestCaseCustomFields TestCaseCustomFields `xml:"custom-fields"` -} - -type Title struct { - Content string `xml:",chardata"` -} - -type Description struct { - Content string `xml:",chardata"` -} - -type TestCaseCustomFields struct { - CustomFields []TestCaseCustomField `xml:"custom-field"` -} - -type TestCaseCustomField struct { - Content string `xml:"content,attr"` - ID string `xml:"id,attr"` -} - -type PolarionReporter struct { - suite PolarionTestCases - Run bool - filename string - projectId string - testSuiteName string -} - -func (reporter *PolarionReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { - reporter.suite = PolarionTestCases{ - TestCases: []PolarionTestCase{}, - } - reporter.testSuiteName = summary.SuiteDescription -} - -func (reporter *PolarionReporter) SpecWillRun(specSummary *types.SpecSummary) { -} - -func (reporter *PolarionReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { -} - -func (reporter *PolarionReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { -} - -func (reporter *PolarionReporter) SpecDidComplete(specSummary *types.SpecSummary) { - testName := fmt.Sprintf( - "%s: %s", - specSummary.ComponentTexts[1], - strings.Join(specSummary.ComponentTexts[2:], " "), - ) - testCase := PolarionTestCase{ - Title: Title{Content: testName}, - Description: Description{Content: testName}, - } - customFields := TestCaseCustomFields{} - customFields.CustomFields = append(customFields.CustomFields, TestCaseCustomField{ - Content: "automated", - ID: "caseautomation", - }) - testCase.TestCaseCustomFields = customFields - - reporter.suite.TestCases = append(reporter.suite.TestCases, testCase) -} - -func (reporter *PolarionReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { - if reporter.projectId == "" { - fmt.Println("Can not create Polarion report without project ID") - return - } - reporter.suite.ProjectID = reporter.projectId - file, err := os.Create(reporter.filename) - if err != nil { - fmt.Printf("Failed to create Polarion report file: %s\n\t%s", reporter.filename, err.Error()) - return - } - defer file.Close() - file.WriteString(xml.Header) - encoder := xml.NewEncoder(file) - encoder.Indent(" ", " ") - err = encoder.Encode(reporter.suite) - if err != nil { - fmt.Printf("Failed to generate Polarion report\n\t%s", err.Error()) - } -} diff --git a/vendor/google.golang.org/appengine/datastore/save.go b/vendor/google.golang.org/appengine/datastore/save.go index 728d4ca0c869..7b045a595568 100644 --- a/vendor/google.golang.org/appengine/datastore/save.go +++ b/vendor/google.golang.org/appengine/datastore/save.go @@ -306,22 +306,28 @@ func propertiesToProto(defaultAppID string, key *Key, props []Property) (*pb.Ent return e, nil } -// isEmptyValue is taken from the encoding/json package in the -// standard library. +// isEmptyValue is taken from the encoding/json package in the standard library. func isEmptyValue(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + // TODO(perfomance): Only reflect.String needed, other property types are not supported (copy/paste from json package) return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + // TODO(perfomance): Uint* are unsupported property types - should be removed (copy/paste from json package) return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() + case reflect.Struct: + switch x := v.Interface().(type) { + case time.Time: + return x.IsZero() + } } return false } diff --git a/vendor/gopkg.in/ini.v1/ini.go b/vendor/gopkg.in/ini.v1/ini.go index ccba1ee52a41..d983532299ed 100644 --- a/vendor/gopkg.in/ini.v1/ini.go +++ b/vendor/gopkg.in/ini.v1/ini.go @@ -32,7 +32,7 @@ const ( // Maximum allowed depth when recursively substituing variable names. _DEPTH_VALUES = 99 - _VERSION = "1.35.0" + _VERSION = "1.36.0" ) // Version returns current package version literal. @@ -140,6 +140,11 @@ type LoadOptions struct { // AllowNestedValues indicates whether to allow AWS-like nested values. // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values AllowNestedValues bool + // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values. + // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure + // Relevant quote: Values can also span multiple lines, as long as they are indented deeper + // than the first line of the value. + AllowPythonMultilineValues bool // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" UnescapeValueDoubleQuotes bool diff --git a/vendor/gopkg.in/ini.v1/parser.go b/vendor/gopkg.in/ini.v1/parser.go index db3af8f00447..826e893c0d77 100644 --- a/vendor/gopkg.in/ini.v1/parser.go +++ b/vendor/gopkg.in/ini.v1/parser.go @@ -19,11 +19,14 @@ import ( "bytes" "fmt" "io" + "regexp" "strconv" "strings" "unicode" ) +var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)") + type tokenType int const ( @@ -194,7 +197,8 @@ func hasSurroundedQuote(in string, quote byte) bool { } func (p *parser) readValue(in []byte, - ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols bool) (string, error) { + parserBufferSize int, + ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols, allowPythonMultilines bool) (string, error) { line := strings.TrimLeftFunc(string(in), unicode.IsSpace) if len(line) == 0 { @@ -224,11 +228,13 @@ func (p *parser) readValue(in []byte, return line[startIdx : pos+startIdx], nil } + lastChar := line[len(line)-1] // Won't be able to reach here if value only contains whitespace line = strings.TrimSpace(line) + trimmedLastChar := line[len(line)-1] // Check continuation lines when desired - if !ignoreContinuation && line[len(line)-1] == '\\' { + if !ignoreContinuation && trimmedLastChar == '\\' { return p.readContinuationLines(line[:len(line)-1]) } @@ -252,7 +258,50 @@ func (p *parser) readValue(in []byte, if strings.Contains(line, `\#`) { line = strings.Replace(line, `\#`, "#", -1) } + } else if allowPythonMultilines && lastChar == '\n' { + parserBufferPeekResult, _ := p.buf.Peek(parserBufferSize) + peekBuffer := bytes.NewBuffer(parserBufferPeekResult) + + identSize := -1 + val := line + + for { + peekData, peekErr := peekBuffer.ReadBytes('\n') + if peekErr != nil { + if peekErr == io.EOF { + return val, nil + } + return "", peekErr + } + + peekMatches := pythonMultiline.FindStringSubmatch(string(peekData)) + if len(peekMatches) != 3 { + return val, nil + } + + currentIdentSize := len(peekMatches[1]) + // NOTE: Return if not a python-ini multi-line value. + if currentIdentSize < 0 { + return val, nil + } + identSize = currentIdentSize + + // NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer. + _, err := p.readUntil('\n') + if err != nil { + return "", err + } + + val += fmt.Sprintf("\n%s", peekMatches[2]) + } + + // NOTE: If it was a Python multi-line value, + // return the appended value. + if identSize > 0 { + return val, nil + } } + return line, nil } @@ -276,6 +325,29 @@ func (f *File) parse(reader io.Reader) (err error) { var line []byte var inUnparseableSection bool + + // NOTE: Iterate and increase `currentPeekSize` until + // the size of the parser buffer is found. + // TODO: When Golang 1.10 is the lowest version supported, + // replace with `parserBufferSize := p.buf.Size()`. + parserBufferSize := 0 + // NOTE: Peek 1kb at a time. + currentPeekSize := 1024 + + if f.options.AllowPythonMultilineValues { + for { + peekBytes, _ := p.buf.Peek(currentPeekSize) + peekBytesLength := len(peekBytes) + + if parserBufferSize >= peekBytesLength { + break + } + + currentPeekSize *= 2 + parserBufferSize = peekBytesLength + } + } + for !p.isEOF { line, err = p.readUntil('\n') if err != nil { @@ -352,10 +424,12 @@ func (f *File) parse(reader io.Reader) (err error) { // Treat as boolean key when desired, and whole line is key name. if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { kname, err := p.readValue(line, + parserBufferSize, f.options.IgnoreContinuation, f.options.IgnoreInlineComment, f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols) + f.options.UnescapeValueCommentSymbols, + f.options.AllowPythonMultilineValues) if err != nil { return err } @@ -379,10 +453,12 @@ func (f *File) parse(reader io.Reader) (err error) { } value, err := p.readValue(line[offset:], + parserBufferSize, f.options.IgnoreContinuation, f.options.IgnoreInlineComment, f.options.UnescapeValueDoubleQuotes, - f.options.UnescapeValueCommentSymbols) + f.options.UnescapeValueCommentSymbols, + f.options.AllowPythonMultilineValues) if err != nil { return err } diff --git a/vendor/github.com/kubevirt/qe-tools/.gitignore b/vendor/kubevirt.io/qe-tools/.gitignore similarity index 94% rename from vendor/github.com/kubevirt/qe-tools/.gitignore rename to vendor/kubevirt.io/qe-tools/.gitignore index 08baa1afd923..cb2a93605ba3 100644 --- a/vendor/github.com/kubevirt/qe-tools/.gitignore +++ b/vendor/kubevirt.io/qe-tools/.gitignore @@ -13,3 +13,5 @@ # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ .idea + +**/polarion.xml diff --git a/vendor/kubevirt.io/qe-tools/.travis.yml b/vendor/kubevirt.io/qe-tools/.travis.yml new file mode 100644 index 000000000000..176a51c220d5 --- /dev/null +++ b/vendor/kubevirt.io/qe-tools/.travis.yml @@ -0,0 +1,19 @@ +language: go + +go: + - 1.8 + - 1.9 + +go_import_path: kubevirt.io/qe-tools + +install: true + +notifications: + email: false + +before_script: + - go get github.com/onsi/ginkgo + - go get github.com/onsi/gomega + +script: + - make test diff --git a/vendor/github.com/kubevirt/qe-tools/LICENSE b/vendor/kubevirt.io/qe-tools/LICENSE similarity index 100% rename from vendor/github.com/kubevirt/qe-tools/LICENSE rename to vendor/kubevirt.io/qe-tools/LICENSE diff --git a/vendor/kubevirt.io/qe-tools/Makefile b/vendor/kubevirt.io/qe-tools/Makefile new file mode 100644 index 000000000000..015765732bbc --- /dev/null +++ b/vendor/kubevirt.io/qe-tools/Makefile @@ -0,0 +1,12 @@ +all: build + +build: + cd pkg/ && go fmt ./... && go install -v ./... +deps-update: + glide cc && glide update --strip-vendor + hack/dep-prune.sh + +test: + cd pkg/ && go test -v ./... + +.PHONY: build deps-update test diff --git a/vendor/github.com/kubevirt/qe-tools/glide.lock b/vendor/kubevirt.io/qe-tools/glide.lock similarity index 100% rename from vendor/github.com/kubevirt/qe-tools/glide.lock rename to vendor/kubevirt.io/qe-tools/glide.lock diff --git a/vendor/github.com/kubevirt/qe-tools/glide.yaml b/vendor/kubevirt.io/qe-tools/glide.yaml similarity index 100% rename from vendor/github.com/kubevirt/qe-tools/glide.yaml rename to vendor/kubevirt.io/qe-tools/glide.yaml diff --git a/vendor/github.com/kubevirt/qe-tools/hack/dep-prune.sh b/vendor/kubevirt.io/qe-tools/hack/dep-prune.sh similarity index 100% rename from vendor/github.com/kubevirt/qe-tools/hack/dep-prune.sh rename to vendor/kubevirt.io/qe-tools/hack/dep-prune.sh diff --git a/vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/README.md b/vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/README.md new file mode 100644 index 000000000000..483b998dc6b3 --- /dev/null +++ b/vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/README.md @@ -0,0 +1,33 @@ +# Ginkgo reporters + +These reporters will build the xunit xml + + +## Polarion reporter +This reporter fills in the xunit file the needed fields in order to upload it into Polarion as test run + +#### Required parameters: +- --polarion-execution=true to enable the reporter +- --project-id="QE" will be set under 'properties' +- --polarion-custom-plannedin="QE_1_0" will be set under 'properties' +- --test-tier="tier1" will be set under 'properties' + +#### Optional parameters: +- --polarion-report-file the output file will be generated under working directory, the default is polarion_results.xml + +### Usage + +Include the reporter in the tests entry point +``` +if ginkgo_reporters.Polarion.Run { + reporters = append(reporters, &ginkgo_reporters.Polarion) + } +``` + +when executing the tests, in addition to your regular execution parameters, +add the reporter parameters as specified above + +``` bash +go test YOUR_PARAMS --polarion-execution=true --project-id="QE" --polarion-custom-plannedin="QE_1_0" --test-tier="tier1" --polarion-report-file="polarion.xml" +``` +Will generate `polarion.xml` file under the work directory that can be imported into polarion. diff --git a/vendor/github.com/kubevirt/qe-tools/ginkgo-reporters/junit_reporter.go b/vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/junit_reporter.go similarity index 100% rename from vendor/github.com/kubevirt/qe-tools/ginkgo-reporters/junit_reporter.go rename to vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/junit_reporter.go diff --git a/vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/polarion_reporter.go b/vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/polarion_reporter.go new file mode 100644 index 000000000000..6c04a7f19c22 --- /dev/null +++ b/vendor/kubevirt.io/qe-tools/pkg/ginkgo-reporters/polarion_reporter.go @@ -0,0 +1,208 @@ +/* + * This file is part of the KubeVirt project + * + * 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. + * + * Copyright 2018 Red Hat, Inc. + * + */ + +package ginkgo_reporters + +import ( + "encoding/xml" + "flag" + "fmt" + "strings" + + "kubevirt.io/qe-tools/pkg/polarion-xml" + + "github.com/onsi/ginkgo/config" + "github.com/onsi/ginkgo/types" +) + +var Polarion = PolarionReporter{} + +func init() { + flag.BoolVar(&Polarion.Run, "polarion-execution", false, "Run Polarion reporter") + flag.StringVar(&Polarion.ProjectId, "polarion-project-id", "", "Set Polarion project ID") + flag.StringVar(&Polarion.Filename, "polarion-report-file", "polarion_results.xml", "Set Polarion report file path") + flag.StringVar(&Polarion.PlannedIn, "polarion-custom-plannedin", "", "Set Polarion planned-in ID") + flag.StringVar(&Polarion.Tier, "test-tier", "", "Set test tier number") +} + +type PolarionTestSuite struct { + XMLName xml.Name `xml:"testsuite"` + Tests int `xml:"tests,attr"` + Failures int `xml:"failures,attr"` + Time float64 `xml:"time,attr"` + Properties PolarionProperties `xml:"properties"` + TestCases []PolarionTestCase `xml:"testcase"` +} + +type PolarionTestCase struct { + Name string `xml:"name,attr"` + FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"` + Skipped *JUnitSkipped `xml:"skipped,omitempty"` + SystemOut string `xml:"system-out,omitempty"` +} + +type JUnitFailureMessage struct { + Type string `xml:"type,attr"` + Message string `xml:",chardata"` +} + +type JUnitSkipped struct { + XMLName xml.Name `xml:"skipped"` +} + +type PolarionProperties struct { + Property []PolarionProperty `xml:"property"` +} + +type PolarionProperty struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} + +type PolarionReporter struct { + Suite PolarionTestSuite + Run bool + Filename string + TestSuiteName string + ProjectId string + PlannedIn string + Tier string +} + +func (reporter *PolarionReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { + + reporter.Suite = PolarionTestSuite{ + Properties: PolarionProperties{}, + TestCases: []PolarionTestCase{}, + } + + properties := PolarionProperties{ + Property: []PolarionProperty{ + { + Name: "polarion-project-id", + Value: reporter.ProjectId, + }, + { + Name: "polarion-testcase-lookup-method", + Value: "name", + }, + { + Name: "polarion-custom-plannedin", + Value: reporter.PlannedIn, + }, + { + Name: "polarion-testrun-id", + Value: reporter.PlannedIn + "_" + reporter.Tier, + }, + { + Name: "polarion-custom-isautomated", + Value: "True", + }, + }, + } + + reporter.Suite.Properties = properties + reporter.TestSuiteName = summary.SuiteDescription +} + +func (reporter *PolarionReporter) SpecWillRun(specSummary *types.SpecSummary) { +} + +func (reporter *PolarionReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { +} + +func (reporter *PolarionReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { +} + +func failureMessage(failure types.SpecFailure) string { + return fmt.Sprintf("%s\n%s\n%s", failure.ComponentCodeLocation.String(), failure.Message, failure.Location.String()) +} + +func (reporter *PolarionReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) { + if setupSummary.State != types.SpecStatePassed { + testCase := PolarionTestCase{ + Name: name, + } + + testCase.FailureMessage = &JUnitFailureMessage{ + Type: reporter.failureTypeForState(setupSummary.State), + Message: failureMessage(setupSummary.Failure), + } + testCase.SystemOut = setupSummary.CapturedOutput + reporter.Suite.TestCases = append(reporter.Suite.TestCases, testCase) + } +} + +func (reporter *PolarionReporter) SpecDidComplete(specSummary *types.SpecSummary) { + testName := fmt.Sprintf( + "%s: %s", + specSummary.ComponentTexts[1], + strings.Join(specSummary.ComponentTexts[2:], " "), + ) + testCase := PolarionTestCase{ + Name: testName, + } + if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked { + testCase.FailureMessage = &JUnitFailureMessage{ + Type: reporter.failureTypeForState(specSummary.State), + Message: failureMessage(specSummary.Failure), + } + testCase.SystemOut = specSummary.CapturedOutput + } + if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending { + testCase.Skipped = &JUnitSkipped{} + } + reporter.Suite.TestCases = append(reporter.Suite.TestCases, testCase) +} + +func (reporter *PolarionReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { + if reporter.ProjectId == "" { + fmt.Println("Can not create Polarion report without project ID") + return + } + if reporter.PlannedIn == "" { + fmt.Println("Can not create Polarion report without planned-in ID") + return + } + if reporter.Tier == "" { + fmt.Println("Can not create Polarion report without tier ID") + return + } + + reporter.Suite.Tests = summary.NumberOfSpecsThatWillBeRun + reporter.Suite.Time = summary.RunTime.Seconds() + reporter.Suite.Failures = summary.NumberOfFailedSpecs + + // generate polarion test cases XML file + polarion_xml.GeneratePolarionXmlFile(reporter.Filename, reporter.Suite) + +} + +func (reporter *PolarionReporter) failureTypeForState(state types.SpecState) string { + switch state { + case types.SpecStateFailed: + return "Failure" + case types.SpecStateTimedOut: + return "Timeout" + case types.SpecStatePanicked: + return "Panic" + default: + return "" + } +} diff --git a/vendor/kubevirt.io/qe-tools/pkg/polarion-generator/README.md b/vendor/kubevirt.io/qe-tools/pkg/polarion-generator/README.md new file mode 100644 index 000000000000..8f72e511219d --- /dev/null +++ b/vendor/kubevirt.io/qe-tools/pkg/polarion-generator/README.md @@ -0,0 +1,42 @@ +# Polarion test cases generator + +This tool will parse tests files in ginkgo format and extract +- Title - generated from concatenation of `Describe`, `Context`, `When`, `Specify` and `It` +- Description - generated from concatenation of `Describe`, `Context`, `When`, `Specify` and `It` +- Steps - generated from `By` +- Additional custom fields + +### Usage +```bash +make +polarion-generator --tests-dir=tests/ --output-file=polarion.xml --project-id=QE +``` +It will generate `polarion.xml` file under the work directory that can be imported into polarion. + +### Limitations + +Because generator use static analysis of AST, it creates number of limitations +- can not parse `By` in methods outside of main test `Describe` scope +- can not parse calls to methods under the `By`, for example +`By(fmt.Sprintf("%s step", "test"))` will not generate test step +- it will not parse steps from method, if the method was define after the call + +### Additional custom fields for a test + +You can automatically generate additional test custom fields like `importance` or `positive`, +you just need to create relevant polarion comment under test case. +``` +... +It("should work", func() { + // +polarion:caseimportance=critical + // +polarion:caseposneg=positive + ... +}) +``` + +Custom fields + +Name | Supported Values +--- | --- +caseimportance | critical, high, medium, low +caseposneg | positive, negative diff --git a/vendor/kubevirt.io/qe-tools/pkg/polarion-generator/test_cases_generator.go b/vendor/kubevirt.io/qe-tools/pkg/polarion-generator/test_cases_generator.go new file mode 100644 index 000000000000..fc51e3017c29 --- /dev/null +++ b/vendor/kubevirt.io/qe-tools/pkg/polarion-generator/test_cases_generator.go @@ -0,0 +1,425 @@ +/* + * This file is part of the KubeVirt project + * + * 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. + * + * Copyright 2018 Red Hat, Inc. + * + */ + +package main + +import ( + "flag" + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path/filepath" + "strings" + + "kubevirt.io/qe-tools/pkg/polarion-xml" +) + +const ( + ginkgoDescribe = "Describe" + ginkgoContext = "Context" + ginkgoSpecify = "Specify" + ginkgoTable = "DescribeTable" + ginkgoWhen = "When" + ginkgoBy = "By" + ginkgoIt = "It" +) + +const polarionPrefix = "+polarion:" + +var polarionCustomFields = map[string][]string{ + "caseimportance": {"critical", "high", "medium", "low"}, + "caseposneg": {"positive", "negative"}, +} + +type ginkgoBlock struct { + content []string + rparenPos []token.Pos + steps []string + stepContext []token.Pos + funcBlocks []funcBlock +} + +type funcBlock struct { + steps []string + name string +} + +func addCustomField(customFields *polarion_xml.TestCaseCustomFields, id, content string) { + customFields.CustomFields = append( + customFields.CustomFields, polarion_xml.TestCaseCustomField{ + Content: content, + ID: id, + }) +} + +func addTestStep(testCaseSteps *polarion_xml.TestCaseSteps, content string, prepend bool) { + testCaseStep := polarion_xml.TestCaseStep{ + StepColumn: []polarion_xml.TestCaseStepColumn{ + { + Content: content, + ID: "step", + }, + { + Content: "Succeeded", + ID: "expectedResult", + }, + }, + } + if prepend { + testCaseSteps.Steps = append([]polarion_xml.TestCaseStep{testCaseStep}, testCaseSteps.Steps...) + } else { + testCaseSteps.Steps = append(testCaseSteps.Steps, testCaseStep) + } +} + +func parseFunc(block *funcBlock, funcBody *ast.BlockStmt) { + ast.Inspect(funcBody, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.CallExpr: + ident, ok := x.Fun.(*ast.Ident) + if !ok { + return false + } + + if ident.Name != ginkgoBy { + return false + } else { + lit, ok := x.Args[0].(*ast.BasicLit) + if !ok { + return false + } + block.steps = append(block.steps, strings.Trim(lit.Value, "\"")) + } + } + return true + }) +} + +func parseIt(testcase *polarion_xml.TestCase, block *ginkgoBlock, funcBody *ast.BlockStmt) { + // add test steps from BeforeEach() + for i := len(block.stepContext) - 1; i >= 0; i-- { + if block.stepContext[i] > funcBody.Rbrace { + if testcase.TestCaseSteps == nil { + testcase.TestCaseSteps = &polarion_xml.TestCaseSteps{} + } + addTestStep(testcase.TestCaseSteps, block.steps[i], true) + } else { + block.stepContext = block.stepContext[:len(block.rparenPos)-1] + block.steps = block.steps[:len(block.content)-1] + } + } + + ast.Inspect(funcBody, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.CallExpr: + var ident *ast.Ident + if v, ok := x.Fun.(*ast.Ident); ok { + ident = v + } else if v, ok := x.Fun.(*ast.SelectorExpr); ok { + ident = v.Sel + } else { + return false + } + + for _, b := range block.funcBlocks { + if b.name == ident.Name { + for _, step := range b.steps { + if testcase.TestCaseSteps == nil { + testcase.TestCaseSteps = &polarion_xml.TestCaseSteps{} + } + addTestStep(testcase.TestCaseSteps, step, false) + } + return true + } + } + + if len(x.Args) < 1 { + return true + } + + var content string + if v, ok := x.Args[0].(*ast.BasicLit); ok { + content = v.Value + } else if v, ok := x.Args[0].(*ast.SelectorExpr); ok { + content = v.Sel.Name + } else { + return true + } + value := strings.Trim(content, "\"") + + switch ident.Name { + case ginkgoBy: + if testcase.TestCaseSteps == nil { + testcase.TestCaseSteps = &polarion_xml.TestCaseSteps{} + } + addTestStep(testcase.TestCaseSteps, value, false) + } + } + return true + }) +} + +func parseTable(testcases *polarion_xml.TestCases, block *ginkgoBlock, exprs []ast.Expr, customFields *polarion_xml.TestCaseCustomFields) { + lit := exprs[0].(*ast.BasicLit) + baseName := strings.Trim(lit.Value, "\"") + + funLit := exprs[1].(*ast.FuncLit) + tempCase := &polarion_xml.TestCase{} + parseIt(tempCase, block, funLit.Body) + + for _, entry := range exprs[2:] { + callerExpr := entry.(*ast.CallExpr) + for i := len(block.rparenPos) - 1; i >= 0; i-- { + if block.rparenPos[i] > callerExpr.Rparen { + break + } else { + block.rparenPos = block.rparenPos[:len(block.rparenPos)-1] + block.content = block.content[:len(block.content)-1] + } + } + + var content string + if v, ok := callerExpr.Args[0].(*ast.BasicLit); ok { + content = v.Value + } else if v, ok := callerExpr.Args[0].(*ast.SelectorExpr); ok { + content = v.Sel.Name + } + + value := strings.Trim(content, "\"") + title := fmt.Sprintf( + "%s:%s %s %s", + block.content[0], + strings.Join(block.content[1:], " "), + baseName, + value, + ) + testCase := &polarion_xml.TestCase{ + Title: polarion_xml.Title{Content: title}, + Description: polarion_xml.Description{Content: title}, + TestCaseCustomFields: *customFields, + TestCaseSteps: tempCase.TestCaseSteps, + } + addCustomField(&testCase.TestCaseCustomFields, "caseautomation", "automated") + addCustomField(&testCase.TestCaseCustomFields, "testtype", "functional") + addCustomField(&testCase.TestCaseCustomFields, "upstream", "yes") + testcases.TestCases = append(testcases.TestCases, *testCase) + } +} + +func parseComments(n ast.Node, commentMap *ast.CommentMap, customFields *polarion_xml.TestCaseCustomFields) { + for _, cg := range commentMap.Filter(n).Comments() { + for _, c := range cg.List { + if !strings.HasPrefix(strings.Trim(c.Text, "// "), polarionPrefix) { + continue + } + if polarionComment := strings.Split(c.Text, ":"); len(polarionComment) != 2 { + panic(fmt.Errorf("polarion comment %s has incorrect format", c.Text)) + } else if polarionField := strings.Split(polarionComment[1], "="); len(polarionField) != 2 { + panic(fmt.Errorf("polarion comment %s has incorrect custom field format", c.Text)) + } else { + supportedValues, ok := polarionCustomFields[polarionField[0]] + if !ok { + panic(fmt.Errorf("usupported polarion custom field id %s", polarionField[0])) + } + if isFieldValueSupported(polarionField[1], supportedValues) { + addCustomField(customFields, polarionField[0], polarionField[1]) + } else { + panic(fmt.Errorf("usupported value %s for polarion custom field %s", polarionField[1], polarionField[0])) + } + } + } + } +} + +func isFieldValueSupported(value string, supportedValues []string) bool { + for _, v := range supportedValues { + if v == value { + return true + } + } + return false +} + +// FillPolarionTestCases parse ginkgo format test and fill polarion test cases struct accordingly +func FillPolarionTestCases(f *ast.File, testCases *polarion_xml.TestCases, commentMap *ast.CommentMap) error { + var block *ginkgoBlock + + ast.Inspect(f, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.AssignStmt: + ident, ok := x.Lhs[0].(*ast.Ident) + if !ok { + return false + } + + funcDef, ok := x.Rhs[0].(*ast.FuncLit) + if !ok { + return false + } + + funcBlock := &funcBlock{name: ident.Name} + parseFunc(funcBlock, funcDef.Body) + block.funcBlocks = append(block.funcBlocks, *funcBlock) + return false + + case *ast.CallExpr: + var ident *ast.Ident + if v, ok := x.Fun.(*ast.Ident); ok { + ident = v + } else if v, ok := x.Fun.(*ast.SelectorExpr); ok { + ident = v.Sel + } else { + return false + } + + if len(x.Args) < 1 { + return true + } + + var content string + if v, ok := x.Args[0].(*ast.BasicLit); ok { + content = v.Value + } else if v, ok := x.Args[0].(*ast.SelectorExpr); ok { + content = v.Sel.Name + } else { + return true + } + value := strings.Trim(content, "\"") + + switch ident.Name { + case ginkgoDescribe, ginkgoContext, ginkgoWhen: + if block == nil { + block = &ginkgoBlock{ + content: []string{value}, + rparenPos: []token.Pos{x.Rparen}, + } + } else { + for i := len(block.rparenPos) - 1; i >= 0; i-- { + if block.rparenPos[i] > x.Rparen { + block.rparenPos = append(block.rparenPos, x.Rparen) + block.content = append(block.content, value) + break + } else { + block.rparenPos = block.rparenPos[:len(block.rparenPos)-1] + block.content = block.content[:len(block.content)-1] + } + } + } + case ginkgoBy: + block.steps = append(block.steps, value) + block.stepContext = append(block.stepContext, block.rparenPos[len(block.rparenPos)-1]) + case ginkgoTable: + customFields := polarion_xml.TestCaseCustomFields{} + parseComments(x, commentMap, &customFields) + parseTable(testCases, block, x.Args, &customFields) + return false + case ginkgoIt, ginkgoSpecify: + for i := len(block.rparenPos) - 1; i >= 0; i-- { + if block.rparenPos[i] > x.Rparen { + break + } else { + block.rparenPos = block.rparenPos[:len(block.rparenPos)-1] + block.content = block.content[:len(block.content)-1] + } + } + title := fmt.Sprintf("%s: %s", block.content[0], value) + if len(block.content[1:]) > 0 { + title = fmt.Sprintf("%s: %s %s", block.content[0], strings.Join(block.content[1:], " "), value) + } + customFields := polarion_xml.TestCaseCustomFields{} + addCustomField(&customFields, "caseautomation", "automated") + addCustomField(&customFields, "testtype", "functional") + addCustomField(&customFields, "upstream", "yes") + testCase := polarion_xml.TestCase{ + Title: polarion_xml.Title{Content: title}, + Description: polarion_xml.Description{Content: title}, + } + parseComments(x, commentMap, &customFields) + testCase.TestCaseCustomFields = customFields + funLit := x.Args[1].(*ast.FuncLit) + parseIt(&testCase, block, funLit.Body) + testCases.TestCases = append(testCases.TestCases, testCase) + return false + } + } + return true + }) + return nil +} + +func main() { + // parse input flags + testsDir := flag.String("tests-dir", ".", "Directory with tests files") + outputFile := flag.String("output-file", "polarion.xml", "Generated polarion test cases") + polarionProjectId := flag.String("project-id", "", "Set the Polarion project ID") + dryRun := flag.String("dry-run", "false", "Dry-run property") + flag.Parse() + + // collect all test files from the directory + var files []string + err := filepath.Walk(*testsDir, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + + if !strings.Contains(info.Name(), "_test") { + return nil + } + files = append(files, path) + return nil + }) + if err != nil { + panic(err) + } + + // parse all test files and fill polarion test cases + var testCases = &polarion_xml.TestCases{ + ProjectID: *polarionProjectId, + Properties: polarion_xml.PolarionProperties{ + Property: []polarion_xml.PolarionProperty{ + { + Name: "lookup-method", + Value: "name", + }, + { + Name: "dry-run", + Value: *dryRun, + }, + }, + }, + } + for _, file := range files { + // Create the AST by parsing src + fset := token.NewFileSet() // positions are relative to fset + f, err := parser.ParseFile(fset, file, nil, parser.ParseComments) + if err != nil { + panic(err) + } + // Create comment map + cmap := ast.NewCommentMap(fset, f, f.Comments) + + // fill polarion test cases struct + FillPolarionTestCases(f, testCases, &cmap) + } + + // generate polarion test cases XML file + polarion_xml.GeneratePolarionXmlFile(*outputFile, testCases) +} diff --git a/vendor/kubevirt.io/qe-tools/pkg/polarion-xml/polarion_xml.go b/vendor/kubevirt.io/qe-tools/pkg/polarion-xml/polarion_xml.go new file mode 100644 index 000000000000..aa47f31afa61 --- /dev/null +++ b/vendor/kubevirt.io/qe-tools/pkg/polarion-xml/polarion_xml.go @@ -0,0 +1,94 @@ +/* + * This file is part of the KubeVirt project + * + * 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. + * + * Copyright 2018 Red Hat, Inc. + * + */ + +package polarion_xml + +import ( + "encoding/xml" + "fmt" + "os" +) + +type TestCases struct { + Properties PolarionProperties `xml:"properties"` + XMLName xml.Name `xml:"testcases"` + TestCases []TestCase `xml:"testcase"` + ProjectID string `xml:"project-id,attr"` +} + +type TestCase struct { + Title Title `xml:"title"` + Description Description `xml:"description"` + TestCaseCustomFields TestCaseCustomFields `xml:"custom-fields"` + TestCaseSteps *TestCaseSteps `xml:"test-steps,omitempty"` +} + +type Title struct { + Content string `xml:",chardata"` +} + +type Description struct { + Content string `xml:",chardata"` +} + +type TestCaseCustomFields struct { + CustomFields []TestCaseCustomField `xml:"custom-field"` +} + +type TestCaseCustomField struct { + Content string `xml:"content,attr"` + ID string `xml:"id,attr"` +} + +type TestCaseSteps struct { + Steps []TestCaseStep `xml:"test-step"` +} + +type TestCaseStep struct { + StepColumn []TestCaseStepColumn `xml:"test-step-column"` +} + +type TestCaseStepColumn struct { + Content string `xml:",chardata"` + ID string `xml:"id,attr"` +} + +type PolarionProperties struct { + Property []PolarionProperty `xml:"property"` +} + +type PolarionProperty struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} + +func GeneratePolarionXmlFile(outputFile string, testCases interface{}) { + file, err := os.Create(outputFile) + if err != nil { + panic(fmt.Errorf("Failed to create Polarion report file: %s\n\t%s", outputFile, err.Error())) + } + defer file.Close() + file.WriteString(xml.Header) + encoder := xml.NewEncoder(file) + encoder.Indent(" ", " ") + err = encoder.Encode(testCases) + if err != nil { + panic(fmt.Errorf("Failed to generate Polarion report\n\t%s", err.Error())) + } +}