pipeline_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. package core
  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/hercules.v4/internal/pb"
  13. "gopkg.in/src-d/hercules.v4/internal/test"
  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) Flag() string {
  45. return "mytest"
  46. }
  47. func (item *testPipelineItem) Features() []string {
  48. f := [...]string{"power"}
  49. return f[:]
  50. }
  51. func (item *testPipelineItem) Initialize(repository *git.Repository) {
  52. item.Initialized = repository != nil
  53. }
  54. func (item *testPipelineItem) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  55. if item.TestError {
  56. return nil, errors.New("error")
  57. }
  58. obj, exists := deps["commit"]
  59. item.DepsConsumed = exists
  60. if item.DepsConsumed {
  61. commit := obj.(*object.Commit)
  62. item.CommitMatches = commit.Hash == plumbing.NewHash(
  63. "af9ddc0db70f09f3f27b4b98e415592a7485171c")
  64. obj, item.DepsConsumed = deps["index"]
  65. if item.DepsConsumed {
  66. item.IndexMatches = obj.(int) == 0
  67. }
  68. }
  69. return map[string]interface{}{"test": item}, nil
  70. }
  71. func (item *testPipelineItem) Finalize() interface{} {
  72. return item
  73. }
  74. func (item *testPipelineItem) Serialize(result interface{}, binary bool, writer io.Writer) error {
  75. return nil
  76. }
  77. type dependingTestPipelineItem struct {
  78. DependencySatisfied bool
  79. TestNilConsumeReturn bool
  80. }
  81. func (item *dependingTestPipelineItem) Name() string {
  82. return "Test2"
  83. }
  84. func (item *dependingTestPipelineItem) Provides() []string {
  85. arr := [...]string{"test2"}
  86. return arr[:]
  87. }
  88. func (item *dependingTestPipelineItem) Requires() []string {
  89. arr := [...]string{"test"}
  90. return arr[:]
  91. }
  92. func (item *dependingTestPipelineItem) ListConfigurationOptions() []ConfigurationOption {
  93. options := [...]ConfigurationOption{{
  94. Name: "TestOption2",
  95. Description: "The option description.",
  96. Flag: "test-option2",
  97. Type: IntConfigurationOption,
  98. Default: 10,
  99. }}
  100. return options[:]
  101. }
  102. func (item *dependingTestPipelineItem) Configure(facts map[string]interface{}) {
  103. }
  104. func (item *dependingTestPipelineItem) Initialize(repository *git.Repository) {
  105. }
  106. func (item *dependingTestPipelineItem) Flag() string {
  107. return "depflag"
  108. }
  109. func (item *dependingTestPipelineItem) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  110. _, exists := deps["test"]
  111. item.DependencySatisfied = exists
  112. if !item.TestNilConsumeReturn {
  113. return map[string]interface{}{"test2": item}, nil
  114. }
  115. return nil, nil
  116. }
  117. func (item *dependingTestPipelineItem) Finalize() interface{} {
  118. return true
  119. }
  120. func (item *dependingTestPipelineItem) Serialize(result interface{}, binary bool, writer io.Writer) error {
  121. return nil
  122. }
  123. func TestPipelineFacts(t *testing.T) {
  124. pipeline := NewPipeline(test.Repository)
  125. pipeline.SetFact("fact", "value")
  126. assert.Equal(t, pipeline.GetFact("fact"), "value")
  127. }
  128. func TestPipelineFeatures(t *testing.T) {
  129. pipeline := NewPipeline(test.Repository)
  130. pipeline.SetFeature("feat")
  131. val, _ := pipeline.GetFeature("feat")
  132. assert.True(t, val)
  133. _, exists := pipeline.GetFeature("!")
  134. assert.False(t, exists)
  135. Registry.featureFlags.Set("777")
  136. defer func() {
  137. Registry.featureFlags = arrayFeatureFlags{Flags: []string{}, Choices: map[string]bool{}}
  138. }()
  139. pipeline.SetFeaturesFromFlags()
  140. _, exists = pipeline.GetFeature("777")
  141. assert.False(t, exists)
  142. assert.Panics(t, func() {
  143. pipeline.SetFeaturesFromFlags(
  144. &PipelineItemRegistry{}, &PipelineItemRegistry{})
  145. })
  146. }
  147. func TestPipelineRun(t *testing.T) {
  148. pipeline := NewPipeline(test.Repository)
  149. item := &testPipelineItem{}
  150. pipeline.AddItem(item)
  151. pipeline.Initialize(map[string]interface{}{})
  152. assert.True(t, item.Initialized)
  153. commits := make([]*object.Commit, 1)
  154. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  155. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  156. result, err := pipeline.Run(commits)
  157. assert.Nil(t, err)
  158. assert.Equal(t, 2, len(result))
  159. assert.Equal(t, item, result[item].(*testPipelineItem))
  160. common := result[nil].(*CommonAnalysisResult)
  161. assert.Equal(t, common.BeginTime, int64(1481719092))
  162. assert.Equal(t, common.EndTime, int64(1481719092))
  163. assert.Equal(t, common.CommitsNumber, 1)
  164. assert.True(t, common.RunTime.Nanoseconds()/1e6 < 100)
  165. assert.True(t, item.DepsConsumed)
  166. assert.True(t, item.CommitMatches)
  167. assert.True(t, item.IndexMatches)
  168. pipeline.RemoveItem(item)
  169. result, err = pipeline.Run(commits)
  170. assert.Nil(t, err)
  171. assert.Equal(t, 1, len(result))
  172. }
  173. func TestPipelineOnProgress(t *testing.T) {
  174. pipeline := NewPipeline(test.Repository)
  175. var progressOk1, progressOk2 bool
  176. onProgress := func(step int, total int) {
  177. if step == 0 && total == 1 {
  178. progressOk1 = true
  179. }
  180. if step == 1 && total == 1 && progressOk1 {
  181. progressOk2 = true
  182. }
  183. }
  184. pipeline.OnProgress = onProgress
  185. commits := make([]*object.Commit, 1)
  186. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  187. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  188. result, err := pipeline.Run(commits)
  189. assert.Nil(t, err)
  190. assert.Equal(t, 1, len(result))
  191. assert.True(t, progressOk1)
  192. assert.True(t, progressOk2)
  193. }
  194. func TestPipelineCommits(t *testing.T) {
  195. pipeline := NewPipeline(test.Repository)
  196. commits := pipeline.Commits()
  197. assert.True(t, len(commits) >= 90)
  198. assert.Equal(t, commits[0].Hash, plumbing.NewHash(
  199. "cce947b98a050c6d356bc6ba95030254914027b1"))
  200. assert.Equal(t, commits[89].Hash, plumbing.NewHash(
  201. "6db8065cdb9bb0758f36a7e75fc72ab95f9e8145"))
  202. assert.NotEqual(t, commits[len(commits)-1], commits[len(commits)-2])
  203. }
  204. func TestLoadCommitsFromFile(t *testing.T) {
  205. tmp, err := ioutil.TempFile("", "hercules-test-")
  206. assert.Nil(t, err)
  207. tmp.WriteString("cce947b98a050c6d356bc6ba95030254914027b1\n6db8065cdb9bb0758f36a7e75fc72ab95f9e8145")
  208. tmp.Close()
  209. defer os.Remove(tmp.Name())
  210. commits, err := LoadCommitsFromFile(tmp.Name(), test.Repository)
  211. assert.Nil(t, err)
  212. assert.Equal(t, len(commits), 2)
  213. assert.Equal(t, commits[0].Hash, plumbing.NewHash(
  214. "cce947b98a050c6d356bc6ba95030254914027b1"))
  215. assert.Equal(t, commits[1].Hash, plumbing.NewHash(
  216. "6db8065cdb9bb0758f36a7e75fc72ab95f9e8145"))
  217. commits, err = LoadCommitsFromFile("/WAT?xxx!", test.Repository)
  218. assert.Nil(t, commits)
  219. assert.NotNil(t, err)
  220. tmp, err = ioutil.TempFile("", "hercules-test-")
  221. assert.Nil(t, err)
  222. tmp.WriteString("WAT")
  223. tmp.Close()
  224. defer os.Remove(tmp.Name())
  225. commits, err = LoadCommitsFromFile(tmp.Name(), test.Repository)
  226. assert.Nil(t, commits)
  227. assert.NotNil(t, err)
  228. tmp, err = ioutil.TempFile("", "hercules-test-")
  229. assert.Nil(t, err)
  230. tmp.WriteString("ffffffffffffffffffffffffffffffffffffffff")
  231. tmp.Close()
  232. defer os.Remove(tmp.Name())
  233. commits, err = LoadCommitsFromFile(tmp.Name(), test.Repository)
  234. assert.Nil(t, commits)
  235. assert.NotNil(t, err)
  236. }
  237. func TestPipelineDeps(t *testing.T) {
  238. pipeline := NewPipeline(test.Repository)
  239. item1 := &dependingTestPipelineItem{}
  240. item2 := &testPipelineItem{}
  241. pipeline.AddItem(item1)
  242. pipeline.AddItem(item2)
  243. assert.Equal(t, pipeline.Len(), 2)
  244. pipeline.Initialize(map[string]interface{}{})
  245. commits := make([]*object.Commit, 1)
  246. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  247. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  248. result, err := pipeline.Run(commits)
  249. assert.Nil(t, err)
  250. assert.True(t, result[item1].(bool))
  251. assert.Equal(t, result[item2], item2)
  252. item1.TestNilConsumeReturn = true
  253. assert.Panics(t, func() { pipeline.Run(commits) })
  254. }
  255. func TestPipelineDeployFeatures(t *testing.T) {
  256. pipeline := NewPipeline(test.Repository)
  257. pipeline.DeployItem(&testPipelineItem{})
  258. f, _ := pipeline.GetFeature("power")
  259. assert.True(t, f)
  260. }
  261. func TestPipelineError(t *testing.T) {
  262. pipeline := NewPipeline(test.Repository)
  263. item := &testPipelineItem{}
  264. item.TestError = true
  265. pipeline.AddItem(item)
  266. pipeline.Initialize(map[string]interface{}{})
  267. commits := make([]*object.Commit, 1)
  268. commits[0], _ = test.Repository.CommitObject(plumbing.NewHash(
  269. "af9ddc0db70f09f3f27b4b98e415592a7485171c"))
  270. result, err := pipeline.Run(commits)
  271. assert.Nil(t, result)
  272. assert.NotNil(t, err)
  273. }
  274. func TestCommonAnalysisResultMerge(t *testing.T) {
  275. c1 := CommonAnalysisResult{
  276. BeginTime: 1513620635, EndTime: 1513720635, CommitsNumber: 1, RunTime: 100}
  277. assert.Equal(t, c1.BeginTimeAsTime().Unix(), int64(1513620635))
  278. assert.Equal(t, c1.EndTimeAsTime().Unix(), int64(1513720635))
  279. c2 := CommonAnalysisResult{
  280. BeginTime: 1513620535, EndTime: 1513730635, CommitsNumber: 2, RunTime: 200}
  281. c1.Merge(&c2)
  282. assert.Equal(t, c1.BeginTime, int64(1513620535))
  283. assert.Equal(t, c1.EndTime, int64(1513730635))
  284. assert.Equal(t, c1.CommitsNumber, 3)
  285. assert.Equal(t, c1.RunTime.Nanoseconds(), int64(300))
  286. }
  287. func TestCommonAnalysisResultMetadata(t *testing.T) {
  288. c1 := &CommonAnalysisResult{
  289. BeginTime: 1513620635, EndTime: 1513720635, CommitsNumber: 1, RunTime: 100 * 1e6}
  290. meta := &pb.Metadata{}
  291. c1 = MetadataToCommonAnalysisResult(c1.FillMetadata(meta))
  292. assert.Equal(t, c1.BeginTimeAsTime().Unix(), int64(1513620635))
  293. assert.Equal(t, c1.EndTimeAsTime().Unix(), int64(1513720635))
  294. assert.Equal(t, c1.CommitsNumber, 1)
  295. assert.Equal(t, c1.RunTime.Nanoseconds(), int64(100*1e6))
  296. }
  297. func TestConfigurationOptionTypeString(t *testing.T) {
  298. opt := ConfigurationOptionType(0)
  299. assert.Equal(t, opt.String(), "")
  300. opt = ConfigurationOptionType(1)
  301. assert.Equal(t, opt.String(), "int")
  302. opt = ConfigurationOptionType(2)
  303. assert.Equal(t, opt.String(), "string")
  304. opt = ConfigurationOptionType(3)
  305. assert.Equal(t, opt.String(), "float")
  306. opt = ConfigurationOptionType(4)
  307. assert.Equal(t, opt.String(), "string")
  308. opt = ConfigurationOptionType(5)
  309. assert.Panics(t, func() { _ = opt.String() })
  310. }
  311. func TestConfigurationOptionFormatDefault(t *testing.T) {
  312. opt := ConfigurationOption{Type: StringConfigurationOption, Default: "ololo"}
  313. assert.Equal(t, opt.FormatDefault(), "\"ololo\"")
  314. opt = ConfigurationOption{Type: IntConfigurationOption, Default: 7}
  315. assert.Equal(t, opt.FormatDefault(), "7")
  316. opt = ConfigurationOption{Type: BoolConfigurationOption, Default: false}
  317. assert.Equal(t, opt.FormatDefault(), "false")
  318. opt = ConfigurationOption{Type: FloatConfigurationOption, Default: 0.5}
  319. assert.Equal(t, opt.FormatDefault(), "0.5")
  320. }