pipeline_test.go 22 KB

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