• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 The Wuffs Authors.
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//    https://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
15package main
16
17import (
18	"flag"
19	"fmt"
20	"os"
21	"os/exec"
22	"path/filepath"
23	"strings"
24
25	cf "github.com/google/wuffs/cmd/commonflags"
26)
27
28func doBench(wuffsRoot string, args []string) error { return doBenchTest(wuffsRoot, args, true) }
29func doTest(wuffsRoot string, args []string) error  { return doBenchTest(wuffsRoot, args, false) }
30
31func doBenchTest(wuffsRoot string, args []string, bench bool) error {
32	flags := flag.NewFlagSet("test", flag.ExitOnError)
33	ccompilersFlag := flags.String("ccompilers", cf.CcompilersDefault, cf.CcompilersUsage)
34	cformatterFlag := flags.String("cformatter", cf.CformatterDefault, cf.CformatterUsage)
35	focusFlag := flags.String("focus", cf.FocusDefault, cf.FocusUsage)
36	iterscaleFlag := flags.Int("iterscale", cf.IterscaleDefault, cf.IterscaleUsage)
37	langsFlag := flags.String("langs", langsDefault, langsUsage)
38	mimicFlag := flags.Bool("mimic", cf.MimicDefault, cf.MimicUsage)
39	repsFlag := flags.Int("reps", cf.RepsDefault, cf.RepsUsage)
40	skipgenFlag := flags.Bool("skipgen", skipgenDefault, skipgenUsage)
41	skipgendepsFlag := flags.Bool("skipgendeps", skipgendepsDefault, skipgendepsUsage)
42
43	if err := flags.Parse(args); err != nil {
44		return err
45	}
46
47	langs, err := parseLangs(*langsFlag)
48	if err != nil {
49		return err
50	}
51	if !cf.IsAlphaNumericIsh(*ccompilersFlag) {
52		return fmt.Errorf("bad -ccompilers flag value %q", *ccompilersFlag)
53	}
54	if !cf.IsAlphaNumericIsh(*cformatterFlag) {
55		return fmt.Errorf("bad -cformatter flag value %q", *cformatterFlag)
56	}
57	if !cf.IsAlphaNumericIsh(*focusFlag) {
58		return fmt.Errorf("bad -focus flag value %q", *focusFlag)
59	}
60	if *iterscaleFlag < cf.IterscaleMin || cf.IterscaleMax < *iterscaleFlag {
61		return fmt.Errorf("bad -iterscale flag value %d, outside the range [%d..%d]",
62			*iterscaleFlag, cf.IterscaleMin, cf.IterscaleMax)
63	}
64	if *repsFlag < cf.RepsMin || cf.RepsMax < *repsFlag {
65		return fmt.Errorf("bad -reps flag value %d, outside the range [%d..%d]",
66			*repsFlag, cf.RepsMin, cf.RepsMax)
67	}
68
69	args = flags.Args()
70	if len(args) == 0 {
71		args = []string{"base", "std/..."}
72	}
73
74	cmdArgs := []string(nil)
75	if bench {
76		cmdArgs = append(cmdArgs, "bench",
77			fmt.Sprintf("-iterscale=%d", *iterscaleFlag),
78			fmt.Sprintf("-reps=%d", *repsFlag),
79		)
80	} else {
81		cmdArgs = append(cmdArgs, "test")
82	}
83	if *focusFlag != "" {
84		cmdArgs = append(cmdArgs, fmt.Sprintf("-focus=%s", *focusFlag))
85	}
86	if *mimicFlag {
87		cmdArgs = append(cmdArgs, "-mimic")
88	}
89
90	h := testHelper{
91		wuffsRoot:  wuffsRoot,
92		langs:      langs,
93		cmdArgs:    cmdArgs,
94		ccompilers: *ccompilersFlag,
95	}
96
97	// Ensure that we are testing the latest version of the generated code.
98	if !*skipgenFlag {
99		gh := genHelper{
100			wuffsRoot:   wuffsRoot,
101			langs:       langs,
102			cformatter:  *cformatterFlag,
103			skipgen:     *skipgenFlag,
104			skipgendeps: *skipgendepsFlag,
105		}
106		for _, arg := range args {
107			recursive := strings.HasSuffix(arg, "/...")
108			if recursive {
109				arg = arg[:len(arg)-4]
110			}
111			if arg == "" {
112				continue
113			}
114
115			if err := gh.gen(arg, recursive); err != nil {
116				return err
117			}
118		}
119		if err := genrelease(wuffsRoot, langs, cf.Version{}); err != nil {
120			return err
121		}
122	}
123
124	failed := false
125	for _, arg := range args {
126		recursive := strings.HasSuffix(arg, "/...")
127		if recursive {
128			arg = arg[:len(arg)-4]
129		}
130		if arg == "" {
131			continue
132		}
133
134		// Proceed with benching / testing the generated code.
135		f, err := h.benchTest(arg, recursive)
136		if err != nil {
137			return err
138		}
139		failed = failed || f
140	}
141	if failed {
142		s0, s1 := "test", "tests"
143		if bench {
144			s0, s1 = "bench", "benchmarks"
145		}
146		return fmt.Errorf("wuffs %s: some %s failed", s0, s1)
147	}
148	return nil
149}
150
151type testHelper struct {
152	wuffsRoot  string
153	langs      []string
154	cmdArgs    []string
155	ccompilers string
156}
157
158func (h *testHelper) benchTest(dirname string, recursive bool) (failed bool, err error) {
159	if dirname == "base" {
160		return false, nil
161	}
162	qualFilenames, dirnames, err := listDir(
163		filepath.Join(h.wuffsRoot, filepath.FromSlash(dirname)), ".wuffs", recursive)
164	if err != nil {
165		return false, err
166	}
167	if len(qualFilenames) > 0 {
168		f, err := h.benchTestDir(dirname)
169		if err != nil {
170			return false, err
171		}
172		failed = failed || f
173	}
174	if len(dirnames) > 0 {
175		for _, d := range dirnames {
176			f, err := h.benchTest(filepath.Join(dirname, d), recursive)
177			if err != nil {
178				return false, err
179			}
180			failed = failed || f
181		}
182	}
183	return failed, nil
184}
185
186func (h *testHelper) benchTestDir(dirname string) (failed bool, err error) {
187	if packageName := filepath.Base(dirname); !validName(packageName) {
188		return false, fmt.Errorf(`invalid package %q, not in [a-z0-9]+`, packageName)
189	}
190
191	for _, lang := range h.langs {
192		command := "wuffs-" + lang
193		args := []string(nil)
194		args = append(args, h.cmdArgs...)
195		if lang == "c" {
196			args = append(args, fmt.Sprintf("-ccompilers=%s", h.ccompilers))
197		}
198		args = append(args, filepath.Join(h.wuffsRoot, "test", lang, filepath.FromSlash(dirname)))
199		cmd := exec.Command(command, args...)
200		cmd.Stdout = os.Stdout
201		cmd.Stderr = os.Stderr
202		if err := cmd.Run(); err == nil {
203			// No-op.
204		} else if _, ok := err.(*exec.ExitError); ok {
205			failed = true
206		} else {
207			return false, err
208		}
209	}
210	return failed, nil
211}
212