| 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 |