Index: appengine/cmd/dm/distributor/impl/jobsim/parser/parser_test.go |
diff --git a/appengine/cmd/dm/distributor/impl/jobsim/parser/parser_test.go b/appengine/cmd/dm/distributor/impl/jobsim/parser/parser_test.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..77892757d23fd206a4a382cbe253294f27152bc0 |
--- /dev/null |
+++ b/appengine/cmd/dm/distributor/impl/jobsim/parser/parser_test.go |
@@ -0,0 +1,135 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package parser |
+ |
+import ( |
+ "fmt" |
+ "testing" |
+ "time" |
+ |
+ . "github.com/luci/luci-go/common/testing/assertions" |
+ . "github.com/smartystreets/goconvey/convey" |
+) |
+ |
+func TestValidation(t *testing.T) { |
+ good := map[string]Phrase{ |
+ "hello": {DepsStage{{Name: "hello"}}}, |
+ |
+ "A'": {DepsStage{{Name: "A'"}}}, |
+ |
+ "A' # comment!\n,{4}B&C": { |
+ DepsStage{{Name: "A'"}}, |
+ DepsStage{ |
+ {Name: "B", ShardCount: 4}, |
+ {Name: "C"}, |
+ }, |
+ }, |
+ |
+ "A,sub(B,C),D": { |
+ DepsStage{{Name: "A"}}, |
+ DepsStage{{Name: "sub", Substages: Phrase{ |
+ DepsStage{{Name: "B"}}, |
+ DepsStage{{Name: "C"}}, |
+ }}}, |
+ DepsStage{{Name: "D"}}, |
+ }, |
+ |
+ "[5]A": {DepsStage{ |
+ {Name: "A", AttemptNums: RangeSlice{{5, 0}}}}}, |
+ |
+ "A&B&C": {DepsStage{ |
+ {Name: "A"}, {Name: "B"}, {Name: "C"}, |
+ }}, |
+ |
+ "top(A,B)": { |
+ DepsStage{{Name: "top", Substages: Phrase{ |
+ DepsStage{{Name: "A"}}, |
+ DepsStage{{Name: "B"}}, |
+ }}}, |
+ }, |
+ |
+ "top(A,%1, B)": { |
+ DepsStage{{Name: "top", Substages: Phrase{ |
+ DepsStage{{Name: "A"}}, |
+ FailureStage(1), |
+ DepsStage{{Name: "B"}}, |
+ }}}, |
+ }, |
+ |
+ "cool(@5,=10<20)": { |
+ DepsStage{{Name: "cool", Substages: Phrase{ |
+ StallStage(time.Second * 5), |
+ &ReturnStage{10, time.Second * 20}, |
+ }}}, |
+ }, |
+ |
+ "[1-3, 2-5]cool": {DepsStage{ |
+ {Name: "cool", AttemptNums: RangeSlice{{1, 3}, {2, 5}}}}}, |
+ |
+ "cool+4(nerd(%80))": {DepsStage{ |
+ {Name: "cool", Retries: 4, Substages: Phrase{ |
+ DepsStage{{Name: "nerd", Substages: Phrase{ |
+ FailureStage(80), |
+ }}}, |
+ }}, |
+ }}, |
+ |
+ "[1,2-3]cool": {DepsStage{ |
+ {Name: "cool", AttemptNums: RangeSlice{{1, 0}, {2, 3}}}, |
+ }}, |
+ } |
+ |
+ bad := map[string]string{ |
+ "": "empty phrase", |
+ "&wat": "expected dependency", |
+ "=10,wat": `extra: ",wat"`, |
+ "=10,=20": `extra: ",=20"`, |
+ "wat&%10": `extra: "&%10"`, |
+ "100": "expected phrase", |
+ "A10": `extra: "10"`, |
+ ",something": "expected stage", |
+ "A.A'&&": `extra: ".A'&&"`, |
+ "A{5}": `extra: "{5}"`, |
+ "(5)": "expected phrase", |
+ "=cow": "expected number", |
+ "=10<neet": "expected number", |
+ "@moo": "expected number", |
+ "%moo": "expected number", |
+ "what&&": `extra: "&&"`, |
+ "{no}cats": "expected phrase", |
+ "{10,moo}": "expected phrase", |
+ "[2-1]cool": `invalid range "2-1": lo >= hi`, |
+ "[2-bob]cool": "expected number", |
+ "[bob]cool": "expected number", |
+ "cool+nope": "expected number", |
+ "cool(@)": "expected number", |
+ "cool!pop": `extra: "!pop"`, |
+ "cool+0": "zero value not acceptable", |
+ "[0]cool": "zero value not acceptable", |
+ "[0-2]cool": "zero value not acceptable", |
+ } |
+ |
+ Convey("validPhrase", t, func() { |
+ Convey("good", func() { |
+ for in, p := range good { |
+ Convey(fmt.Sprintf("%q", in), func() { |
+ cleaned := CleanPhrase(in) |
+ out, err := ParsePhrase(cleaned) |
+ So(err, ShouldBeNil) |
+ So(out, ShouldResembleV, p) |
+ So(out.String(), ShouldEqual, cleaned) |
+ }) |
+ } |
+ }) |
+ Convey("bad", func() { |
+ for in, expect := range bad { |
+ Convey(fmt.Sprintf("%q", in), func() { |
+ _, err := ParsePhrase(CleanPhrase(in)) |
+ So(err, ShouldErrLike, expect) |
+ }) |
+ } |
+ }) |
+ }) |
+} |