pipeline_test.go 27 KB

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