shotness_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. package leaves
  2. import (
  3. "bytes"
  4. "io/ioutil"
  5. "os"
  6. "path"
  7. "testing"
  8. "time"
  9. "github.com/gogo/protobuf/proto"
  10. "github.com/sergi/go-diff/diffmatchpatch"
  11. "github.com/stretchr/testify/assert"
  12. "gopkg.in/bblfsh/sdk.v2/uast"
  13. "gopkg.in/bblfsh/sdk.v2/uast/nodes"
  14. "gopkg.in/bblfsh/sdk.v2/uast/nodes/nodesproto"
  15. "gopkg.in/src-d/go-git.v4/plumbing/object"
  16. "gopkg.in/src-d/hercules.v10/internal/core"
  17. "gopkg.in/src-d/hercules.v10/internal/pb"
  18. items "gopkg.in/src-d/hercules.v10/internal/plumbing"
  19. uast_items "gopkg.in/src-d/hercules.v10/internal/plumbing/uast"
  20. "gopkg.in/src-d/hercules.v10/internal/test"
  21. )
  22. func fixtureShotness() *ShotnessAnalysis {
  23. sh := &ShotnessAnalysis{}
  24. sh.Initialize(test.Repository)
  25. sh.Configure(nil)
  26. return sh
  27. }
  28. func TestShotnessMeta(t *testing.T) {
  29. sh := &ShotnessAnalysis{}
  30. assert.Nil(t, sh.Initialize(test.Repository))
  31. assert.NotNil(t, sh.nodes)
  32. assert.NotNil(t, sh.files)
  33. assert.Equal(t, sh.Name(), "Shotness")
  34. assert.Len(t, sh.Provides(), 0)
  35. assert.Equal(t, len(sh.Requires()), 2)
  36. assert.Equal(t, sh.Requires()[0], items.DependencyFileDiff)
  37. assert.Equal(t, sh.Requires()[1], uast_items.DependencyUastChanges)
  38. assert.Len(t, sh.ListConfigurationOptions(), 2)
  39. assert.Equal(t, sh.ListConfigurationOptions()[0].Name, ConfigShotnessXpathStruct)
  40. assert.Equal(t, sh.ListConfigurationOptions()[1].Name, ConfigShotnessXpathName)
  41. assert.Nil(t, sh.Configure(nil))
  42. assert.Equal(t, sh.XpathStruct, DefaultShotnessXpathStruct)
  43. assert.Equal(t, sh.XpathName, DefaultShotnessXpathName)
  44. assert.NoError(t, sh.Configure(map[string]interface{}{
  45. ConfigShotnessXpathStruct: "xpath!",
  46. ConfigShotnessXpathName: "another!",
  47. }))
  48. assert.Equal(t, sh.XpathStruct, "xpath!")
  49. assert.Equal(t, sh.XpathName, "another!")
  50. logger := core.NewLogger()
  51. assert.NoError(t, sh.Configure(map[string]interface{}{
  52. core.ConfigLogger: logger,
  53. }))
  54. assert.Equal(t, logger, sh.l)
  55. assert.Equal(t, []string{uast_items.FeatureUast}, sh.Features())
  56. }
  57. func TestShotnessRegistration(t *testing.T) {
  58. summoned := core.Registry.Summon((&ShotnessAnalysis{}).Name())
  59. assert.Len(t, summoned, 1)
  60. assert.Equal(t, summoned[0].Name(), "Shotness")
  61. leaves := core.Registry.GetLeaves()
  62. matched := false
  63. for _, tp := range leaves {
  64. if tp.Flag() == (&ShotnessAnalysis{}).Flag() {
  65. matched = true
  66. break
  67. }
  68. }
  69. assert.True(t, matched)
  70. }
  71. func loadUast(t *testing.T, name string) nodes.Node {
  72. filename := path.Join("..", "internal", "test_data", name)
  73. reader, err := os.Open(filename)
  74. if err != nil {
  75. assert.Failf(t, "cannot load %s: %v", filename, err)
  76. }
  77. node, err := nodesproto.ReadTree(reader)
  78. if err != nil {
  79. assert.Failf(t, "cannot load %s: %v", filename, err)
  80. }
  81. return node
  82. }
  83. func bakeShotness(t *testing.T, eraseEndPosition bool) (*ShotnessAnalysis, ShotnessResult) {
  84. sh := fixtureShotness()
  85. bytes1, err := ioutil.ReadFile(path.Join("..", "internal", "test_data", "1.java"))
  86. assert.Nil(t, err)
  87. bytes2, err := ioutil.ReadFile(path.Join("..", "internal", "test_data", "2.java"))
  88. assert.Nil(t, err)
  89. dmp := diffmatchpatch.New()
  90. dmp.DiffTimeout = time.Hour
  91. src, dst, _ := dmp.DiffLinesToRunes(string(bytes1), string(bytes2))
  92. state := map[string]interface{}{}
  93. state[core.DependencyCommit] = &object.Commit{}
  94. fileDiffs := map[string]items.FileDiffData{}
  95. const fileName = "test.java"
  96. fileDiffs[fileName] = items.FileDiffData{
  97. OldLinesOfCode: len(src),
  98. NewLinesOfCode: len(dst),
  99. Diffs: dmp.DiffMainRunes(src, dst, false),
  100. }
  101. state[items.DependencyFileDiff] = fileDiffs
  102. uastChanges := make([]uast_items.Change, 1)
  103. myLoadUast := func(name string) nodes.Node {
  104. node := loadUast(t, name)
  105. if eraseEndPosition {
  106. uast_items.VisitEachNode(node, func(child nodes.Node) {
  107. obj, _ := child.(nodes.Object)[uast.KeyPos].(nodes.Object)
  108. if len(obj) == 0 {
  109. return
  110. }
  111. obj[uast.KeyEnd] = nil
  112. })
  113. }
  114. return node
  115. }
  116. state[uast_items.DependencyUastChanges] = uastChanges
  117. uastChanges[0] = uast_items.Change{
  118. Change: &object.Change{
  119. From: object.ChangeEntry{},
  120. To: object.ChangeEntry{Name: fileName}},
  121. Before: nil, After: myLoadUast("uast1.pb"),
  122. }
  123. iresult, err := sh.Consume(state)
  124. assert.Nil(t, err)
  125. assert.Nil(t, iresult)
  126. uastChanges[0] = uast_items.Change{
  127. Change: &object.Change{
  128. From: object.ChangeEntry{Name: fileName},
  129. To: object.ChangeEntry{Name: fileName}},
  130. Before: myLoadUast("uast1.pb"), After: myLoadUast("uast2.pb"),
  131. }
  132. iresult, err = sh.Consume(state)
  133. assert.Nil(t, err)
  134. assert.Nil(t, iresult)
  135. return sh, sh.Finalize().(ShotnessResult)
  136. }
  137. func TestShotnessConsume(t *testing.T) {
  138. sh := fixtureShotness()
  139. bytes1, err := ioutil.ReadFile(path.Join("..", "internal", "test_data", "1.java"))
  140. assert.Nil(t, err)
  141. bytes2, err := ioutil.ReadFile(path.Join("..", "internal", "test_data", "2.java"))
  142. assert.Nil(t, err)
  143. dmp := diffmatchpatch.New()
  144. dmp.DiffTimeout = time.Hour
  145. src, dst, _ := dmp.DiffLinesToRunes(string(bytes1), string(bytes2))
  146. state := map[string]interface{}{}
  147. state[core.DependencyCommit] = &object.Commit{}
  148. fileDiffs := map[string]items.FileDiffData{}
  149. const fileName = "test.java"
  150. const newfileName = "new.java"
  151. fileDiffs[fileName] = items.FileDiffData{
  152. OldLinesOfCode: len(src),
  153. NewLinesOfCode: len(dst),
  154. Diffs: dmp.DiffMainRunes(src, dst, false),
  155. }
  156. state[items.DependencyFileDiff] = fileDiffs
  157. uastChanges := make([]uast_items.Change, 1)
  158. state[uast_items.DependencyUastChanges] = uastChanges
  159. uastChanges[0] = uast_items.Change{
  160. Change: &object.Change{
  161. From: object.ChangeEntry{},
  162. To: object.ChangeEntry{Name: fileName}},
  163. Before: nil, After: loadUast(t, "uast1.pb"),
  164. }
  165. iresult, err := sh.Consume(state)
  166. assert.Nil(t, err)
  167. assert.Nil(t, iresult)
  168. uastChanges[0] = uast_items.Change{
  169. Change: &object.Change{
  170. From: object.ChangeEntry{Name: fileName},
  171. To: object.ChangeEntry{Name: newfileName}},
  172. Before: loadUast(t, "uast1.pb"), After: loadUast(t, "uast2.pb"),
  173. }
  174. fileDiffs[newfileName] = fileDiffs[fileName]
  175. delete(fileDiffs, fileName)
  176. iresult, err = sh.Consume(state)
  177. assert.Nil(t, err)
  178. assert.Nil(t, iresult)
  179. assert.Len(t, sh.nodes, 18)
  180. assert.Len(t, sh.files, 1)
  181. assert.Len(t, sh.files["new.java"], 18)
  182. for _, node := range sh.nodes {
  183. assert.Equal(t, node.Summary.Type, "uast:FunctionGroup")
  184. if node.Summary.Name != "testUnpackEntryFromFile" {
  185. assert.Equal(t, node.Count, 1)
  186. if node.Summary.Name != "testUnpackEntryFromStreamToFile" {
  187. assert.Len(t, node.Couples, 16)
  188. } else {
  189. assert.Len(t, node.Couples, 1)
  190. }
  191. } else {
  192. assert.Equal(t, node.Count, 2)
  193. assert.Len(t, node.Couples, 17)
  194. }
  195. }
  196. result := sh.Finalize().(ShotnessResult)
  197. assert.Len(t, result.Nodes, 18)
  198. assert.Len(t, result.Counters, 18)
  199. if len(result.Nodes) != 18 || len(result.Counters) != 18 {
  200. t.FailNow()
  201. }
  202. assert.Equal(t, result.Nodes[14].String(),
  203. "uast:FunctionGroup_testUnpackEntryFromStreamToFile_"+newfileName)
  204. assert.Equal(t, result.Counters[14], map[int]int{14: 1, 13: 1})
  205. assert.Equal(t, result.Nodes[15].String(),
  206. "uast:FunctionGroup_testUnpackEntryFromStream_"+newfileName)
  207. assert.Equal(t, result.Counters[15], map[int]int{
  208. 8: 1, 0: 1, 5: 1, 6: 1, 11: 1, 1: 1, 13: 1, 17: 1, 3: 1, 15: 1, 9: 1, 4: 1, 7: 1, 16: 1, 2: 1, 12: 1, 10: 1})
  209. uastChanges[0] = uast_items.Change{
  210. Change: &object.Change{
  211. From: object.ChangeEntry{Name: newfileName},
  212. To: object.ChangeEntry{}},
  213. Before: loadUast(t, "uast2.pb"), After: nil,
  214. }
  215. iresult, err = sh.Consume(state)
  216. assert.Nil(t, err)
  217. assert.Nil(t, iresult)
  218. assert.Len(t, sh.nodes, 0)
  219. assert.Len(t, sh.files, 0)
  220. }
  221. func TestShotnessFork(t *testing.T) {
  222. sh1 := fixtureShotness()
  223. clones := sh1.Fork(1)
  224. assert.Len(t, clones, 1)
  225. sh2 := clones[0].(*ShotnessAnalysis)
  226. assert.True(t, sh1 == sh2)
  227. sh1.Merge([]core.PipelineItem{sh2})
  228. }
  229. func TestShotnessConsumeNoEnd(t *testing.T) {
  230. _, result1 := bakeShotness(t, false)
  231. _, result2 := bakeShotness(t, true)
  232. assert.Equal(t, result1, result2)
  233. }
  234. func TestShotnessSerializeText(t *testing.T) {
  235. sh, result := bakeShotness(t, false)
  236. buffer := &bytes.Buffer{}
  237. assert.Nil(t, sh.Serialize(result, false, buffer))
  238. assert.Equal(t, buffer.String(), ` - name: testAddEntry
  239. file: test.java
  240. internal_role: uast:FunctionGroup
  241. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  242. - name: testArchiveEquals
  243. file: test.java
  244. internal_role: uast:FunctionGroup
  245. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  246. - name: testContainsAnyEntry
  247. file: test.java
  248. internal_role: uast:FunctionGroup
  249. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  250. - name: testDuplicateEntryAtAddOrReplace
  251. file: test.java
  252. internal_role: uast:FunctionGroup
  253. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  254. - name: testDuplicateEntryAtAdd
  255. file: test.java
  256. internal_role: uast:FunctionGroup
  257. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  258. - name: testDuplicateEntryAtReplace
  259. file: test.java
  260. internal_role: uast:FunctionGroup
  261. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  262. - name: testPackEntries
  263. file: test.java
  264. internal_role: uast:FunctionGroup
  265. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  266. - name: testPackEntry
  267. file: test.java
  268. internal_role: uast:FunctionGroup
  269. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  270. - name: testPreserveRoot
  271. file: test.java
  272. internal_role: uast:FunctionGroup
  273. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  274. - name: testRemoveDirs
  275. file: test.java
  276. internal_role: uast:FunctionGroup
  277. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  278. - name: testRemoveEntry
  279. file: test.java
  280. internal_role: uast:FunctionGroup
  281. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  282. - name: testRepackArchive
  283. file: test.java
  284. internal_role: uast:FunctionGroup
  285. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  286. - name: testUnexplode
  287. file: test.java
  288. internal_role: uast:FunctionGroup
  289. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  290. - name: testUnpackEntryFromFile
  291. file: test.java
  292. internal_role: uast:FunctionGroup
  293. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":2,"14":1,"15":1,"16":1,"17":1}
  294. - name: testUnpackEntryFromStreamToFile
  295. file: test.java
  296. internal_role: uast:FunctionGroup
  297. counters: {"13":1,"14":1}
  298. - name: testUnpackEntryFromStream
  299. file: test.java
  300. internal_role: uast:FunctionGroup
  301. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  302. - name: testZipException
  303. file: test.java
  304. internal_role: uast:FunctionGroup
  305. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  306. - name: unexplodeWithException
  307. file: test.java
  308. internal_role: uast:FunctionGroup
  309. counters: {"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"15":1,"16":1,"17":1}
  310. `)
  311. }
  312. func TestShotnessSerializeBinary(t *testing.T) {
  313. sh, result := bakeShotness(t, false)
  314. buffer := &bytes.Buffer{}
  315. assert.Nil(t, sh.Serialize(result, true, buffer))
  316. message := pb.ShotnessAnalysisResults{}
  317. err := proto.Unmarshal(buffer.Bytes(), &message)
  318. assert.Nil(t, err)
  319. assert.Len(t, message.Records, 18)
  320. assert.Equal(t, message.Records[14].Name, "testUnpackEntryFromStreamToFile")
  321. assert.Equal(t, message.Records[14].Counters, map[int32]int32{14: 1, 13: 1})
  322. }