github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/grid/grid.go (about) 1 package grid 2 3 import ( 4 "github.com/evergreen-ci/evergreen" 5 "github.com/evergreen-ci/evergreen/apimodels" 6 "github.com/evergreen-ci/evergreen/db" 7 "github.com/evergreen-ci/evergreen/model/build" 8 "github.com/evergreen-ci/evergreen/model/task" 9 "github.com/evergreen-ci/evergreen/model/version" 10 "gopkg.in/mgo.v2/bson" 11 ) 12 13 // CellId represents a unique identifier for each cell on 14 // the Grid page. 15 type CellId struct { 16 Task string `bson:"d" json:"task"` 17 Variant string `bson:"v" json:"variant"` 18 } 19 20 // CellHistory holds historical data for each cell on the 21 // Grid page. 22 type CellHistory struct { 23 Id string `bson:"d" json:"id"` 24 Revision string `bson:"v" json:"revision"` 25 Status string `bson:"s" json:"status"` 26 StatusDetails apimodels.TaskEndDetail `bson:"e" json:"task_end_details"` 27 } 28 29 // Cell contains information for each cell on the Grid page. 30 // It includes both the cell's identifier and its history. 31 type Cell struct { 32 Id CellId `bson:"_id" json:"cellId"` 33 History []CellHistory `bson:"h" json:"history"` 34 } 35 36 // Grid is a slice of cells. 37 type Grid []Cell 38 39 type FailureId struct { 40 Test string `bson:"t" json:"test"` 41 Task string `bson:"d" json:"task"` 42 } 43 44 // VariantInfo holds information for each variant a test fails on. 45 type VariantInfo struct { 46 Name string `bson:"n" json:"name"` 47 TaskId string `bson:"i" json:"task_id"` 48 } 49 50 // Failure contains data for a particular test failure - including 51 // the test's task display name, and the variants its failing on. 52 type Failure struct { 53 Id FailureId `bson:"_id" json:"identifier"` 54 Variants []VariantInfo `bson:"a" json:"variants"` 55 } 56 57 // Revision failure contains the revision and a list of the 58 // task failures that exist on that version. 59 type RevisionFailure struct { 60 Id string `bson:"_id" json:"revision"` 61 Failures []TaskFailure `bson:"a" json:"failures"` 62 } 63 64 // TaskFailure has the information needed to be displayed on 65 // the revision failures tab. 66 type TaskFailure struct { 67 BuildVariant string `bson:"n" json:"variant"` 68 TestName string `bson:"i" json:"test"` 69 TaskName string `bson:"t" json:"task"` 70 TaskId string `bson:"tid" json:"task_id"` 71 } 72 73 // Failures holds failures. 74 type Failures []Failure 75 76 // RevisionFailures holds revision failures 77 type RevisionFailures []RevisionFailure 78 79 // FetchCells returns a Grid of Cells - grouped by variant and display name. 80 // current is the most recent version and from which to fetch prior Cells 81 // going back as far as depth versions. 82 func FetchCells(current version.Version, depth int) (Grid, error) { 83 cells := Grid{} 84 pipeline := []bson.M{ 85 // Stage 1: Get all builds from the current version going back 86 // as far as depth versions. 87 {"$match": bson.M{ 88 build.RequesterKey: evergreen.RepotrackerVersionRequester, 89 build.RevisionOrderNumberKey: bson.M{ 90 "$lte": current.RevisionOrderNumber, 91 "$gte": (current.RevisionOrderNumber - depth), 92 }, 93 build.ProjectKey: current.Identifier, 94 }}, 95 // Stage 2: Sort the builds by the most recently completed. 96 {"$sort": bson.M{ 97 build.RevisionOrderNumberKey: -1, 98 }}, 99 // Stage 3: Project only the relevant fields. 100 {"$project": bson.M{ 101 build.TasksKey: 1, 102 build.RevisionKey: 1, 103 "v": "$" + build.BuildVariantKey, 104 }}, 105 // Stage 4: Flatten the task cache for easier grouping. 106 {"$unwind": "$tasks"}, 107 // Stage 5: Rewrite and project out only the relevant fields. 108 {"$project": bson.M{ 109 "_id": 0, 110 "v": 1, 111 "r": "$" + build.RevisionKey, 112 "d": "$" + build.TasksKey + "." + build.TaskCacheDisplayNameKey, 113 "st": "$" + build.TasksKey + "." + build.TaskCacheStatusKey, 114 "ed": "$" + build.TasksKey + "." + build.TaskCacheStatusDetailsKey, 115 "id": "$" + build.TasksKey + "." + build.TaskCacheIdKey, 116 }}, 117 // Stage 6: Group the tasks by variant and display name. For each group, 118 // add the history - all prior versioned tasks along with their status, 119 // id, and revision identifier. 120 {"$group": bson.M{ 121 "_id": bson.M{ 122 "v": "$v", 123 "d": "$d", 124 }, 125 "h": bson.M{ 126 "$push": bson.M{ 127 "s": "$st", 128 "e": "$ed", 129 "d": "$id", 130 "v": "$r", 131 }, 132 }, 133 }}, 134 } 135 return cells, db.Aggregate(build.Collection, pipeline, &cells) 136 } 137 138 // FetchFailures returns the most recent test failures that have occurred at or 139 // before the current version - looking back as far as depth versions. 140 func FetchFailures(current version.Version, depth int) (Failures, error) { 141 pipeline := []bson.M{ 142 // Stage 1: Get the most recent completed tasks - looking back as far as 143 // depth versions - on this project. 144 {"$match": bson.M{ 145 task.RevisionOrderNumberKey: bson.M{ 146 "$lte": current.RevisionOrderNumber, 147 "$gte": (current.RevisionOrderNumber - depth), 148 }, 149 task.ProjectKey: current.Identifier, 150 task.RequesterKey: evergreen.RepotrackerVersionRequester, 151 task.StatusKey: bson.M{ 152 "$in": []string{ 153 evergreen.TaskFailed, 154 evergreen.TaskSucceeded, 155 }, 156 }, 157 }}, 158 // Stage 2: Sort the tasks by the most recently completed. 159 {"$sort": bson.M{ 160 task.RevisionOrderNumberKey: -1, 161 }}, 162 // Stage 3: Project only relevant fields. 163 {"$project": bson.M{ 164 task.DisplayNameKey: 1, 165 task.BuildVariantKey: 1, 166 task.TestResultsKey: 1, 167 task.IdKey: 1, 168 }}, 169 // Stage 4: Group these tasks by display name and buildvariant - 170 // this returns the most recently completed grouped by task display name 171 // and by variant. We take only the first test results (adding its task 172 // id) for each task/variant group. 173 {"$group": bson.M{ 174 "_id": bson.M{ 175 "t": "$" + task.DisplayNameKey, 176 "v": "$" + task.BuildVariantKey, 177 }, 178 "l": bson.M{ 179 "$first": "$" + task.TestResultsKey, 180 }, 181 "tid": bson.M{ 182 "$first": "$" + task.IdKey, 183 }, 184 }}, 185 // Stage 5: For each group, filter out those task/variant combinations 186 // that don't have at least one test failure in them. 187 {"$match": bson.M{ 188 "l." + task.TestResultStatusKey: evergreen.TestFailedStatus, 189 }}, 190 // Stage 6: Rewrite each task/variant combination from the _id into the 191 // top-level. Project only the test name and status for all tests, and 192 // add a 'status' literal string to each group. This sets up the 193 // documents for redacting in next stage. 194 {"$project": bson.M{ 195 "tid": 1, 196 "t": "$_id.t", 197 "v": "$_id.v", 198 "l." + task.TestResultStatusKey: 1, 199 "l." + task.TestResultTestFileKey: 1, 200 "status": bson.M{ 201 "$literal": evergreen.TestFailedStatus, 202 }, 203 }}, 204 // Stage 7: While each test result contains at least one failed test, 205 // some other tests may have passed. Prune individual tests that did 206 // not fail. 207 {"$redact": bson.M{ 208 "$cond": bson.M{ 209 "if": bson.M{ 210 "$eq": []string{ 211 "$status", evergreen.TestFailedStatus, 212 }, 213 }, 214 "then": "$$DESCEND", 215 "else": "$$PRUNE", 216 }, 217 }}, 218 // Stage 8: We no longer need the status fields so project only fields 219 // we want to return. 220 {"$project": bson.M{ 221 "f": "$l." + task.TestResultTestFileKey, 222 "_id": 0, 223 "tid": 1, 224 "t": 1, 225 "v": 1, 226 }}, 227 // Stage 9: Flatten each failing test so we can group them by all the 228 // variants on which they are failing. 229 {"$unwind": "$f"}, 230 // Stage 10: Group individual test failure. For each, add the variants 231 // it's failing on (and the accompanying task id) and include task's 232 // display name. 233 {"$group": bson.M{ 234 "_id": bson.M{ 235 "t": "$f", 236 "d": "$t", 237 }, 238 "a": bson.M{ 239 "$push": bson.M{ 240 "n": "$v", 241 "i": "$tid", 242 }, 243 }, 244 }}, 245 } 246 failures := Failures{} 247 return failures, db.Aggregate(task.Collection, pipeline, &failures) 248 } 249 250 // FetchRevisionOrderFailures returns the most recent test failures 251 // grouped by revision - looking as far back as depth revisions 252 func FetchRevisionOrderFailures(current version.Version, depth int) (RevisionFailures, error) { 253 pipeline := []bson.M{ 254 // Stage 1: Get the most recent completed tasks - looking back as far as 255 // depth versions - on this project. 256 {"$match": bson.M{ 257 task.RevisionOrderNumberKey: bson.M{ 258 "$lte": current.RevisionOrderNumber, 259 "$gte": (current.RevisionOrderNumber - depth), 260 }, 261 task.ProjectKey: current.Identifier, 262 task.RequesterKey: evergreen.RepotrackerVersionRequester, 263 }}, 264 // Stage 2: Project only relevant fields. 265 {"$project": bson.M{ 266 task.DisplayNameKey: 1, 267 task.RevisionKey: 1, 268 task.BuildVariantKey: 1, 269 task.IdKey: 1, 270 "l": "$" + task.TestResultsKey, 271 }}, 272 // Stage 3: Flatten out the test results 273 {"$unwind": "$l"}, 274 // Stage 4: Take only failed test results 275 {"$match": bson.M{ 276 "l." + task.TestResultStatusKey: evergreen.TestFailedStatus, 277 }}, 278 // Stage 5: Project only relevant fields including just the test file key 279 {"$project": bson.M{ 280 task.RevisionKey: 1, 281 task.BuildVariantKey: 1, 282 task.DisplayNameKey: 1, 283 task.IdKey: 1, 284 "f": "$l." + task.TestResultTestFileKey, 285 }}, 286 // Stage 6: Group by revision. For each one include the 287 // variant name, task name, task id and test name 288 {"$group": bson.M{ 289 "_id": "$" + task.RevisionKey, 290 "a": bson.M{ 291 "$push": bson.M{ 292 "n": "$" + task.BuildVariantKey, 293 "i": "$f", 294 "t": "$" + task.DisplayNameKey, 295 "tid": "$" + task.IdKey, 296 }, 297 }, 298 }}, 299 } 300 taskFailures := RevisionFailures{} 301 return taskFailures, db.Aggregate(task.Collection, pipeline, &taskFailures) 302 }