pipeline_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. package core
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "testing"
  10. "time"
  11. "github.com/stretchr/testify/assert"
  12. "gopkg.in/src-d/go-git.v4"
  13. "gopkg.in/src-d/go-git.v4/plumbing"
  14. "gopkg.in/src-d/go-git.v4/plumbing/object"
  15. "gopkg.in/src-d/hercules.v6/internal/pb"
  16. "gopkg.in/src-d/hercules.v6/internal/test"
  17. )
  18. type testPipelineItem struct {
  19. Initialized bool
  20. DepsConsumed bool
  21. Forked bool
  22. Merged *bool
  23. CommitMatches bool
  24. IndexMatches bool
  25. MergeState *int
  26. TestError bool
  27. ConfigureRaises bool
  28. InitializeRaises bool
  29. }
  30. func (item *testPipelineItem) Name() string {
  31. return "Test"
  32. }
  33. func (item *testPipelineItem) Provides() []string {
  34. arr := [...]string{"test"}
  35. return arr[:]
  36. }
  37. func (item *testPipelineItem) Requires() []string {
  38. return []string{}
  39. }
  40. func (item *testPipelineItem) Configure(facts map[string]interface{}) error {
  41. if item.ConfigureRaises {
  42. return errors.New("test1")
  43. }
  44. return nil
  45. }
  46. func (item *testPipelineItem) ListConfigurationOptions() []ConfigurationOption {
  47. options := [...]ConfigurationOption{{
  48. Name: "TestOption",
  49. Description: "The option description.",
  50. Flag: "test-option",
  51. Type: IntConfigurationOption,
  52. Default: 10,
  53. }}
  54. return options[:]
  55. }
  56. func (item *testPipelineItem) Flag() string {
  57. return "mytest"
  58. }
  59. func (item *testPipelineItem) Description() string {
  60. return "description!"
  61. }
  62. func (item *testPipelineItem) Features() []string {
  63. f := [...]string{"power"}
  64. return f[:]
  65. }
  66. func (item *testPipelineItem) Initialize(repository *git.Repository) error {
  67. item.Initialized = repository != nil
  68. item.Merged = new(bool)
  69. item.MergeState = new(int)
  70. if item.InitializeRaises {
  71. return errors.New("test2")
  72. }
  73. return nil
  74. }
  75. func (item *testPipelineItem) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  76. if item.TestError {
  77. return nil, errors.New("error")
  78. }
  79. obj, exists := deps[DependencyCommit]
  80. item.DepsConsumed = exists
  81. if item.DepsConsumed {
  82. commit := obj.(*object.Commit)
  83. item.CommitMatches = commit.Hash == plumbing.NewHash(
  84. "af9ddc0db70f09f3f27b4b98e415592a7485171c")
  85. obj, item.DepsConsumed = deps[DependencyIndex]
  86. if item.DepsConsumed {
  87. item.IndexMatches = obj.(int) == 0
  88. }
  89. }
  90. obj, exists = deps[DependencyIsMerge]
  91. if exists {
  92. *item.MergeState++
  93. if obj.(bool) {
  94. *item.MergeState++
  95. }
  96. }
  97. return map[string]interface{}{"test": item}, nil
  98. }
  99. func (item *testPipelineItem) Fork(n int) []PipelineItem {
  100. result := make([]PipelineItem, n)
  101. for i := 0; i < n; i++ {
  102. result[i] = &testPipelineItem{Merged: item.Merged, MergeState: item.MergeState}
  103. }
  104. item.Forked = true
  105. return result
  106. }
  107. func (item *testPipelineItem) Merge(branches []PipelineItem) {
  108. *item.Merged = true
  109. }
  110. func (item *testPipelineItem) Finalize() interface{} {
  111. return item
  112. }
  113. func (item *testPipelineItem) Serialize(result interface{}, binary bool, writer io.Writer) error {
  114. return nil
  115. }
  116. type dependingTestPipelineItem struct {
  117. DependencySatisfied bool
  118. TestNilConsumeReturn bool
  119. Hibernated bool
  120. Booted bool
  121. RaiseHibernateError bool
  122. RaiseBootError bool
  123. }
  124. func (item *dependingTestPipelineItem) Name() string {
  125. return "Test2"
  126. }
  127. func (item *dependingTestPipelineItem) Provides() []string {
  128. arr := [...]string{"test2"}
  129. return arr[:]
  130. }
  131. func (item *dependingTestPipelineItem) Requires() []string {
  132. arr := [...]string{"test"}
  133. return arr[:]
  134. }
  135. func (item *dependingTestPipelineItem) ListConfigurationOptions() []ConfigurationOption {
  136. options := [...]ConfigurationOption{{
  137. Name: "TestOption2",
  138. Description: "The option description.",
  139. Flag: "test-option2",
  140. Type: IntConfigurationOption,
  141. Default: 10,
  142. }}
  143. return options[:]
  144. }
  145. func (item *dependingTestPipelineItem) Configure(facts map[string]interface{}) error {
  146. return nil
  147. }
  148. func (item *dependingTestPipelineItem) Initialize(repository *git.Repository) error {
  149. return nil
  150. }
  151. func (item *dependingTestPipelineItem) Flag() string {
  152. return "depflag"
  153. }
  154. func (item *dependingTestPipelineItem) Description() string {
  155. return "another description"
  156. }
  157. func (item *dependingTestPipelineItem) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  158. _, exists := deps["test"]
  159. item.DependencySatisfied = exists
  160. if !item.TestNilConsumeReturn {
  161. return map[string]interface{}{"test2": item}, nil
  162. }
  163. return nil, nil
  164. }
  165. func (item *dependingTestPipelineItem) Fork(n int) []PipelineItem {
  166. clones := make([]PipelineItem, n)
  167. for i := range clones {
  168. clones[i] = item
  169. }
  170. return clones
  171. }
  172. func (item *dependingTestPipelineItem) Merge(branches []PipelineItem) {
  173. }
  174. func (item *dependingTestPipelineItem) Hibernate() error {
  175. item.Hibernated = true
  176. if item.RaiseHibernateError {
  177. return errors.New("error")
  178. }
  179. return nil
  180. }
  181. func (item *dependingTestPipelineItem) Boot() error {
  182. item.Booted = true
  183. if item.RaiseBootError {
  184. return errors.New("error")
  185. }
  186. return nil
  187. }
  188. func (item *dependingTestPipelineItem) Finalize() interface{} {
  189. return true
  190. }
  191. func (item *dependingTestPipelineItem) Serialize(result interface{}, binary bool, writer io.Writer) error {
  192. return nil
  193. }
  194. func TestPipelineFacts(t *testing.T) {
  195. pipeline := NewPipeline(test.Repository)
  196. pipeline.SetFact("fact", "value")
  197. assert.Equal(t, pipeline.GetFact("fact"), "value")
  198. }
  199. func TestPipelineFeatures(t *testing.T) {
  200. pipeline := NewPipeline(test.Repository)
  201. pipeline.SetFeature("feat")
  202. val, _ := pipeline.GetFeature("feat")
  203. assert.True(t, val)
  204. _, exists := pipeline.GetFeature("!")
  205. assert.False(t, exists)
  206. Registry.featureFlags.Set("777")
  207. defer func() {
  208. Registry.featureFlags = arrayFeatureFlags{Flags: []string{}, Choices: map[string]bool{}}
  209. }()
  210. pipeline.SetFeaturesFromFlags()
  211. _, exists = pipeline.GetFeature("777")
  212. assert.False(t, exists)
  213. assert.Panics(t, func() {
  214. pipeline.SetFeaturesFromFlags(
  215. &PipelineItemRegistry{}, &PipelineItemRegistry{})
  216. })
  217. }
  218. func TestPipelineErrors(t *testing.T) {
  219. pipeline := NewPipeline(test.Repository)
  220. pipeline.SetFact("fact", "value")
  221. assert.Equal(t, pipeline.GetFact("fact"), "value")
  222. item := &testPipelineItem{}
  223. pipeline.AddItem(item)
  224. item.ConfigureRaises = true
  225. err := pipeline.Initialize(map[string]interface{}{})
  226. assert.NotNil(t, err)
  227. assert.Contains(t, err.Error(), "configure")
  228. assert.Contains(t, err.Error(), "test1")
  229. item.ConfigureRaises = false
  230. item.InitializeRaises = true
  231. err = pipeline.Initialize(map[string]interface{}{})
  232. assert.NotNil(t, err)
  233. assert.Contains(t, err.Error(), "initialize")
  234. assert.Contains(t, err.Error(), "test2")
  235. }
  236. func TestPipelineRun(t *testing.T) {
  237. pipeline := NewPipeline(test.Repository)
  238. item := &testPipelineItem{}
  239. pipeline.AddItem(item)
  240. assert.Nil(t, pipeline.Initialize(map[string]interface{}{}))
  241. assert.True(t, item.Initialized)
  242. commits := make([]*object.Commit, 1)
  243. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  244. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  245. result, err := pipeline.Run(commits)
  246. assert.Nil(t, err)
  247. assert.Equal(t, 2, len(result))
  248. assert.Equal(t, item, result[item].(*testPipelineItem))
  249. common := result[nil].(*CommonAnalysisResult)
  250. assert.Equal(t, common.BeginTime, int64(1481719198))
  251. assert.Equal(t, common.EndTime, int64(1481719198))
  252. assert.Equal(t, common.CommitsNumber, 1)
  253. assert.True(t, common.RunTime.Nanoseconds()/1e6 < 100)
  254. assert.Len(t, common.RunTimePerItem, 1)
  255. for key, val := range common.RunTimePerItem {
  256. assert.True(t, val >= 0, key)
  257. }
  258. assert.True(t, item.DepsConsumed)
  259. assert.True(t, item.CommitMatches)
  260. assert.True(t, item.IndexMatches)
  261. assert.Equal(t, 1, *item.MergeState)
  262. assert.True(t, item.Forked)
  263. assert.False(t, *item.Merged)
  264. pipeline.RemoveItem(item)
  265. result, err = pipeline.Run(commits)
  266. assert.Nil(t, err)
  267. assert.Equal(t, 1, len(result))
  268. }
  269. func TestPipelineRunBranches(t *testing.T) {
  270. pipeline := NewPipeline(test.Repository)
  271. item := &testPipelineItem{}
  272. pipeline.AddItem(item)
  273. pipeline.Initialize(map[string]interface{}{})
  274. assert.True(t, item.Initialized)
  275. hashes := []string{
  276. "6db8065cdb9bb0758f36a7e75fc72ab95f9e8145",
  277. "f30daba81ff2bf0b3ba02a1e1441e74f8a4f6fee",
  278. "8a03b5620b1caa72ec9cb847ea88332621e2950a",
  279. "dd9dd084d5851d7dc4399fc7dbf3d8292831ebc5",
  280. "f4ed0405b14f006c0744029d87ddb3245607587a",
  281. }
  282. commits := make([]*object.Commit, len(hashes))
  283. for i, h := range hashes {
  284. var err error
  285. commits[i], err = test.Repository.CommitObject(plumbing.NewHash(h))
  286. if err != nil {
  287. t.Fatal(err)
  288. }
  289. }
  290. result, err := pipeline.Run(commits)
  291. assert.Nil(t, err)
  292. assert.True(t, item.Forked)
  293. assert.True(t, *item.Merged)
  294. assert.Equal(t, 2, len(result))
  295. assert.Equal(t, item, result[item].(*testPipelineItem))
  296. common := result[nil].(*CommonAnalysisResult)
  297. assert.Equal(t, common.CommitsNumber, 5)
  298. assert.Equal(t, *item.MergeState, 8)
  299. }
  300. func TestPipelineOnProgress(t *testing.T) {
  301. pipeline := NewPipeline(test.Repository)
  302. progressOk := 0
  303. onProgress := func(step int, total int) {
  304. if step == 1 && total == 4 {
  305. progressOk++
  306. }
  307. if step == 2 && total == 4 {
  308. progressOk++
  309. }
  310. if step == 3 && total == 4 {
  311. progressOk++
  312. }
  313. if step == 4 && total == 4 {
  314. progressOk++
  315. }
  316. }
  317. pipeline.OnProgress = onProgress
  318. commits := make([]*object.Commit, 1)
  319. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  320. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  321. result, err := pipeline.Run(commits)
  322. assert.Nil(t, err)
  323. assert.Equal(t, 1, len(result))
  324. assert.Equal(t, 4, progressOk)
  325. }
  326. func TestPipelineCommitsFull(t *testing.T) {
  327. pipeline := NewPipeline(test.Repository)
  328. commits, err := pipeline.Commits(false)
  329. assert.Nil(t, err)
  330. assert.True(t, len(commits) >= 100)
  331. hashMap := map[plumbing.Hash]bool{}
  332. for _, c := range commits {
  333. hashMap[c.Hash] = true
  334. }
  335. assert.Equal(t, len(commits), len(hashMap))
  336. assert.Contains(t, hashMap, plumbing.NewHash(
  337. "cce947b98a050c6d356bc6ba95030254914027b1"))
  338. assert.Contains(t, hashMap, plumbing.NewHash(
  339. "a3ee37f91f0d705ec9c41ae88426f0ae44b2fbc3"))
  340. }
  341. func TestPipelineCommitsFirstParent(t *testing.T) {
  342. pipeline := NewPipeline(test.Repository)
  343. commits, err := pipeline.Commits(true)
  344. assert.Nil(t, err)
  345. assert.True(t, len(commits) >= 100)
  346. hashMap := map[plumbing.Hash]bool{}
  347. for _, c := range commits {
  348. hashMap[c.Hash] = true
  349. }
  350. assert.Equal(t, len(commits), len(hashMap))
  351. assert.Contains(t, hashMap, plumbing.NewHash(
  352. "cce947b98a050c6d356bc6ba95030254914027b1"))
  353. assert.NotContains(t, hashMap, plumbing.NewHash(
  354. "a3ee37f91f0d705ec9c41ae88426f0ae44b2fbc3"))
  355. }
  356. func TestLoadCommitsFromFile(t *testing.T) {
  357. tmp, err := ioutil.TempFile("", "hercules-test-")
  358. assert.Nil(t, err)
  359. tmp.WriteString("cce947b98a050c6d356bc6ba95030254914027b1\n6db8065cdb9bb0758f36a7e75fc72ab95f9e8145")
  360. tmp.Close()
  361. defer os.Remove(tmp.Name())
  362. commits, err := LoadCommitsFromFile(tmp.Name(), test.Repository)
  363. assert.Nil(t, err)
  364. assert.Equal(t, len(commits), 2)
  365. assert.Equal(t, commits[0].Hash, plumbing.NewHash(
  366. "cce947b98a050c6d356bc6ba95030254914027b1"))
  367. assert.Equal(t, commits[1].Hash, plumbing.NewHash(
  368. "6db8065cdb9bb0758f36a7e75fc72ab95f9e8145"))
  369. commits, err = LoadCommitsFromFile("/WAT?xxx!", test.Repository)
  370. assert.Nil(t, commits)
  371. assert.NotNil(t, err)
  372. tmp, err = ioutil.TempFile("", "hercules-test-")
  373. assert.Nil(t, err)
  374. tmp.WriteString("WAT")
  375. tmp.Close()
  376. defer os.Remove(tmp.Name())
  377. commits, err = LoadCommitsFromFile(tmp.Name(), test.Repository)
  378. assert.Nil(t, commits)
  379. assert.NotNil(t, err)
  380. tmp, err = ioutil.TempFile("", "hercules-test-")
  381. assert.Nil(t, err)
  382. tmp.WriteString("ffffffffffffffffffffffffffffffffffffffff")
  383. tmp.Close()
  384. defer os.Remove(tmp.Name())
  385. commits, err = LoadCommitsFromFile(tmp.Name(), test.Repository)
  386. assert.Nil(t, commits)
  387. assert.NotNil(t, err)
  388. }
  389. func TestPipelineDeps(t *testing.T) {
  390. pipeline := NewPipeline(test.Repository)
  391. item1 := &dependingTestPipelineItem{}
  392. item2 := &testPipelineItem{}
  393. pipeline.AddItem(item1)
  394. pipeline.AddItem(item2)
  395. assert.Equal(t, pipeline.Len(), 2)
  396. pipeline.Initialize(map[string]interface{}{})
  397. commits := make([]*object.Commit, 1)
  398. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  399. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  400. result, err := pipeline.Run(commits)
  401. assert.Nil(t, err)
  402. assert.True(t, result[item1].(bool))
  403. assert.Equal(t, result[item2], item2)
  404. item1.TestNilConsumeReturn = true
  405. assert.Panics(t, func() { pipeline.Run(commits) })
  406. }
  407. func TestPipelineDeployFeatures(t *testing.T) {
  408. pipeline := NewPipeline(test.Repository)
  409. pipeline.DeployItem(&testPipelineItem{})
  410. f, _ := pipeline.GetFeature("power")
  411. assert.True(t, f)
  412. }
  413. func TestPipelineError(t *testing.T) {
  414. pipeline := NewPipeline(test.Repository)
  415. item := &testPipelineItem{}
  416. item.TestError = true
  417. pipeline.AddItem(item)
  418. pipeline.Initialize(map[string]interface{}{})
  419. commits := make([]*object.Commit, 1)
  420. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  421. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  422. result, err := pipeline.Run(commits)
  423. assert.Nil(t, result)
  424. assert.NotNil(t, err)
  425. }
  426. func TestPipelineDryRun(t *testing.T) {
  427. pipeline := NewPipeline(test.Repository)
  428. item := &testPipelineItem{}
  429. item.TestError = true
  430. pipeline.AddItem(item)
  431. pipeline.DryRun = true
  432. pipeline.Initialize(map[string]interface{}{})
  433. assert.True(t, pipeline.DryRun)
  434. pipeline.DryRun = false
  435. pipeline.Initialize(map[string]interface{}{ConfigPipelineDryRun: true})
  436. assert.True(t, pipeline.DryRun)
  437. commits := make([]*object.Commit, 1)
  438. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  439. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  440. result, err := pipeline.Run(commits)
  441. assert.NotNil(t, result)
  442. assert.Len(t, result, 1)
  443. assert.Contains(t, result, nil)
  444. assert.Nil(t, err)
  445. }
  446. func TestPipelineDryRunFalse(t *testing.T) {
  447. pipeline := NewPipeline(test.Repository)
  448. item := &testPipelineItem{}
  449. pipeline.AddItem(item)
  450. pipeline.Initialize(map[string]interface{}{ConfigPipelineDryRun: false})
  451. commits := make([]*object.Commit, 1)
  452. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  453. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  454. result, err := pipeline.Run(commits)
  455. assert.NotNil(t, result)
  456. assert.Len(t, result, 2)
  457. assert.Contains(t, result, nil)
  458. assert.Contains(t, result, item)
  459. assert.Nil(t, err)
  460. assert.True(t, item.DepsConsumed)
  461. assert.True(t, item.CommitMatches)
  462. assert.True(t, item.IndexMatches)
  463. assert.Equal(t, 1, *item.MergeState)
  464. assert.True(t, item.Forked)
  465. assert.False(t, *item.Merged)
  466. }
  467. func TestPipelineDumpPlanConfigure(t *testing.T) {
  468. pipeline := NewPipeline(test.Repository)
  469. item := &testPipelineItem{}
  470. pipeline.AddItem(item)
  471. pipeline.DumpPlan = true
  472. pipeline.DryRun = true
  473. pipeline.Initialize(map[string]interface{}{})
  474. assert.True(t, pipeline.DumpPlan)
  475. pipeline.DumpPlan = false
  476. pipeline.Initialize(map[string]interface{}{ConfigPipelineDumpPlan: true})
  477. assert.True(t, pipeline.DumpPlan)
  478. stream := &bytes.Buffer{}
  479. planPrintFunc = func(args ...interface{}) {
  480. fmt.Fprintln(stream, args...)
  481. }
  482. commits := make([]*object.Commit, 1)
  483. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  484. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  485. result, err := pipeline.Run(commits)
  486. assert.NotNil(t, result)
  487. assert.Len(t, result, 1)
  488. assert.Contains(t, result, nil)
  489. assert.Nil(t, err)
  490. assert.Equal(t, `E [1]
  491. C 1 af9ddc0db70f09f3f27b4b98e415592a7485171c
  492. `, stream.String())
  493. }
  494. func TestCommonAnalysisResultMerge(t *testing.T) {
  495. c1 := CommonAnalysisResult{
  496. BeginTime: 1513620635, EndTime: 1513720635, CommitsNumber: 1, RunTime: 100,
  497. RunTimePerItem: map[string]float64{"one": 1, "two": 2}}
  498. assert.Equal(t, c1.BeginTimeAsTime().Unix(), int64(1513620635))
  499. assert.Equal(t, c1.EndTimeAsTime().Unix(), int64(1513720635))
  500. c2 := CommonAnalysisResult{
  501. BeginTime: 1513620535, EndTime: 1513730635, CommitsNumber: 2, RunTime: 200,
  502. RunTimePerItem: map[string]float64{"two": 4, "three": 8}}
  503. c1.Merge(&c2)
  504. assert.Equal(t, c1.BeginTime, int64(1513620535))
  505. assert.Equal(t, c1.EndTime, int64(1513730635))
  506. assert.Equal(t, c1.CommitsNumber, 3)
  507. assert.Equal(t, c1.RunTime.Nanoseconds(), int64(300))
  508. assert.Equal(t, c1.RunTimePerItem, map[string]float64{"one": 1, "two": 6, "three": 8})
  509. }
  510. func TestCommonAnalysisResultMetadata(t *testing.T) {
  511. c1 := &CommonAnalysisResult{
  512. BeginTime: 1513620635, EndTime: 1513720635, CommitsNumber: 1, RunTime: 100 * 1e6,
  513. RunTimePerItem: map[string]float64{"one": 1, "two": 2}}
  514. meta := &pb.Metadata{}
  515. c1 = MetadataToCommonAnalysisResult(c1.FillMetadata(meta))
  516. assert.Equal(t, c1.BeginTimeAsTime().Unix(), int64(1513620635))
  517. assert.Equal(t, c1.EndTimeAsTime().Unix(), int64(1513720635))
  518. assert.Equal(t, c1.CommitsNumber, 1)
  519. assert.Equal(t, c1.RunTime.Nanoseconds(), int64(100*1e6))
  520. assert.Equal(t, c1.RunTimePerItem, map[string]float64{"one": 1, "two": 2})
  521. }
  522. func TestConfigurationOptionTypeString(t *testing.T) {
  523. opt := ConfigurationOptionType(0)
  524. assert.Equal(t, opt.String(), "")
  525. opt = ConfigurationOptionType(1)
  526. assert.Equal(t, opt.String(), "int")
  527. opt = ConfigurationOptionType(2)
  528. assert.Equal(t, opt.String(), "string")
  529. opt = ConfigurationOptionType(3)
  530. assert.Equal(t, opt.String(), "float")
  531. opt = ConfigurationOptionType(4)
  532. assert.Equal(t, opt.String(), "string")
  533. opt = ConfigurationOptionType(5)
  534. assert.Panics(t, func() { _ = opt.String() })
  535. }
  536. func TestConfigurationOptionFormatDefault(t *testing.T) {
  537. opt := ConfigurationOption{Type: StringConfigurationOption, Default: "ololo"}
  538. assert.Equal(t, opt.FormatDefault(), "\"ololo\"")
  539. opt = ConfigurationOption{Type: IntConfigurationOption, Default: 7}
  540. assert.Equal(t, opt.FormatDefault(), "7")
  541. opt = ConfigurationOption{Type: BoolConfigurationOption, Default: false}
  542. assert.Equal(t, opt.FormatDefault(), "false")
  543. opt = ConfigurationOption{Type: FloatConfigurationOption, Default: 0.5}
  544. assert.Equal(t, opt.FormatDefault(), "0.5")
  545. }
  546. func TestPrepareRunPlanTiny(t *testing.T) {
  547. rootCommit, err := test.Repository.CommitObject(plumbing.NewHash(
  548. "cce947b98a050c6d356bc6ba95030254914027b1"))
  549. if err != nil {
  550. t.Fatal(err)
  551. }
  552. plan := prepareRunPlan([]*object.Commit{rootCommit}, 0, true)
  553. assert.Len(t, plan, 2)
  554. assert.Equal(t, runActionEmerge, plan[0].Action)
  555. assert.Equal(t, rootBranchIndex, plan[0].Items[0])
  556. assert.Equal(t, "cce947b98a050c6d356bc6ba95030254914027b1", plan[0].Commit.Hash.String())
  557. assert.Equal(t, runActionCommit, plan[1].Action)
  558. assert.Equal(t, rootBranchIndex, plan[1].Items[0])
  559. assert.Equal(t, "cce947b98a050c6d356bc6ba95030254914027b1", plan[1].Commit.Hash.String())
  560. }
  561. func TestPrepareRunPlanSmall(t *testing.T) {
  562. cit, err := test.Repository.Log(&git.LogOptions{From: plumbing.ZeroHash})
  563. if err != nil {
  564. panic(err)
  565. }
  566. defer cit.Close()
  567. var commits []*object.Commit
  568. timeCutoff := time.Date(2016, 12, 15, 0, 0, 0, 0, time.FixedZone("CET", 7200))
  569. cit.ForEach(func(commit *object.Commit) error {
  570. reliableTime := time.Date(commit.Author.When.Year(), commit.Author.When.Month(),
  571. commit.Author.When.Day(), commit.Author.When.Hour(), commit.Author.When.Minute(),
  572. commit.Author.When.Second(), 0, time.FixedZone("CET", 7200))
  573. if reliableTime.Before(timeCutoff) {
  574. commits = append(commits, commit)
  575. }
  576. return nil
  577. })
  578. plan := prepareRunPlan(commits, 0, false)
  579. /*for _, p := range plan {
  580. if p.Commit != nil {
  581. fmt.Println(p.Action, p.Commit.Hash.String(), p.Items)
  582. } else {
  583. fmt.Println(p.Action, strings.Repeat(" ", 40), p.Items)
  584. }
  585. }*/
  586. // fork, merge and one artificial commit per branch
  587. assert.Len(t, plan, len(commits)+1)
  588. assert.Equal(t, runActionEmerge, plan[0].Action)
  589. assert.Equal(t, "cce947b98a050c6d356bc6ba95030254914027b1", plan[0].Commit.Hash.String())
  590. assert.Equal(t, rootBranchIndex, plan[0].Items[0])
  591. assert.Equal(t, runActionCommit, plan[1].Action)
  592. assert.Equal(t, rootBranchIndex, plan[1].Items[0])
  593. assert.Equal(t, "cce947b98a050c6d356bc6ba95030254914027b1", plan[1].Commit.Hash.String())
  594. assert.Equal(t, runActionCommit, plan[2].Action)
  595. assert.Equal(t, rootBranchIndex, plan[2].Items[0])
  596. assert.Equal(t, "a3ee37f91f0d705ec9c41ae88426f0ae44b2fbc3", plan[2].Commit.Hash.String())
  597. assert.Equal(t, runActionCommit, plan[10].Action)
  598. assert.Equal(t, rootBranchIndex, plan[10].Items[0])
  599. assert.Equal(t, "a28e9064c70618dc9d68e1401b889975e0680d11", plan[10].Commit.Hash.String())
  600. }
  601. func TestMergeDag(t *testing.T) {
  602. cit, err := test.Repository.Log(&git.LogOptions{From: plumbing.ZeroHash})
  603. if err != nil {
  604. panic(err)
  605. }
  606. defer cit.Close()
  607. var commits []*object.Commit
  608. timeCutoff := time.Date(2017, 8, 12, 0, 0, 0, 0, time.FixedZone("CET", 7200))
  609. cit.ForEach(func(commit *object.Commit) error {
  610. reliableTime := time.Date(commit.Author.When.Year(), commit.Author.When.Month(),
  611. commit.Author.When.Day(), commit.Author.When.Hour(), commit.Author.When.Minute(),
  612. commit.Author.When.Second(), 0, time.FixedZone("CET", 7200))
  613. if reliableTime.Before(timeCutoff) {
  614. commits = append(commits, commit)
  615. }
  616. return nil
  617. })
  618. hashes, dag := buildDag(commits)
  619. leaveRootComponent(hashes, dag)
  620. mergedDag, _ := mergeDag(hashes, dag)
  621. for key, vals := range mergedDag {
  622. if key != plumbing.NewHash("a28e9064c70618dc9d68e1401b889975e0680d11") &&
  623. key != plumbing.NewHash("db325a212d0bc99b470e000641d814745024bbd5") {
  624. assert.Len(t, vals, len(dag[key]), key.String())
  625. } else {
  626. mvals := map[string]bool{}
  627. for _, val := range vals {
  628. mvals[val.Hash.String()] = true
  629. }
  630. if key == plumbing.NewHash("a28e9064c70618dc9d68e1401b889975e0680d11") {
  631. assert.Contains(t, mvals, "db325a212d0bc99b470e000641d814745024bbd5")
  632. assert.Contains(t, mvals, "be9b61e09b08b98e64ed461a4004c9e2412f78ee")
  633. }
  634. if key == plumbing.NewHash("db325a212d0bc99b470e000641d814745024bbd5") {
  635. assert.Contains(t, mvals, "f30daba81ff2bf0b3ba02a1e1441e74f8a4f6fee")
  636. assert.Contains(t, mvals, "8a03b5620b1caa72ec9cb847ea88332621e2950a")
  637. }
  638. }
  639. }
  640. assert.Len(t, mergedDag, 8)
  641. assert.Contains(t, mergedDag, plumbing.NewHash("cce947b98a050c6d356bc6ba95030254914027b1"))
  642. assert.Contains(t, mergedDag, plumbing.NewHash("a3ee37f91f0d705ec9c41ae88426f0ae44b2fbc3"))
  643. assert.Contains(t, mergedDag, plumbing.NewHash("a28e9064c70618dc9d68e1401b889975e0680d11"))
  644. assert.Contains(t, mergedDag, plumbing.NewHash("be9b61e09b08b98e64ed461a4004c9e2412f78ee"))
  645. assert.Contains(t, mergedDag, plumbing.NewHash("db325a212d0bc99b470e000641d814745024bbd5"))
  646. assert.Contains(t, mergedDag, plumbing.NewHash("f30daba81ff2bf0b3ba02a1e1441e74f8a4f6fee"))
  647. assert.Contains(t, mergedDag, plumbing.NewHash("8a03b5620b1caa72ec9cb847ea88332621e2950a"))
  648. assert.Contains(t, mergedDag, plumbing.NewHash("dd9dd084d5851d7dc4399fc7dbf3d8292831ebc5"))
  649. queue := []plumbing.Hash{plumbing.NewHash("cce947b98a050c6d356bc6ba95030254914027b1")}
  650. visited := map[plumbing.Hash]bool{}
  651. for len(queue) > 0 {
  652. head := queue[len(queue)-1]
  653. queue = queue[:len(queue)-1]
  654. if visited[head] {
  655. continue
  656. }
  657. visited[head] = true
  658. for _, child := range mergedDag[head] {
  659. queue = append(queue, child.Hash)
  660. }
  661. }
  662. assert.Len(t, visited, 8)
  663. }
  664. func TestPrepareRunPlanBig(t *testing.T) {
  665. cases := [][7]int{
  666. {2017, 8, 9, 0, 0, 0, 0},
  667. {2017, 8, 10, 0, 0, 0, 0},
  668. {2017, 8, 24, 1, 1, 1, 1},
  669. {2017, 9, 19, 1 - 2, 1, 1, 1},
  670. {2017, 9, 23, 1 - 2, 1, 1, 1},
  671. {2017, 12, 8, 1, 1, 1, 1},
  672. {2017, 12, 9, 1, 1, 1, 1},
  673. {2017, 12, 10, 1, 1, 1, 1},
  674. {2017, 12, 11, 2, 2, 2, 2},
  675. {2017, 12, 19, 3, 3, 3, 3},
  676. {2017, 12, 27, 3, 3, 3, 3},
  677. {2018, 1, 10, 3, 3, 3, 3},
  678. {2018, 1, 16, 3, 3, 3, 3},
  679. {2018, 1, 18, 4, 5, 4, 4},
  680. {2018, 1, 23, 5, 5, 5, 5},
  681. {2018, 3, 12, 6, 6, 6, 6},
  682. {2018, 5, 13, 6, 6, 6, 6},
  683. {2018, 5, 16, 7, 7, 7, 7},
  684. }
  685. for _, testCase := range cases {
  686. func() {
  687. cit, err := test.Repository.Log(&git.LogOptions{From: plumbing.ZeroHash})
  688. if err != nil {
  689. panic(err)
  690. }
  691. defer cit.Close()
  692. var commits []*object.Commit
  693. timeCutoff := time.Date(
  694. testCase[0], time.Month(testCase[1]), testCase[2], 0, 0, 0, 0, time.FixedZone("CET", 7200))
  695. cit.ForEach(func(commit *object.Commit) error {
  696. reliableTime := time.Date(commit.Author.When.Year(), commit.Author.When.Month(),
  697. commit.Author.When.Day(), commit.Author.When.Hour(), commit.Author.When.Minute(),
  698. commit.Author.When.Second(), 0, time.FixedZone("CET", 7200))
  699. if reliableTime.Before(timeCutoff) {
  700. commits = append(commits, commit)
  701. }
  702. return nil
  703. })
  704. plan := prepareRunPlan(commits, 0, false)
  705. /*for _, p := range plan {
  706. if p.Commit != nil {
  707. fmt.Println(p.Action, p.Commit.Hash.String(), p.Items)
  708. } else {
  709. fmt.Println(p.Action, strings.Repeat(" ", 40), p.Items)
  710. }
  711. }*/
  712. numCommits := 0
  713. numForks := 0
  714. numMerges := 0
  715. numDeletes := 0
  716. numEmerges := 0
  717. processed := map[plumbing.Hash]map[int]int{}
  718. for _, p := range plan {
  719. switch p.Action {
  720. case runActionCommit:
  721. branches := processed[p.Commit.Hash]
  722. if branches == nil {
  723. branches = map[int]int{}
  724. processed[p.Commit.Hash] = branches
  725. }
  726. branches[p.Items[0]]++
  727. for _, parent := range p.Commit.ParentHashes {
  728. assert.Contains(t, processed, parent)
  729. }
  730. numCommits++
  731. case runActionFork:
  732. numForks++
  733. case runActionMerge:
  734. counts := map[int]int{}
  735. for _, i := range p.Items {
  736. counts[i]++
  737. }
  738. for x, v := range counts {
  739. assert.Equal(t, 1, v, x)
  740. }
  741. numMerges++
  742. case runActionDelete:
  743. numDeletes++
  744. case runActionEmerge:
  745. numEmerges++
  746. }
  747. }
  748. for c, branches := range processed {
  749. for b, v := range branches {
  750. assert.Equal(t, 1, v, fmt.Sprint(c.String(), b))
  751. }
  752. }
  753. assert.Equal(t, numCommits, len(commits)+testCase[3], fmt.Sprintf("commits %v", testCase))
  754. assert.Equal(t, numForks, testCase[4], fmt.Sprintf("forks %v", testCase))
  755. assert.Equal(t, numMerges, testCase[5], fmt.Sprintf("merges %v", testCase))
  756. assert.Equal(t, numDeletes, testCase[6], fmt.Sprintf("deletes %v", testCase))
  757. assert.Equal(t, numEmerges, 1, fmt.Sprintf("emerges %v", testCase))
  758. }()
  759. }
  760. }
  761. func TestPipelineRunHibernation(t *testing.T) {
  762. pipeline := NewPipeline(test.Repository)
  763. pipeline.HibernationDistance = 2
  764. pipeline.AddItem(&testPipelineItem{})
  765. item := &dependingTestPipelineItem{}
  766. pipeline.AddItem(item)
  767. pipeline.Initialize(map[string]interface{}{})
  768. hashes := []string{
  769. "0183e08978007c746468fca9f68e6e2fbf32100c",
  770. "b467a682f680a4dcfd74869480a52f8be3a4fdf0",
  771. "31c9f752f9ce103e85523442fa3f05b1ff4ea546",
  772. "6530890fcd02fb5e6e85ce2951fdd5c555f2c714",
  773. "feb2d230777cbb492ecbc27dea380dc1e7b8f437",
  774. "9b30d2abc043ab59aa7ec7b50970c65c90b98853",
  775. }
  776. commits := make([]*object.Commit, len(hashes))
  777. for i, h := range hashes {
  778. var err error
  779. commits[i], err = test.Repository.CommitObject(plumbing.NewHash(h))
  780. if err != nil {
  781. t.Fatal(err)
  782. }
  783. }
  784. _, err := pipeline.Run(commits)
  785. assert.Nil(t, err)
  786. assert.True(t, item.Hibernated)
  787. assert.True(t, item.Booted)
  788. item.RaiseHibernateError = true
  789. assert.Panics(t, func() { pipeline.Run(commits) })
  790. item.RaiseHibernateError = false
  791. pipeline.Run(commits)
  792. item.RaiseBootError = true
  793. assert.Panics(t, func() { pipeline.Run(commits) })
  794. }