pipeline_test.go 29 KB

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