pipeline_test.go 28 KB

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