github.com/jonsyu1/godel@v0.0.0-20171017211503-64567a0cf169/apps/gunit/cmd/global.go (about) 1 // Copyright 2016 Palantir Technologies, 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 cmd 16 17 import ( 18 "fmt" 19 "sort" 20 "strings" 21 22 "github.com/palantir/amalgomate/amalgomated" 23 "github.com/palantir/pkg/cli" 24 "github.com/palantir/pkg/cli/flag" 25 "github.com/palantir/pkg/matcher" 26 "github.com/palantir/pkg/pkgpath" 27 "github.com/pkg/errors" 28 29 "github.com/palantir/godel/apps/gunit/generated_src" 30 "github.com/palantir/godel/apps/gunit/params" 31 ) 32 33 var Library = amalgomated.NewCmdLibrary(amalgomatedtesters.Instance()) 34 35 const ( 36 junitOutputPathFlagName = "junit-output" 37 raceFlagName = "race" 38 tagsFlagName = "tags" 39 verboseFlagName = "verbose" 40 verboseFlagAlias = "v" 41 ) 42 43 var ( 44 GlobalFlags = []flag.Flag{ 45 flag.StringFlag{ 46 Name: tagsFlagName, 47 Usage: "Run tests that are part of the provided tags (use commas to separate multiple tags)", 48 }, 49 flag.BoolFlag{ 50 Name: verboseFlagName, 51 Alias: verboseFlagAlias, 52 Usage: "Enable verbose output for tests", 53 }, 54 flag.BoolFlag{ 55 Name: raceFlagName, 56 Usage: "Enable race detector for tests", 57 }, 58 flag.StringFlag{ 59 Name: junitOutputPathFlagName, 60 Usage: "Path to JUnit XML output (if provided, verbose flag is set to true)", 61 }, 62 } 63 ) 64 65 func Tags(ctx cli.Context) []string { 66 if !ctx.Has(tagsFlagName) { 67 return nil 68 } 69 return strings.Split(strings.ToLower(ctx.String(tagsFlagName)), ",") 70 } 71 72 func Verbose(ctx cli.Context) bool { 73 return ctx.Bool(verboseFlagName) 74 } 75 76 func Race(ctx cli.Context) bool { 77 return ctx.Bool(raceFlagName) 78 } 79 80 func JUnitOutputPath(ctx cli.Context) string { 81 return ctx.String(junitOutputPathFlagName) 82 } 83 84 // TagsMatcher returns a Matcher that matches all packages that are matched by the provided tags. If no tags are 85 // provided, returns nil. If the tags consist of a single tag named "all", the returned matcher matches the union of all 86 // known tags. If the tags consist of a single tag named "none", the returned matcher matches everything except the 87 // union of all known tags (untagged tests). 88 func TagsMatcher(tags []string, cfg params.GUnit) (matcher.Matcher, error) { 89 if len(tags) == 0 { 90 // if no tags were provided, does not match anything 91 return nil, nil 92 } 93 94 if len(tags) == 1 { 95 var allMatchers []matcher.Matcher 96 for _, matcher := range cfg.Tags { 97 allMatchers = append(allMatchers, matcher) 98 } 99 anyTagMatcher := matcher.Any(allMatchers...) 100 switch tags[0] { 101 case params.AllTagName: 102 // if tags contains only a single tag that is the "all" tag, return matcher that matches union of all tags 103 return anyTagMatcher, nil 104 case params.NoneTagName: 105 // if tags contains only a single tag that is the "none" tag, return matcher that matches not of union of all tags 106 return matcher.Not(anyTagMatcher), nil 107 } 108 } 109 110 // due to previous check, if "all" or "none" tag exists at this point it means that it was one of multiple tags 111 for _, tag := range tags { 112 switch tag { 113 case params.AllTagName, params.NoneTagName: 114 return nil, errors.Errorf("if %q tag is specified, it must be the only tag specified", tag) 115 } 116 } 117 118 var tagMatchers []matcher.Matcher 119 var missingTags []string 120 for _, tag := range tags { 121 if include, ok := cfg.Tags[tag]; ok { 122 tagMatchers = append(tagMatchers, include) 123 } else { 124 missingTags = append(missingTags, fmt.Sprintf("%q", tag)) 125 } 126 } 127 128 if len(missingTags) > 0 { 129 var allTags []string 130 for tag := range cfg.Tags { 131 allTags = append(allTags, fmt.Sprintf("%q", tag)) 132 } 133 sort.Strings(allTags) 134 validTagsOutput := fmt.Sprintf("Valid tags: %v", strings.Join(allTags, ", ")) 135 if len(allTags) == 0 { 136 validTagsOutput = "No tags are defined." 137 } 138 return nil, fmt.Errorf("Tags %v not defined in configuration. %s", strings.Join(missingTags, ", "), validTagsOutput) 139 } 140 141 // not possible: if initial tags were empty then should have already returned, if specified tags did not match then 142 // missing block should have executed and returned, so at this point matchers must exist 143 if len(tagMatchers) == 0 { 144 panic("no matching tags found") 145 } 146 147 // OR of tags 148 return matcher.Any(tagMatchers...), nil 149 } 150 151 // PkgPaths returns a slice that contains the relative package paths for the packages "pkgPaths" relative to the 152 // project directory "wd" excluding any of the paths that match the provided "exclude" Matcher. If "pkgPaths" is an 153 // empty slice, then all of the packages in "wd" (except those that match the "exclude" matcher) are returned. 154 func PkgPaths(pkgPaths []string, wd string, exclude matcher.Matcher) ([]string, error) { 155 var pkgs pkgpath.Packages 156 var err error 157 if len(pkgPaths) == 0 { 158 // if input slice is empty, return all matching packages 159 pkgs, err = pkgpath.PackagesInDir(wd, exclude) 160 if err != nil { 161 return nil, errors.Wrapf(err, "failed to list packages in %s", wd) 162 } 163 } else { 164 // otherwise, filter provided packages and return those that are not excluded 165 var nonExcludedPkgs []string 166 for _, currPkg := range pkgPaths { 167 if !exclude.Match(currPkg) { 168 nonExcludedPkgs = append(nonExcludedPkgs, currPkg) 169 } 170 } 171 pkgs, err = pkgpath.PackagesFromPaths(wd, nonExcludedPkgs) 172 if err != nil { 173 return nil, errors.Wrapf(err, "failed to parse %v as packages", pkgPaths) 174 } 175 } 176 resultPkgPaths, err := pkgs.Paths(pkgpath.Relative) 177 if err != nil { 178 return nil, errors.Wrapf(err, "failed to get relative paths for packages %v", pkgs) 179 } 180 return resultPkgPaths, nil 181 }