OLD | NEW |
1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package deps | 5 package deps |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 | 9 |
10 "github.com/golang/protobuf/proto" | 10 "github.com/golang/protobuf/proto" |
11 "github.com/luci/gae/service/datastore" | 11 "github.com/luci/gae/service/datastore" |
| 12 "github.com/luci/gae/service/datastore/dumper" |
| 13 "github.com/luci/luci-go/appengine/cmd/dm/distributor/fake" |
12 "github.com/luci/luci-go/appengine/cmd/dm/model" | 14 "github.com/luci/luci-go/appengine/cmd/dm/model" |
| 15 "github.com/luci/luci-go/appengine/cmd/dm/mutate" |
| 16 "github.com/luci/luci-go/appengine/tumble" |
13 dm "github.com/luci/luci-go/common/api/dm/service/v1" | 17 dm "github.com/luci/luci-go/common/api/dm/service/v1" |
14 "github.com/luci/luci-go/common/clock" | |
15 "github.com/luci/luci-go/common/cryptorand" | |
16 . "github.com/luci/luci-go/common/testing/assertions" | |
17 . "github.com/smartystreets/goconvey/convey" | 18 . "github.com/smartystreets/goconvey/convey" |
18 "golang.org/x/net/context" | 19 "golang.org/x/net/context" |
19 ) | 20 ) |
20 | 21 |
21 func ensureQuest(c context.Context, name string, aids ...uint32) string { | 22 func testSetup() (ttest *tumble.Testing, c context.Context, dist *fake.Distribut
or, s testDepsServer) { |
22 » desc := &dm.Quest_Desc{ | 23 » ttest, c, dist, reg := fake.Setup(mutate.FinishExecutionFn) |
23 » » DistributorConfigName: "foof", | 24 » s = testDepsServer{newDecoratedDeps(reg)} |
24 » » JsonPayload: fmt.Sprintf(`{"name": "%s"}`, name), | 25 » return |
25 » } | 26 } |
26 » q, err := model.NewQuest(c, desc) | 27 |
27 » So(err, ShouldBeNil) | 28 type testDepsServer struct { |
28 » qsts, err := newDecoratedDeps().EnsureGraphData(c, &dm.EnsureGraphDataRe
q{ | 29 » dm.DepsServer |
| 30 } |
| 31 |
| 32 func (s testDepsServer) ensureQuest(c context.Context, name string, aids ...uint
32) string { |
| 33 » desc := fake.QuestDesc(name) |
| 34 » q := model.NewQuest(c, desc) |
| 35 » qsts, err := s.EnsureGraphData(c, &dm.EnsureGraphDataReq{ |
29 Quest: []*dm.Quest_Desc{desc}, | 36 Quest: []*dm.Quest_Desc{desc}, |
30 Attempts: dm.NewAttemptList(map[string][]uint32{q.ID: aids}), | 37 Attempts: dm.NewAttemptList(map[string][]uint32{q.ID: aids}), |
31 }) | 38 }) |
32 So(err, ShouldBeNil) | 39 So(err, ShouldBeNil) |
33 for qid := range qsts.Result.Quests { | 40 for qid := range qsts.Result.Quests { |
34 So(qid, ShouldEqual, q.ID) | 41 So(qid, ShouldEqual, q.ID) |
35 return qid | 42 return qid |
36 } | 43 } |
37 panic("impossible") | 44 panic("impossible") |
38 } | 45 } |
39 | 46 |
40 func mkToken(c context.Context) []byte { | 47 func dumpDatastore(c context.Context) { |
41 » rtok := make([]byte, 32) | 48 » ds := datastore.Get(c) |
42 » _, err := cryptorand.Read(c, rtok) | 49 » snap := ds.Testable().TakeIndexSnapshot() |
43 » if err != nil { | 50 » ds.Testable().CatchupIndexes() |
44 » » panic(err) | 51 » defer ds.Testable().SetIndexSnapshot(snap) |
45 » } | 52 |
46 » return rtok | 53 » fmt.Println("dumping datastore") |
| 54 » dumper.Config{ |
| 55 » » PropFilters: dumper.PropFilterMap{ |
| 56 » » » {"Quest", "Desc"}: func(prop datastore.Property) string
{ |
| 57 » » » » desc := &dm.Quest_Desc{} |
| 58 » » » » if err := proto.Unmarshal(prop.Value().([]byte),
desc); err != nil { |
| 59 » » » » » panic(err) |
| 60 » » » » } |
| 61 » » » » return desc.String() |
| 62 » » » }, |
| 63 » » » {"Attempt", "State"}: func(prop datastore.Property) stri
ng { |
| 64 » » » » return dm.Attempt_State(int32(prop.Value().(int6
4))).String() |
| 65 » » » }, |
| 66 » » » {"Execution", "State"}: func(prop datastore.Property) st
ring { |
| 67 » » » » return dm.Execution_State(int32(prop.Value().(in
t64))).String() |
| 68 » » » }, |
| 69 » » }, |
| 70 » }.Query(c, nil) |
47 } | 71 } |
48 | |
49 func execute(c context.Context, aid *dm.Attempt_ID) *dm.Execution_Auth { | |
50 // takes an NeedsExecution attempt, and moves it to Executing | |
51 rtok := mkToken(c) | |
52 ret := &dm.Execution_Auth{Token: rtok} | |
53 | |
54 err := datastore.Get(c).RunInTransaction(func(c context.Context) error { | |
55 ds := datastore.Get(c) | |
56 atmpt := &model.Attempt{ID: *aid} | |
57 if err := ds.Get(atmpt); err != nil { | |
58 panic(err) | |
59 } | |
60 | |
61 atmpt.CurExecution++ | |
62 atmpt.MustModifyState(c, dm.Attempt_EXECUTING) | |
63 | |
64 ret.Id = dm.NewExecutionID(atmpt.ID.Quest, atmpt.ID.Id, atmpt.Cu
rExecution) | |
65 | |
66 So(ds.PutMulti([]interface{}{atmpt, &model.Execution{ | |
67 ID: atmpt.CurExecution, | |
68 Created: clock.Now(c), | |
69 State: dm.Execution_SCHEDULED, | |
70 Attempt: ds.KeyForObj(atmpt), | |
71 Token: rtok, | |
72 }}), ShouldBeNil) | |
73 return nil | |
74 }, nil) | |
75 So(err, ShouldBeNil) | |
76 return ret | |
77 } | |
78 | |
79 func activate(c context.Context, auth *dm.Execution_Auth) *dm.Execution_Auth { | |
80 newTok := mkToken(c) | |
81 _, err := newDecoratedDeps().ActivateExecution(c, &dm.ActivateExecutionR
eq{ | |
82 Auth: auth, ExecutionToken: newTok}) | |
83 So(err, ShouldBeNil) | |
84 return &dm.Execution_Auth{Id: auth.Id, Token: newTok} | |
85 } | |
86 | |
87 func depOn(c context.Context, fromAuth *dm.Execution_Auth, to ...*dm.Attempt_ID)
{ | |
88 req := &dm.EnsureGraphDataReq{ | |
89 ForExecution: fromAuth, | |
90 Attempts: dm.NewAttemptList(nil), | |
91 } | |
92 req.Attempts.AddAIDs(to...) | |
93 | |
94 rsp, err := newDecoratedDeps().EnsureGraphData(c, req) | |
95 if err != nil { | |
96 panic(err) | |
97 } | |
98 So(rsp.ShouldHalt, ShouldBeTrue) | |
99 } | |
100 | |
101 func purgeTimestamps(gd *dm.GraphData) { | |
102 for _, q := range gd.Quests { | |
103 if q.GetData().GetCreated() != nil { | |
104 q.Data.Created = nil | |
105 } | |
106 for _, a := range q.GetAttempts() { | |
107 if a.Data != nil { | |
108 a.Data.Created = nil | |
109 a.Data.Modified = nil | |
110 if ne := a.Data.GetNeedsExecution(); ne != nil { | |
111 ne.Pending = nil | |
112 } else if fi := a.Data.GetFinished(); fi != nil
{ | |
113 fi.Expiration = nil | |
114 } | |
115 } | |
116 for _, e := range a.Executions { | |
117 if e.Data != nil { | |
118 e.Data.Created = nil | |
119 } | |
120 } | |
121 } | |
122 } | |
123 } | |
124 | |
125 func WalkShouldReturn(c context.Context, keepTimestamps ...bool) func(request in
terface{}, expect ...interface{}) string { | |
126 kt := len(keepTimestamps) > 0 && keepTimestamps[0] | |
127 | |
128 normalize := func(gd *dm.GraphData) *dm.GraphData { | |
129 data, err := proto.Marshal(gd) | |
130 if err != nil { | |
131 panic(err) | |
132 } | |
133 So(err, ShouldBeNil) | |
134 ret := &dm.GraphData{} | |
135 if err := proto.Unmarshal(data, ret); err != nil { | |
136 panic(err) | |
137 } | |
138 if !kt { | |
139 purgeTimestamps(ret) | |
140 } | |
141 return ret | |
142 } | |
143 | |
144 return func(request interface{}, expect ...interface{}) string { | |
145 r := request.(*dm.WalkGraphReq) | |
146 e := expect[0].(*dm.GraphData) | |
147 ret, err := newDecoratedDeps().WalkGraph(c, r) | |
148 if nilExpect := ShouldErrLike(err, nil); nilExpect != "" { | |
149 return nilExpect | |
150 } | |
151 return ShouldResemble(normalize(ret), e) | |
152 } | |
153 } | |
OLD | NEW |