burndown_test.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. package hercules
  2. import (
  3. "bytes"
  4. "io"
  5. "io/ioutil"
  6. "path"
  7. "testing"
  8. "github.com/gogo/protobuf/proto"
  9. "github.com/stretchr/testify/assert"
  10. "gopkg.in/src-d/go-git.v4/plumbing"
  11. "gopkg.in/src-d/go-git.v4/plumbing/object"
  12. "gopkg.in/src-d/hercules.v3/pb"
  13. )
  14. func TestBurndownMeta(t *testing.T) {
  15. burndown := BurndownAnalysis{}
  16. assert.Equal(t, burndown.Name(), "Burndown")
  17. assert.Equal(t, len(burndown.Provides()), 0)
  18. required := [...]string{"file_diff", "changes", "blob_cache", "day", "author"}
  19. for _, name := range required {
  20. assert.Contains(t, burndown.Requires(), name)
  21. }
  22. opts := burndown.ListConfigurationOptions()
  23. matches := 0
  24. for _, opt := range opts {
  25. switch opt.Name {
  26. case ConfigBurndownGranularity, ConfigBurndownSampling, ConfigBurndownTrackFiles,
  27. ConfigBurndownTrackPeople, ConfigBurndownDebug:
  28. matches++
  29. }
  30. }
  31. assert.Len(t, opts, matches)
  32. assert.Equal(t, burndown.Flag(), "burndown")
  33. }
  34. func TestBurndownConfigure(t *testing.T) {
  35. burndown := BurndownAnalysis{}
  36. facts := map[string]interface{}{}
  37. facts[ConfigBurndownGranularity] = 100
  38. facts[ConfigBurndownSampling] = 200
  39. facts[ConfigBurndownTrackFiles] = true
  40. facts[ConfigBurndownTrackPeople] = true
  41. facts[ConfigBurndownDebug] = true
  42. facts[FactIdentityDetectorPeopleCount] = 5
  43. facts[FactIdentityDetectorReversedPeopleDict] = burndown.Requires()
  44. burndown.Configure(facts)
  45. assert.Equal(t, burndown.Granularity, 100)
  46. assert.Equal(t, burndown.Sampling, 200)
  47. assert.Equal(t, burndown.TrackFiles, true)
  48. assert.Equal(t, burndown.PeopleNumber, 5)
  49. assert.Equal(t, burndown.Debug, true)
  50. assert.Equal(t, burndown.reversedPeopleDict, burndown.Requires())
  51. facts[ConfigBurndownTrackPeople] = false
  52. facts[FactIdentityDetectorPeopleCount] = 50
  53. burndown.Configure(facts)
  54. assert.Equal(t, burndown.PeopleNumber, 0)
  55. facts = map[string]interface{}{}
  56. burndown.Configure(facts)
  57. assert.Equal(t, burndown.Granularity, 100)
  58. assert.Equal(t, burndown.Sampling, 200)
  59. assert.Equal(t, burndown.TrackFiles, true)
  60. assert.Equal(t, burndown.PeopleNumber, 0)
  61. assert.Equal(t, burndown.Debug, true)
  62. assert.Equal(t, burndown.reversedPeopleDict, burndown.Requires())
  63. }
  64. func TestBurndownRegistration(t *testing.T) {
  65. tp, exists := Registry.registered[(&BurndownAnalysis{}).Name()]
  66. assert.True(t, exists)
  67. assert.Equal(t, tp.Elem().Name(), "BurndownAnalysis")
  68. tp, exists = Registry.flags[(&BurndownAnalysis{}).Flag()]
  69. assert.True(t, exists)
  70. assert.Equal(t, tp.Elem().Name(), "BurndownAnalysis")
  71. }
  72. func TestBurndownInitialize(t *testing.T) {
  73. burndown := BurndownAnalysis{}
  74. burndown.Sampling = -10
  75. burndown.Granularity = DefaultBurndownGranularity
  76. burndown.Initialize(testRepository)
  77. assert.Equal(t, burndown.Sampling, DefaultBurndownGranularity)
  78. assert.Equal(t, burndown.Granularity, DefaultBurndownGranularity)
  79. burndown.Sampling = 0
  80. burndown.Granularity = DefaultBurndownGranularity - 1
  81. burndown.Initialize(testRepository)
  82. assert.Equal(t, burndown.Sampling, DefaultBurndownGranularity-1)
  83. assert.Equal(t, burndown.Granularity, DefaultBurndownGranularity-1)
  84. burndown.Sampling = DefaultBurndownGranularity - 1
  85. burndown.Granularity = -10
  86. burndown.Initialize(testRepository)
  87. assert.Equal(t, burndown.Sampling, DefaultBurndownGranularity-1)
  88. assert.Equal(t, burndown.Granularity, DefaultBurndownGranularity)
  89. }
  90. func TestBurndownConsumeFinalize(t *testing.T) {
  91. burndown := BurndownAnalysis{
  92. Granularity: 30,
  93. Sampling: 30,
  94. PeopleNumber: 2,
  95. TrackFiles: true,
  96. }
  97. burndown.Initialize(testRepository)
  98. deps := map[string]interface{}{}
  99. // stage 1
  100. deps["author"] = 0
  101. deps["day"] = 0
  102. cache := map[plumbing.Hash]*object.Blob{}
  103. hash := plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe")
  104. cache[hash], _ = testRepository.BlobObject(hash)
  105. hash = plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9")
  106. cache[hash], _ = testRepository.BlobObject(hash)
  107. hash = plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1")
  108. cache[hash], _ = testRepository.BlobObject(hash)
  109. hash = plumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9")
  110. cache[hash], _ = testRepository.BlobObject(hash)
  111. deps["blob_cache"] = cache
  112. changes := make(object.Changes, 3)
  113. treeFrom, _ := testRepository.TreeObject(plumbing.NewHash(
  114. "a1eb2ea76eb7f9bfbde9b243861474421000eb96"))
  115. treeTo, _ := testRepository.TreeObject(plumbing.NewHash(
  116. "994eac1cd07235bb9815e547a75c84265dea00f5"))
  117. changes[0] = &object.Change{From: object.ChangeEntry{
  118. Name: "analyser.go",
  119. Tree: treeFrom,
  120. TreeEntry: object.TreeEntry{
  121. Name: "analyser.go",
  122. Mode: 0100644,
  123. Hash: plumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9"),
  124. },
  125. }, To: object.ChangeEntry{
  126. Name: "analyser.go",
  127. Tree: treeTo,
  128. TreeEntry: object.TreeEntry{
  129. Name: "analyser.go",
  130. Mode: 0100644,
  131. Hash: plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"),
  132. },
  133. }}
  134. changes[1] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{
  135. Name: "cmd/hercules/main.go",
  136. Tree: treeTo,
  137. TreeEntry: object.TreeEntry{
  138. Name: "cmd/hercules/main.go",
  139. Mode: 0100644,
  140. Hash: plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"),
  141. },
  142. },
  143. }
  144. changes[2] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{
  145. Name: ".travis.yml",
  146. Tree: treeTo,
  147. TreeEntry: object.TreeEntry{
  148. Name: ".travis.yml",
  149. Mode: 0100644,
  150. Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"),
  151. },
  152. },
  153. }
  154. deps["changes"] = changes
  155. fd := fixtureFileDiff()
  156. result, err := fd.Consume(deps)
  157. assert.Nil(t, err)
  158. deps["file_diff"] = result["file_diff"]
  159. result, err = burndown.Consume(deps)
  160. assert.Nil(t, result)
  161. assert.Nil(t, err)
  162. assert.Equal(t, burndown.previousDay, 0)
  163. assert.Equal(t, len(burndown.files), 3)
  164. assert.Equal(t, burndown.files["cmd/hercules/main.go"].Len(), 207)
  165. assert.Equal(t, burndown.files["analyser.go"].Len(), 926)
  166. assert.Equal(t, burndown.files[".travis.yml"].Len(), 12)
  167. assert.Equal(t, len(burndown.people), 2)
  168. assert.Equal(t, burndown.people[0][0], int64(12+207+926))
  169. assert.Equal(t, len(burndown.globalStatus), 1)
  170. assert.Equal(t, burndown.globalStatus[0], int64(12+207+926))
  171. assert.Equal(t, len(burndown.globalHistory), 0)
  172. assert.Equal(t, len(burndown.fileHistories), 0)
  173. burndown2 := BurndownAnalysis{
  174. Granularity: 30,
  175. Sampling: 0,
  176. }
  177. burndown2.Initialize(testRepository)
  178. _, err = burndown2.Consume(deps)
  179. assert.Nil(t, err)
  180. assert.Equal(t, len(burndown2.people), 0)
  181. assert.Equal(t, len(burndown2.peopleHistories), 0)
  182. assert.Equal(t, len(burndown2.fileHistories), 0)
  183. // stage 2
  184. // 2b1ed978194a94edeabbca6de7ff3b5771d4d665
  185. deps["author"] = 1
  186. deps["day"] = 30
  187. cache = map[plumbing.Hash]*object.Blob{}
  188. hash = plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe")
  189. cache[hash], _ = testRepository.BlobObject(hash)
  190. hash = plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1")
  191. cache[hash], _ = testRepository.BlobObject(hash)
  192. hash = plumbing.NewHash("29c9fafd6a2fae8cd20298c3f60115bc31a4c0f2")
  193. cache[hash], _ = testRepository.BlobObject(hash)
  194. hash = plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9")
  195. cache[hash], _ = testRepository.BlobObject(hash)
  196. hash = plumbing.NewHash("f7d918ec500e2f925ecde79b51cc007bac27de72")
  197. cache[hash], _ = testRepository.BlobObject(hash)
  198. deps["blob_cache"] = cache
  199. changes = make(object.Changes, 3)
  200. treeFrom, _ = testRepository.TreeObject(plumbing.NewHash(
  201. "96c6ece9b2f3c7c51b83516400d278dea5605100"))
  202. treeTo, _ = testRepository.TreeObject(plumbing.NewHash(
  203. "251f2094d7b523d5bcc60e663b6cf38151bf8844"))
  204. changes[0] = &object.Change{From: object.ChangeEntry{
  205. Name: "analyser.go",
  206. Tree: treeFrom,
  207. TreeEntry: object.TreeEntry{
  208. Name: "analyser.go",
  209. Mode: 0100644,
  210. Hash: plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"),
  211. },
  212. }, To: object.ChangeEntry{
  213. Name: "burndown.go",
  214. Tree: treeTo,
  215. TreeEntry: object.TreeEntry{
  216. Name: "burndown.go",
  217. Mode: 0100644,
  218. Hash: plumbing.NewHash("29c9fafd6a2fae8cd20298c3f60115bc31a4c0f2"),
  219. },
  220. },
  221. }
  222. changes[1] = &object.Change{From: object.ChangeEntry{
  223. Name: "cmd/hercules/main.go",
  224. Tree: treeFrom,
  225. TreeEntry: object.TreeEntry{
  226. Name: "cmd/hercules/main.go",
  227. Mode: 0100644,
  228. Hash: plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"),
  229. },
  230. }, To: object.ChangeEntry{
  231. Name: "cmd/hercules/main.go",
  232. Tree: treeTo,
  233. TreeEntry: object.TreeEntry{
  234. Name: "cmd/hercules/main.go",
  235. Mode: 0100644,
  236. Hash: plumbing.NewHash("f7d918ec500e2f925ecde79b51cc007bac27de72"),
  237. },
  238. },
  239. }
  240. changes[2] = &object.Change{From: object.ChangeEntry{
  241. Name: ".travis.yml",
  242. Tree: treeTo,
  243. TreeEntry: object.TreeEntry{
  244. Name: ".travis.yml",
  245. Mode: 0100644,
  246. Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"),
  247. },
  248. }, To: object.ChangeEntry{},
  249. }
  250. deps["changes"] = changes
  251. fd = fixtureFileDiff()
  252. result, err = fd.Consume(deps)
  253. assert.Nil(t, err)
  254. deps["file_diff"] = result["file_diff"]
  255. result, err = burndown.Consume(deps)
  256. assert.Nil(t, result)
  257. assert.Nil(t, err)
  258. assert.Equal(t, burndown.previousDay, 30)
  259. assert.Equal(t, len(burndown.files), 2)
  260. assert.Equal(t, burndown.files["cmd/hercules/main.go"].Len(), 290)
  261. assert.Equal(t, burndown.files["burndown.go"].Len(), 543)
  262. assert.Equal(t, len(burndown.people), 2)
  263. assert.Equal(t, len(burndown.globalStatus), 2)
  264. assert.Equal(t, burndown.globalStatus[0], int64(464))
  265. assert.Equal(t, burndown.globalStatus[1], int64(0))
  266. assert.Equal(t, len(burndown.globalHistory), 1)
  267. assert.Equal(t, len(burndown.globalHistory[0]), 2)
  268. assert.Equal(t, len(burndown.fileHistories), 3)
  269. out := burndown.Finalize().(BurndownResult)
  270. /*
  271. GlobalHistory [][]int64
  272. FileHistories map[string][][]int64
  273. PeopleHistories [][][]int64
  274. PeopleMatrix [][]int64
  275. */
  276. assert.Equal(t, len(out.GlobalHistory), 2)
  277. for i := 0; i < 2; i++ {
  278. assert.Equal(t, len(out.GlobalHistory[i]), 2)
  279. }
  280. assert.Equal(t, len(out.GlobalHistory), 2)
  281. assert.Equal(t, out.GlobalHistory[0][0], int64(1145))
  282. assert.Equal(t, out.GlobalHistory[0][1], int64(0))
  283. assert.Equal(t, out.GlobalHistory[1][0], int64(464))
  284. assert.Equal(t, out.GlobalHistory[1][1], int64(369))
  285. assert.Equal(t, len(out.FileHistories), 2)
  286. assert.Equal(t, len(out.FileHistories["cmd/hercules/main.go"]), 2)
  287. assert.Equal(t, len(out.FileHistories["burndown.go"]), 2)
  288. assert.Equal(t, len(out.FileHistories["cmd/hercules/main.go"][0]), 2)
  289. assert.Equal(t, len(out.FileHistories["burndown.go"][0]), 2)
  290. assert.Equal(t, len(out.PeopleMatrix), 2)
  291. assert.Equal(t, len(out.PeopleMatrix[0]), 4)
  292. assert.Equal(t, len(out.PeopleMatrix[1]), 4)
  293. assert.Equal(t, out.PeopleMatrix[0][0], int64(1145))
  294. assert.Equal(t, out.PeopleMatrix[0][1], int64(0))
  295. assert.Equal(t, out.PeopleMatrix[0][2], int64(0))
  296. assert.Equal(t, out.PeopleMatrix[0][3], int64(-681))
  297. assert.Equal(t, out.PeopleMatrix[1][0], int64(369))
  298. assert.Equal(t, out.PeopleMatrix[1][1], int64(0))
  299. assert.Equal(t, out.PeopleMatrix[1][2], int64(0))
  300. assert.Equal(t, out.PeopleMatrix[1][3], int64(0))
  301. assert.Equal(t, len(out.PeopleHistories), 2)
  302. for i := 0; i < 2; i++ {
  303. assert.Equal(t, len(out.PeopleHistories[i]), 2)
  304. assert.Equal(t, len(out.PeopleHistories[i][0]), 2)
  305. assert.Equal(t, len(out.PeopleHistories[i][1]), 2)
  306. }
  307. }
  308. func TestBurndownAnalysisSerialize(t *testing.T) {
  309. burndown := BurndownAnalysis{
  310. Granularity: 30,
  311. Sampling: 30,
  312. PeopleNumber: 2,
  313. TrackFiles: true,
  314. }
  315. burndown.Initialize(testRepository)
  316. deps := map[string]interface{}{}
  317. // stage 1
  318. deps["author"] = 0
  319. deps["day"] = 0
  320. cache := map[plumbing.Hash]*object.Blob{}
  321. hash := plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe")
  322. cache[hash], _ = testRepository.BlobObject(hash)
  323. hash = plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9")
  324. cache[hash], _ = testRepository.BlobObject(hash)
  325. hash = plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1")
  326. cache[hash], _ = testRepository.BlobObject(hash)
  327. hash = plumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9")
  328. cache[hash], _ = testRepository.BlobObject(hash)
  329. deps["blob_cache"] = cache
  330. changes := make(object.Changes, 3)
  331. treeFrom, _ := testRepository.TreeObject(plumbing.NewHash(
  332. "a1eb2ea76eb7f9bfbde9b243861474421000eb96"))
  333. treeTo, _ := testRepository.TreeObject(plumbing.NewHash(
  334. "994eac1cd07235bb9815e547a75c84265dea00f5"))
  335. changes[0] = &object.Change{From: object.ChangeEntry{
  336. Name: "analyser.go",
  337. Tree: treeFrom,
  338. TreeEntry: object.TreeEntry{
  339. Name: "analyser.go",
  340. Mode: 0100644,
  341. Hash: plumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9"),
  342. },
  343. }, To: object.ChangeEntry{
  344. Name: "analyser.go",
  345. Tree: treeTo,
  346. TreeEntry: object.TreeEntry{
  347. Name: "analyser.go",
  348. Mode: 0100644,
  349. Hash: plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"),
  350. },
  351. }}
  352. changes[1] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{
  353. Name: "cmd/hercules/main.go",
  354. Tree: treeTo,
  355. TreeEntry: object.TreeEntry{
  356. Name: "cmd/hercules/main.go",
  357. Mode: 0100644,
  358. Hash: plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"),
  359. },
  360. },
  361. }
  362. changes[2] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{
  363. Name: ".travis.yml",
  364. Tree: treeTo,
  365. TreeEntry: object.TreeEntry{
  366. Name: ".travis.yml",
  367. Mode: 0100644,
  368. Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"),
  369. },
  370. },
  371. }
  372. deps["changes"] = changes
  373. fd := fixtureFileDiff()
  374. result, _ := fd.Consume(deps)
  375. deps["file_diff"] = result["file_diff"]
  376. burndown.Consume(deps)
  377. // stage 2
  378. // 2b1ed978194a94edeabbca6de7ff3b5771d4d665
  379. deps["author"] = 1
  380. deps["day"] = 30
  381. cache = map[plumbing.Hash]*object.Blob{}
  382. hash = plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe")
  383. cache[hash], _ = testRepository.BlobObject(hash)
  384. hash = plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1")
  385. cache[hash], _ = testRepository.BlobObject(hash)
  386. hash = plumbing.NewHash("29c9fafd6a2fae8cd20298c3f60115bc31a4c0f2")
  387. cache[hash], _ = testRepository.BlobObject(hash)
  388. hash = plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9")
  389. cache[hash], _ = testRepository.BlobObject(hash)
  390. hash = plumbing.NewHash("f7d918ec500e2f925ecde79b51cc007bac27de72")
  391. cache[hash], _ = testRepository.BlobObject(hash)
  392. deps["blob_cache"] = cache
  393. changes = make(object.Changes, 3)
  394. treeFrom, _ = testRepository.TreeObject(plumbing.NewHash(
  395. "96c6ece9b2f3c7c51b83516400d278dea5605100"))
  396. treeTo, _ = testRepository.TreeObject(plumbing.NewHash(
  397. "251f2094d7b523d5bcc60e663b6cf38151bf8844"))
  398. changes[0] = &object.Change{From: object.ChangeEntry{
  399. Name: "analyser.go",
  400. Tree: treeFrom,
  401. TreeEntry: object.TreeEntry{
  402. Name: "analyser.go",
  403. Mode: 0100644,
  404. Hash: plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"),
  405. },
  406. }, To: object.ChangeEntry{
  407. Name: "burndown.go",
  408. Tree: treeTo,
  409. TreeEntry: object.TreeEntry{
  410. Name: "burndown.go",
  411. Mode: 0100644,
  412. Hash: plumbing.NewHash("29c9fafd6a2fae8cd20298c3f60115bc31a4c0f2"),
  413. },
  414. },
  415. }
  416. changes[1] = &object.Change{From: object.ChangeEntry{
  417. Name: "cmd/hercules/main.go",
  418. Tree: treeFrom,
  419. TreeEntry: object.TreeEntry{
  420. Name: "cmd/hercules/main.go",
  421. Mode: 0100644,
  422. Hash: plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"),
  423. },
  424. }, To: object.ChangeEntry{
  425. Name: "cmd/hercules/main.go",
  426. Tree: treeTo,
  427. TreeEntry: object.TreeEntry{
  428. Name: "cmd/hercules/main.go",
  429. Mode: 0100644,
  430. Hash: plumbing.NewHash("f7d918ec500e2f925ecde79b51cc007bac27de72"),
  431. },
  432. },
  433. }
  434. changes[2] = &object.Change{From: object.ChangeEntry{
  435. Name: ".travis.yml",
  436. Tree: treeTo,
  437. TreeEntry: object.TreeEntry{
  438. Name: ".travis.yml",
  439. Mode: 0100644,
  440. Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"),
  441. },
  442. }, To: object.ChangeEntry{},
  443. }
  444. deps["changes"] = changes
  445. fd = fixtureFileDiff()
  446. result, _ = fd.Consume(deps)
  447. deps["file_diff"] = result["file_diff"]
  448. people := [...]string{"one@srcd", "two@srcd"}
  449. burndown.reversedPeopleDict = people[:]
  450. burndown.Consume(deps)
  451. out := burndown.Finalize().(BurndownResult)
  452. buffer := &bytes.Buffer{}
  453. burndown.Serialize(out, false, buffer)
  454. assert.Equal(t, buffer.String(), ` granularity: 30
  455. sampling: 30
  456. "project": |-
  457. 1145 0
  458. 464 369
  459. files:
  460. "burndown.go": |-
  461. 0 0
  462. 293 250
  463. "cmd/hercules/main.go": |-
  464. 207 0
  465. 171 119
  466. people_sequence:
  467. - "one@srcd"
  468. - "two@srcd"
  469. people:
  470. "one@srcd": |-
  471. 1145 0
  472. 464 0
  473. "two@srcd": |-
  474. 0 0
  475. 0 369
  476. people_interaction: |-
  477. 1145 0 0 -681
  478. 369 0 0 0
  479. `)
  480. buffer = &bytes.Buffer{}
  481. burndown.Serialize(out, true, buffer)
  482. msg := pb.BurndownAnalysisResults{}
  483. proto.Unmarshal(buffer.Bytes(), &msg)
  484. assert.Equal(t, msg.Granularity, int32(30))
  485. assert.Equal(t, msg.Sampling, int32(30))
  486. assert.Equal(t, msg.Project.Name, "project")
  487. assert.Equal(t, msg.Project.NumberOfRows, int32(2))
  488. assert.Equal(t, msg.Project.NumberOfColumns, int32(2))
  489. assert.Len(t, msg.Project.Rows, 2)
  490. assert.Len(t, msg.Project.Rows[0].Columns, 1)
  491. assert.Equal(t, msg.Project.Rows[0].Columns[0], uint32(1145))
  492. assert.Len(t, msg.Project.Rows[1].Columns, 2)
  493. assert.Equal(t, msg.Project.Rows[1].Columns[0], uint32(464))
  494. assert.Equal(t, msg.Project.Rows[1].Columns[1], uint32(369))
  495. assert.Len(t, msg.Files, 2)
  496. assert.Equal(t, msg.Files[0].Name, "burndown.go")
  497. assert.Equal(t, msg.Files[1].Name, "cmd/hercules/main.go")
  498. assert.Len(t, msg.Files[0].Rows, 2)
  499. assert.Len(t, msg.Files[0].Rows[0].Columns, 0)
  500. assert.Len(t, msg.Files[0].Rows[1].Columns, 2)
  501. assert.Equal(t, msg.Files[0].Rows[1].Columns[0], uint32(293))
  502. assert.Equal(t, msg.Files[0].Rows[1].Columns[1], uint32(250))
  503. assert.Len(t, msg.People, 2)
  504. assert.Equal(t, msg.People[0].Name, "one@srcd")
  505. assert.Equal(t, msg.People[1].Name, "two@srcd")
  506. assert.Len(t, msg.People[0].Rows, 2)
  507. assert.Len(t, msg.People[0].Rows[0].Columns, 1)
  508. assert.Len(t, msg.People[0].Rows[1].Columns, 1)
  509. assert.Equal(t, msg.People[0].Rows[0].Columns[0], uint32(1145))
  510. assert.Equal(t, msg.People[0].Rows[1].Columns[0], uint32(464))
  511. assert.Len(t, msg.People[1].Rows, 2)
  512. assert.Len(t, msg.People[1].Rows[0].Columns, 0)
  513. assert.Len(t, msg.People[1].Rows[1].Columns, 2)
  514. assert.Equal(t, msg.People[1].Rows[1].Columns[0], uint32(0))
  515. assert.Equal(t, msg.People[1].Rows[1].Columns[1], uint32(369))
  516. assert.Equal(t, msg.PeopleInteraction.NumberOfRows, int32(2))
  517. assert.Equal(t, msg.PeopleInteraction.NumberOfColumns, int32(4))
  518. data := [...]int64{1145, -681, 369}
  519. assert.Equal(t, msg.PeopleInteraction.Data, data[:])
  520. indices := [...]int32{0, 3, 0}
  521. assert.Equal(t, msg.PeopleInteraction.Indices, indices[:])
  522. indptr := [...]int64{0, 2, 3}
  523. assert.Equal(t, msg.PeopleInteraction.Indptr, indptr[:])
  524. }
  525. type panickingCloser struct {
  526. }
  527. func (c panickingCloser) Close() error {
  528. return io.EOF
  529. }
  530. func TestCheckClose(t *testing.T) {
  531. closer := panickingCloser{}
  532. assert.Panics(t, func() { checkClose(closer) })
  533. }
  534. func TestBurndownAddMatrix(t *testing.T) {
  535. size := 5*3 + 1
  536. daily := make([][]float32, size)
  537. for i := range daily {
  538. daily[i] = make([]float32, size)
  539. }
  540. added := make([][]int64, 5)
  541. for i := range added {
  542. added[i] = make([]int64, 3)
  543. switch i {
  544. case 0:
  545. added[i][0] = 10
  546. case 1:
  547. added[i][0] = 18
  548. added[i][1] = 2
  549. case 2:
  550. added[i][0] = 12
  551. added[i][1] = 14
  552. case 3:
  553. added[i][0] = 10
  554. added[i][1] = 12
  555. added[i][2] = 6
  556. case 4:
  557. added[i][0] = 8
  558. added[i][1] = 9
  559. added[i][2] = 13
  560. }
  561. }
  562. assert.Panics(t, func() {
  563. daily2 := make([][]float32, 16)
  564. for i := range daily2 {
  565. daily2[i] = make([]float32, 15)
  566. }
  567. addBurndownMatrix(added, 5, 3, daily2, 1)
  568. })
  569. assert.Panics(t, func() {
  570. daily2 := make([][]float32, 15)
  571. for i := range daily2 {
  572. daily2[i] = make([]float32, 16)
  573. }
  574. addBurndownMatrix(added, 5, 3, daily2, 1)
  575. })
  576. // yaml.PrintMatrix(os.Stdout, added, 0, "test", true)
  577. /*
  578. "test": |-
  579. 10 0 0
  580. 18 2 0
  581. 12 14 0
  582. 10 12 6
  583. 8 9 13
  584. */
  585. addBurndownMatrix(added, 5, 3, daily, 1)
  586. for i := range daily[0] {
  587. assert.Equal(t, daily[0][i], float32(0))
  588. }
  589. for i := range daily {
  590. assert.Equal(t, daily[i][0], float32(0))
  591. }
  592. /*for _, row := range daily {
  593. fmt.Println(row)
  594. }*/
  595. // check pinned points
  596. for y := 0; y < 5; y++ {
  597. for x := 0; x < 3; x++ {
  598. var sum float32
  599. for i := x * 5; i < (x+1)*5; i++ {
  600. sum += daily[(y+1)*3][i+1]
  601. }
  602. assert.InDelta(t, sum, added[y][x], 0.00001)
  603. }
  604. }
  605. // check overall trend: 0 -> const -> peak -> decay
  606. for x := 0; x < 15; x++ {
  607. for y := 0; y < x; y++ {
  608. assert.Zero(t, daily[y+1][x+1])
  609. }
  610. var prev float32
  611. for y := x; y < ((x+3)/5)*5; y++ {
  612. if prev == 0 {
  613. prev = daily[y+1][x+1]
  614. }
  615. assert.Equal(t, daily[y+1][x+1], prev)
  616. }
  617. for y := ((x + 3) / 5) * 5; y < 15; y++ {
  618. if prev == 0 {
  619. prev = daily[y+1][x+1]
  620. }
  621. assert.True(t, daily[y+1][x+1] <= prev)
  622. prev = daily[y+1][x+1]
  623. }
  624. }
  625. }
  626. func TestBurndownAddMatrixCrazy(t *testing.T) {
  627. size := 5 * 3
  628. daily := make([][]float32, size)
  629. for i := range daily {
  630. daily[i] = make([]float32, size)
  631. }
  632. added := make([][]int64, 5)
  633. for i := range added {
  634. added[i] = make([]int64, 3)
  635. switch i {
  636. case 0:
  637. added[i][0] = 10
  638. case 1:
  639. added[i][0] = 9
  640. added[i][1] = 2
  641. case 2:
  642. added[i][0] = 8
  643. added[i][1] = 16
  644. case 3:
  645. added[i][0] = 7
  646. added[i][1] = 12
  647. added[i][2] = 6
  648. case 4:
  649. added[i][0] = 6
  650. added[i][1] = 9
  651. added[i][2] = 13
  652. }
  653. }
  654. // yaml.PrintMatrix(os.Stdout, added, 0, "test", true)
  655. /*
  656. "test": |-
  657. 10 0 0
  658. 9 2 0
  659. 8 16 0
  660. 7 12 6
  661. 6 9 13
  662. */
  663. addBurndownMatrix(added, 5, 3, daily, 0)
  664. /*for _, row := range daily {
  665. fmt.Println(row)
  666. }*/
  667. // check pinned points
  668. for y := 0; y < 5; y++ {
  669. for x := 0; x < 3; x++ {
  670. var sum float32
  671. for i := x * 5; i < (x+1)*5; i++ {
  672. sum += daily[(y+1)*3-1][i]
  673. }
  674. assert.InDelta(t, sum, added[y][x], 0.00001)
  675. }
  676. }
  677. // check overall trend: 0 -> const -> peak -> decay
  678. for x := 0; x < 15; x++ {
  679. for y := 0; y < x; y++ {
  680. assert.Zero(t, daily[y][x])
  681. }
  682. var prev float32
  683. for y := x; y < ((x+3)/5)*5; y++ {
  684. if prev == 0 {
  685. prev = daily[y][x]
  686. }
  687. assert.Equal(t, daily[y][x], prev)
  688. }
  689. for y := ((x + 3) / 5) * 5; y < 15; y++ {
  690. if prev == 0 {
  691. prev = daily[y][x]
  692. }
  693. assert.True(t, daily[y][x] <= prev)
  694. prev = daily[y][x]
  695. }
  696. }
  697. }
  698. func TestBurndownMergeGlobalHistory(t *testing.T) {
  699. people1 := [...]string{"one", "two"}
  700. res1 := BurndownResult{
  701. GlobalHistory: [][]int64{},
  702. FileHistories: map[string][][]int64{},
  703. PeopleHistories: [][][]int64{},
  704. PeopleMatrix: [][]int64{},
  705. reversedPeopleDict: people1[:],
  706. sampling: 15,
  707. granularity: 20,
  708. }
  709. c1 := CommonAnalysisResult{
  710. BeginTime: 600566400, // 1989 Jan 12
  711. EndTime: 604713600, // 1989 March 1
  712. CommitsNumber: 10,
  713. RunTime: 100000,
  714. }
  715. // 48 days
  716. res1.GlobalHistory = make([][]int64, 48/15+1 /* 4 samples */)
  717. for i := range res1.GlobalHistory {
  718. res1.GlobalHistory[i] = make([]int64, 48/20+1 /* 3 bands */)
  719. switch i {
  720. case 0:
  721. res1.GlobalHistory[i][0] = 1000
  722. case 1:
  723. res1.GlobalHistory[i][0] = 1100
  724. res1.GlobalHistory[i][1] = 400
  725. case 2:
  726. res1.GlobalHistory[i][0] = 900
  727. res1.GlobalHistory[i][1] = 750
  728. res1.GlobalHistory[i][2] = 100
  729. case 3:
  730. res1.GlobalHistory[i][0] = 850
  731. res1.GlobalHistory[i][1] = 700
  732. res1.GlobalHistory[i][2] = 150
  733. }
  734. }
  735. res1.FileHistories["file1"] = res1.GlobalHistory
  736. res1.FileHistories["file2"] = res1.GlobalHistory
  737. res1.PeopleHistories = append(res1.PeopleHistories, res1.GlobalHistory)
  738. res1.PeopleHistories = append(res1.PeopleHistories, res1.GlobalHistory)
  739. res1.PeopleMatrix = append(res1.PeopleMatrix, make([]int64, 4))
  740. res1.PeopleMatrix = append(res1.PeopleMatrix, make([]int64, 4))
  741. res1.PeopleMatrix[0][0] = 10
  742. res1.PeopleMatrix[0][1] = 20
  743. res1.PeopleMatrix[0][2] = 30
  744. res1.PeopleMatrix[0][3] = 40
  745. res1.PeopleMatrix[1][0] = 50
  746. res1.PeopleMatrix[1][1] = 60
  747. res1.PeopleMatrix[1][2] = 70
  748. res1.PeopleMatrix[1][3] = 80
  749. people2 := [...]string{"two", "three"}
  750. res2 := BurndownResult{
  751. GlobalHistory: [][]int64{},
  752. FileHistories: map[string][][]int64{},
  753. PeopleHistories: [][][]int64{},
  754. PeopleMatrix: [][]int64{},
  755. reversedPeopleDict: people2[:],
  756. sampling: 14,
  757. granularity: 19,
  758. }
  759. c2 := CommonAnalysisResult{
  760. BeginTime: 601084800, // 1989 Jan 18
  761. EndTime: 605923200, // 1989 March 15
  762. CommitsNumber: 10,
  763. RunTime: 100000,
  764. }
  765. // 56 days
  766. res2.GlobalHistory = make([][]int64, 56/14 /* 4 samples */)
  767. for i := range res2.GlobalHistory {
  768. res2.GlobalHistory[i] = make([]int64, 56/19+1 /* 3 bands */)
  769. switch i {
  770. case 0:
  771. res2.GlobalHistory[i][0] = 900
  772. case 1:
  773. res2.GlobalHistory[i][0] = 1100
  774. res2.GlobalHistory[i][1] = 400
  775. case 2:
  776. res2.GlobalHistory[i][0] = 900
  777. res2.GlobalHistory[i][1] = 750
  778. res2.GlobalHistory[i][2] = 100
  779. case 3:
  780. res2.GlobalHistory[i][0] = 800
  781. res2.GlobalHistory[i][1] = 600
  782. res2.GlobalHistory[i][2] = 600
  783. }
  784. }
  785. res2.FileHistories["file2"] = res2.GlobalHistory
  786. res2.FileHistories["file3"] = res2.GlobalHistory
  787. res2.PeopleHistories = append(res2.PeopleHistories, res2.GlobalHistory)
  788. res2.PeopleHistories = append(res2.PeopleHistories, res2.GlobalHistory)
  789. res2.PeopleMatrix = append(res2.PeopleMatrix, make([]int64, 4))
  790. res2.PeopleMatrix = append(res2.PeopleMatrix, make([]int64, 4))
  791. res2.PeopleMatrix[0][0] = 100
  792. res2.PeopleMatrix[0][1] = 200
  793. res2.PeopleMatrix[0][2] = 300
  794. res2.PeopleMatrix[0][3] = 400
  795. res2.PeopleMatrix[1][0] = 500
  796. res2.PeopleMatrix[1][1] = 600
  797. res2.PeopleMatrix[1][2] = 700
  798. res2.PeopleMatrix[1][3] = 800
  799. burndown := BurndownAnalysis{}
  800. merged := burndown.MergeResults(res1, res2, &c1, &c2).(BurndownResult)
  801. assert.Equal(t, merged.granularity, 19)
  802. assert.Equal(t, merged.sampling, 14)
  803. assert.Len(t, merged.GlobalHistory, 5)
  804. for _, row := range merged.GlobalHistory {
  805. assert.Len(t, row, 4)
  806. }
  807. assert.Equal(t, merged.FileHistories["file1"], res1.GlobalHistory)
  808. assert.Equal(t, merged.FileHistories["file2"], merged.GlobalHistory)
  809. assert.Equal(t, merged.FileHistories["file3"], res2.GlobalHistory)
  810. assert.Len(t, merged.reversedPeopleDict, 3)
  811. assert.Equal(t, merged.PeopleHistories[0], res1.GlobalHistory)
  812. assert.Equal(t, merged.PeopleHistories[1], merged.GlobalHistory)
  813. assert.Equal(t, merged.PeopleHistories[2], res2.GlobalHistory)
  814. assert.Len(t, merged.PeopleMatrix, 3)
  815. for _, row := range merged.PeopleMatrix {
  816. assert.Len(t, row, 5)
  817. }
  818. assert.Equal(t, merged.PeopleMatrix[0][0], int64(10))
  819. assert.Equal(t, merged.PeopleMatrix[0][1], int64(20))
  820. assert.Equal(t, merged.PeopleMatrix[0][2], int64(30))
  821. assert.Equal(t, merged.PeopleMatrix[0][3], int64(40))
  822. assert.Equal(t, merged.PeopleMatrix[0][4], int64(0))
  823. assert.Equal(t, merged.PeopleMatrix[1][0], int64(150))
  824. assert.Equal(t, merged.PeopleMatrix[1][1], int64(260))
  825. assert.Equal(t, merged.PeopleMatrix[1][2], int64(70))
  826. assert.Equal(t, merged.PeopleMatrix[1][3], int64(380))
  827. assert.Equal(t, merged.PeopleMatrix[1][4], int64(400))
  828. assert.Equal(t, merged.PeopleMatrix[2][0], int64(500))
  829. assert.Equal(t, merged.PeopleMatrix[2][1], int64(600))
  830. assert.Equal(t, merged.PeopleMatrix[2][2], int64(0))
  831. assert.Equal(t, merged.PeopleMatrix[2][3], int64(700))
  832. assert.Equal(t, merged.PeopleMatrix[2][4], int64(800))
  833. burndown.serializeBinary(&merged, ioutil.Discard)
  834. }
  835. func TestBurndownMergeNils(t *testing.T) {
  836. res1 := BurndownResult{
  837. GlobalHistory: [][]int64{},
  838. FileHistories: map[string][][]int64{},
  839. PeopleHistories: [][][]int64{},
  840. PeopleMatrix: [][]int64{},
  841. reversedPeopleDict: []string{},
  842. sampling: 15,
  843. granularity: 20,
  844. }
  845. c1 := CommonAnalysisResult{
  846. BeginTime: 600566400, // 1989 Jan 12
  847. EndTime: 604713600, // 1989 March 1
  848. CommitsNumber: 10,
  849. RunTime: 100000,
  850. }
  851. res2 := BurndownResult{
  852. GlobalHistory: nil,
  853. FileHistories: nil,
  854. PeopleHistories: nil,
  855. PeopleMatrix: nil,
  856. reversedPeopleDict: nil,
  857. sampling: 14,
  858. granularity: 19,
  859. }
  860. c2 := CommonAnalysisResult{
  861. BeginTime: 601084800, // 1989 Jan 18
  862. EndTime: 605923200, // 1989 March 15
  863. CommitsNumber: 10,
  864. RunTime: 100000,
  865. }
  866. burndown := BurndownAnalysis{}
  867. merged := burndown.MergeResults(res1, res2, &c1, &c2).(BurndownResult)
  868. assert.Equal(t, merged.granularity, 19)
  869. assert.Equal(t, merged.sampling, 14)
  870. assert.Nil(t, merged.GlobalHistory)
  871. assert.Nil(t, merged.FileHistories)
  872. assert.Nil(t, merged.PeopleHistories)
  873. assert.Nil(t, merged.PeopleMatrix)
  874. burndown.serializeBinary(&merged, ioutil.Discard)
  875. res2.GlobalHistory = make([][]int64, 56/14 /* 4 samples */)
  876. for i := range res2.GlobalHistory {
  877. res2.GlobalHistory[i] = make([]int64, 56/19+1 /* 3 bands */)
  878. switch i {
  879. case 0:
  880. res2.GlobalHistory[i][0] = 900
  881. case 1:
  882. res2.GlobalHistory[i][0] = 1100
  883. res2.GlobalHistory[i][1] = 400
  884. case 2:
  885. res2.GlobalHistory[i][0] = 900
  886. res2.GlobalHistory[i][1] = 750
  887. res2.GlobalHistory[i][2] = 100
  888. case 3:
  889. res2.GlobalHistory[i][0] = 800
  890. res2.GlobalHistory[i][1] = 600
  891. res2.GlobalHistory[i][2] = 600
  892. }
  893. }
  894. people1 := [...]string{"one", "two"}
  895. res1.reversedPeopleDict = people1[:]
  896. res1.PeopleMatrix = append(res1.PeopleMatrix, make([]int64, 4))
  897. res1.PeopleMatrix = append(res1.PeopleMatrix, make([]int64, 4))
  898. res1.PeopleMatrix[0][0] = 10
  899. res1.PeopleMatrix[0][1] = 20
  900. res1.PeopleMatrix[0][2] = 30
  901. res1.PeopleMatrix[0][3] = 40
  902. res1.PeopleMatrix[1][0] = 50
  903. res1.PeopleMatrix[1][1] = 60
  904. res1.PeopleMatrix[1][2] = 70
  905. res1.PeopleMatrix[1][3] = 80
  906. people2 := [...]string{"two", "three"}
  907. res2.reversedPeopleDict = people2[:]
  908. merged = burndown.MergeResults(res1, res2, &c1, &c2).(BurndownResult)
  909. mgh := [5][4]int64{
  910. {0, 0, 0, 0},
  911. {578, 0, 0, 0},
  912. {798, 546, 0, 0},
  913. {664, 884, 222, 0},
  914. {547, 663, 610, 178},
  915. }
  916. mgh2 := [...][]int64{
  917. mgh[0][:], mgh[1][:], mgh[2][:], mgh[3][:], mgh[4][:],
  918. }
  919. mgh3 := mgh2[:]
  920. assert.Equal(t, mgh3, merged.GlobalHistory)
  921. assert.Len(t, merged.PeopleMatrix, 3)
  922. for _, row := range merged.PeopleMatrix {
  923. assert.Len(t, row, 5)
  924. }
  925. assert.Equal(t, merged.PeopleMatrix[0][0], int64(10))
  926. assert.Equal(t, merged.PeopleMatrix[0][1], int64(20))
  927. assert.Equal(t, merged.PeopleMatrix[0][2], int64(30))
  928. assert.Equal(t, merged.PeopleMatrix[0][3], int64(40))
  929. assert.Equal(t, merged.PeopleMatrix[0][4], int64(0))
  930. assert.Equal(t, merged.PeopleMatrix[1][0], int64(50))
  931. assert.Equal(t, merged.PeopleMatrix[1][1], int64(60))
  932. assert.Equal(t, merged.PeopleMatrix[1][2], int64(70))
  933. assert.Equal(t, merged.PeopleMatrix[1][3], int64(80))
  934. assert.Equal(t, merged.PeopleMatrix[1][4], int64(0))
  935. assert.Equal(t, merged.PeopleMatrix[2][0], int64(0))
  936. assert.Equal(t, merged.PeopleMatrix[2][1], int64(0))
  937. assert.Equal(t, merged.PeopleMatrix[2][2], int64(0))
  938. assert.Equal(t, merged.PeopleMatrix[2][3], int64(0))
  939. assert.Equal(t, merged.PeopleMatrix[2][4], int64(0))
  940. burndown.serializeBinary(&merged, ioutil.Discard)
  941. }
  942. func TestBurndownDeserialize(t *testing.T) {
  943. allBuffer, err := ioutil.ReadFile(path.Join("test_data", "burndown.pb"))
  944. assert.Nil(t, err)
  945. message := pb.AnalysisResults{}
  946. err = proto.Unmarshal(allBuffer, &message)
  947. assert.Nil(t, err)
  948. burndown := BurndownAnalysis{}
  949. iresult, err := burndown.Deserialize(message.Contents[burndown.Name()])
  950. assert.Nil(t, err)
  951. result := iresult.(BurndownResult)
  952. assert.True(t, len(result.GlobalHistory) > 0)
  953. assert.True(t, len(result.FileHistories) > 0)
  954. assert.True(t, len(result.reversedPeopleDict) > 0)
  955. assert.True(t, len(result.PeopleHistories) > 0)
  956. assert.True(t, len(result.PeopleMatrix) > 0)
  957. assert.Equal(t, result.granularity, 30)
  958. assert.Equal(t, result.sampling, 30)
  959. }