pipeline_test.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. package hercules
  2. import (
  3. "errors"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "testing"
  8. "github.com/stretchr/testify/assert"
  9. "gopkg.in/src-d/go-git.v4"
  10. "gopkg.in/src-d/go-git.v4/plumbing"
  11. "gopkg.in/src-d/go-git.v4/plumbing/object"
  12. "gopkg.in/src-d/go-git.v4/storage/memory"
  13. "path"
  14. )
  15. type testPipelineItem struct {
  16. Initialized bool
  17. DepsConsumed bool
  18. CommitMatches bool
  19. IndexMatches bool
  20. TestError bool
  21. }
  22. func (item *testPipelineItem) Name() string {
  23. return "Test"
  24. }
  25. func (item *testPipelineItem) Provides() []string {
  26. arr := [...]string{"test"}
  27. return arr[:]
  28. }
  29. func (item *testPipelineItem) Requires() []string {
  30. return []string{}
  31. }
  32. func (item *testPipelineItem) Configure(facts map[string]interface{}) {
  33. }
  34. func (item *testPipelineItem) ListConfigurationOptions() []ConfigurationOption {
  35. options := [...]ConfigurationOption{{
  36. Name: "TestOption",
  37. Description: "The option description.",
  38. Flag: "test-option",
  39. Type: IntConfigurationOption,
  40. Default: 10,
  41. }}
  42. return options[:]
  43. }
  44. func (item *testPipelineItem) Initialize(repository *git.Repository) {
  45. item.Initialized = repository != nil
  46. }
  47. func (item *testPipelineItem) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  48. if item.TestError {
  49. return nil, errors.New("error")
  50. }
  51. obj, exists := deps["commit"]
  52. item.DepsConsumed = exists
  53. if item.DepsConsumed {
  54. commit := obj.(*object.Commit)
  55. item.CommitMatches = commit.Hash == plumbing.NewHash(
  56. "af9ddc0db70f09f3f27b4b98e415592a7485171c")
  57. obj, item.DepsConsumed = deps["index"]
  58. if item.DepsConsumed {
  59. item.IndexMatches = obj.(int) == 0
  60. }
  61. }
  62. return map[string]interface{}{"test": item}, nil
  63. }
  64. func (item *testPipelineItem) Finalize() interface{} {
  65. return item
  66. }
  67. type dependingTestPipelineItem struct {
  68. DependencySatisfied bool
  69. TestNilConsumeReturn bool
  70. }
  71. func (item *dependingTestPipelineItem) Name() string {
  72. return "Test2"
  73. }
  74. func (item *dependingTestPipelineItem) Provides() []string {
  75. arr := [...]string{"test2"}
  76. return arr[:]
  77. }
  78. func (item *dependingTestPipelineItem) Requires() []string {
  79. arr := [...]string{"test"}
  80. return arr[:]
  81. }
  82. func (item *dependingTestPipelineItem) ListConfigurationOptions() []ConfigurationOption {
  83. options := [...]ConfigurationOption{{
  84. Name: "TestOption",
  85. Description: "The option description.",
  86. Flag: "test-option",
  87. Type: IntConfigurationOption,
  88. Default: 10,
  89. }}
  90. return options[:]
  91. }
  92. func (item *dependingTestPipelineItem) Configure(facts map[string]interface{}) {
  93. }
  94. func (item *dependingTestPipelineItem) Initialize(repository *git.Repository) {
  95. }
  96. func (item *dependingTestPipelineItem) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  97. _, exists := deps["test"]
  98. item.DependencySatisfied = exists
  99. if !item.TestNilConsumeReturn {
  100. return map[string]interface{}{"test2": item}, nil
  101. } else {
  102. return nil, nil
  103. }
  104. }
  105. func (item *dependingTestPipelineItem) Finalize() interface{} {
  106. return item.DependencySatisfied
  107. }
  108. func TestPipelineRun(t *testing.T) {
  109. pipeline := NewPipeline(testRepository)
  110. item := &testPipelineItem{}
  111. pipeline.AddItem(item)
  112. pipeline.Initialize(map[string]interface{}{})
  113. assert.True(t, item.Initialized)
  114. commits := make([]*object.Commit, 1)
  115. commits[0], _ = testRepository.CommitObject(plumbing.NewHash(
  116. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  117. result, err := pipeline.Run(commits)
  118. assert.Nil(t, err)
  119. assert.Equal(t, item, result[item].(*testPipelineItem))
  120. assert.True(t, item.DepsConsumed)
  121. assert.True(t, item.CommitMatches)
  122. assert.True(t, item.IndexMatches)
  123. pipeline.RemoveItem(item)
  124. result, err = pipeline.Run(commits)
  125. assert.Nil(t, err)
  126. assert.Equal(t, 0, len(result))
  127. }
  128. func TestPipelineOnProgress(t *testing.T) {
  129. pipeline := NewPipeline(testRepository)
  130. var progressOk1, progressOk2 bool
  131. onProgress := func(step int, total int) {
  132. if step == 0 && total == 1 {
  133. progressOk1 = true
  134. }
  135. if step == 1 && total == 1 && progressOk1 {
  136. progressOk2 = true
  137. }
  138. }
  139. pipeline.OnProgress = onProgress
  140. commits := make([]*object.Commit, 1)
  141. commits[0], _ = testRepository.CommitObject(plumbing.NewHash(
  142. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  143. result, err := pipeline.Run(commits)
  144. assert.Nil(t, err)
  145. assert.Equal(t, 0, len(result))
  146. assert.True(t, progressOk1)
  147. assert.True(t, progressOk2)
  148. }
  149. func TestPipelineCommits(t *testing.T) {
  150. pipeline := NewPipeline(testRepository)
  151. commits := pipeline.Commits()
  152. assert.True(t, len(commits) >= 90)
  153. assert.Equal(t, commits[0].Hash, plumbing.NewHash(
  154. "cce947b98a050c6d356bc6ba95030254914027b1"))
  155. assert.Equal(t, commits[89].Hash, plumbing.NewHash(
  156. "6db8065cdb9bb0758f36a7e75fc72ab95f9e8145"))
  157. assert.NotEqual(t, commits[len(commits)-1], commits[len(commits)-2])
  158. }
  159. func TestLoadCommitsFromFile(t *testing.T) {
  160. tmp, err := ioutil.TempFile("", "hercules-test-")
  161. assert.Nil(t, err)
  162. tmp.WriteString("cce947b98a050c6d356bc6ba95030254914027b1\n6db8065cdb9bb0758f36a7e75fc72ab95f9e8145")
  163. tmp.Close()
  164. defer os.Remove(tmp.Name())
  165. commits, err := LoadCommitsFromFile(tmp.Name(), testRepository)
  166. assert.Nil(t, err)
  167. assert.Equal(t, len(commits), 2)
  168. assert.Equal(t, commits[0].Hash, plumbing.NewHash(
  169. "cce947b98a050c6d356bc6ba95030254914027b1"))
  170. assert.Equal(t, commits[1].Hash, plumbing.NewHash(
  171. "6db8065cdb9bb0758f36a7e75fc72ab95f9e8145"))
  172. commits, err = LoadCommitsFromFile("/WAT?xxx!", testRepository)
  173. assert.Nil(t, commits)
  174. assert.NotNil(t, err)
  175. tmp, err = ioutil.TempFile("", "hercules-test-")
  176. assert.Nil(t, err)
  177. tmp.WriteString("WAT")
  178. tmp.Close()
  179. defer os.Remove(tmp.Name())
  180. commits, err = LoadCommitsFromFile(tmp.Name(), testRepository)
  181. assert.Nil(t, commits)
  182. assert.NotNil(t, err)
  183. tmp, err = ioutil.TempFile("", "hercules-test-")
  184. assert.Nil(t, err)
  185. tmp.WriteString("ffffffffffffffffffffffffffffffffffffffff")
  186. tmp.Close()
  187. defer os.Remove(tmp.Name())
  188. commits, err = LoadCommitsFromFile(tmp.Name(), testRepository)
  189. assert.Nil(t, commits)
  190. assert.NotNil(t, err)
  191. }
  192. func TestPipelineDeps(t *testing.T) {
  193. pipeline := NewPipeline(testRepository)
  194. item1 := &dependingTestPipelineItem{}
  195. item2 := &testPipelineItem{}
  196. pipeline.AddItem(item1)
  197. pipeline.AddItem(item2)
  198. pipeline.Initialize(map[string]interface{}{})
  199. commits := make([]*object.Commit, 1)
  200. commits[0], _ = testRepository.CommitObject(plumbing.NewHash(
  201. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  202. result, err := pipeline.Run(commits)
  203. assert.Nil(t, err)
  204. assert.True(t, result[item1].(bool))
  205. item1.TestNilConsumeReturn = true
  206. assert.Panics(t, func() { pipeline.Run(commits) })
  207. }
  208. func TestPipelineError(t *testing.T) {
  209. pipeline := NewPipeline(testRepository)
  210. item := &testPipelineItem{}
  211. item.TestError = true
  212. pipeline.AddItem(item)
  213. pipeline.Initialize(map[string]interface{}{})
  214. commits := make([]*object.Commit, 1)
  215. commits[0], _ = testRepository.CommitObject(plumbing.NewHash(
  216. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  217. result, err := pipeline.Run(commits)
  218. assert.Nil(t, result)
  219. assert.NotNil(t, err)
  220. }
  221. func TestPipelineSerialize(t *testing.T) {
  222. pipeline := NewPipeline(testRepository)
  223. pipeline.SetFeature("uast")
  224. pipeline.DeployItem(&BurndownAnalysis{})
  225. facts := map[string]interface{}{}
  226. facts["Pipeline.DryRun"] = true
  227. tmpdir, _ := ioutil.TempDir("", "hercules-")
  228. defer os.RemoveAll(tmpdir)
  229. dotpath := path.Join(tmpdir, "graph.dot")
  230. facts["Pipeline.DumpPath"] = dotpath
  231. pipeline.Initialize(facts)
  232. bdot, _ := ioutil.ReadFile(dotpath)
  233. dot := string(bdot)
  234. assert.Equal(t, `digraph Hercules {
  235. "6 BlobCache" -> "7 [blob_cache]"
  236. "0 DaysSinceStart" -> "3 [day]"
  237. "10 FileDiff" -> "12 [file_diff]"
  238. "15 FileDiffRefiner" -> "16 Burndown"
  239. "1 IdentityDetector" -> "4 [author]"
  240. "8 RenameAnalysis" -> "16 Burndown"
  241. "8 RenameAnalysis" -> "10 FileDiff"
  242. "8 RenameAnalysis" -> "9 UAST"
  243. "8 RenameAnalysis" -> "13 UASTChanges"
  244. "2 TreeDiff" -> "5 [changes]"
  245. "9 UAST" -> "11 [uasts]"
  246. "13 UASTChanges" -> "14 [changed_uasts]"
  247. "4 [author]" -> "16 Burndown"
  248. "7 [blob_cache]" -> "16 Burndown"
  249. "7 [blob_cache]" -> "10 FileDiff"
  250. "7 [blob_cache]" -> "8 RenameAnalysis"
  251. "7 [blob_cache]" -> "9 UAST"
  252. "14 [changed_uasts]" -> "15 FileDiffRefiner"
  253. "5 [changes]" -> "6 BlobCache"
  254. "5 [changes]" -> "8 RenameAnalysis"
  255. "3 [day]" -> "16 Burndown"
  256. "12 [file_diff]" -> "15 FileDiffRefiner"
  257. "11 [uasts]" -> "13 UASTChanges"
  258. }`, dot)
  259. }
  260. func TestPipelineSerializeNoUast(t *testing.T) {
  261. pipeline := NewPipeline(testRepository)
  262. // pipeline.SetFeature("uast")
  263. pipeline.DeployItem(&BurndownAnalysis{})
  264. facts := map[string]interface{}{}
  265. facts["Pipeline.DryRun"] = true
  266. tmpdir, _ := ioutil.TempDir("", "hercules-")
  267. defer os.RemoveAll(tmpdir)
  268. dotpath := path.Join(tmpdir, "graph.dot")
  269. facts["Pipeline.DumpPath"] = dotpath
  270. pipeline.Initialize(facts)
  271. bdot, _ := ioutil.ReadFile(dotpath)
  272. dot := string(bdot)
  273. assert.Equal(t, `digraph Hercules {
  274. "6 BlobCache" -> "7 [blob_cache]"
  275. "0 DaysSinceStart" -> "3 [day]"
  276. "9 FileDiff" -> "10 [file_diff]"
  277. "1 IdentityDetector" -> "4 [author]"
  278. "8 RenameAnalysis" -> "11 Burndown"
  279. "8 RenameAnalysis" -> "9 FileDiff"
  280. "2 TreeDiff" -> "5 [changes]"
  281. "4 [author]" -> "11 Burndown"
  282. "7 [blob_cache]" -> "11 Burndown"
  283. "7 [blob_cache]" -> "9 FileDiff"
  284. "7 [blob_cache]" -> "8 RenameAnalysis"
  285. "5 [changes]" -> "6 BlobCache"
  286. "5 [changes]" -> "8 RenameAnalysis"
  287. "3 [day]" -> "11 Burndown"
  288. "10 [file_diff]" -> "11 Burndown"
  289. }`, dot)
  290. }
  291. func init() {
  292. cwd, err := os.Getwd()
  293. if err == nil {
  294. testRepository, err = git.PlainOpen(cwd)
  295. if err == nil {
  296. iter, err := testRepository.CommitObjects()
  297. if err == nil {
  298. commits := -1
  299. for ; err != io.EOF; _, err = iter.Next() {
  300. if err != nil {
  301. panic(err)
  302. }
  303. commits++
  304. if commits >= 100 {
  305. return
  306. }
  307. }
  308. }
  309. }
  310. }
  311. testRepository, _ = git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
  312. URL: "https://github.com/src-d/hercules",
  313. })
  314. }