shotness_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. package hercules
  2. import (
  3. "io/ioutil"
  4. "path"
  5. "testing"
  6. "bytes"
  7. "github.com/gogo/protobuf/proto"
  8. "github.com/sergi/go-diff/diffmatchpatch"
  9. "github.com/stretchr/testify/assert"
  10. "gopkg.in/bblfsh/sdk.v1/uast"
  11. "gopkg.in/src-d/go-git.v4/plumbing/object"
  12. "gopkg.in/src-d/hercules.v3/pb"
  13. )
  14. func fixtureShotness() *ShotnessAnalysis {
  15. sh := &ShotnessAnalysis{}
  16. sh.Initialize(testRepository)
  17. sh.Configure(nil)
  18. return sh
  19. }
  20. func TestShotnessMeta(t *testing.T) {
  21. sh := &ShotnessAnalysis{}
  22. sh.Initialize(testRepository)
  23. assert.NotNil(t, sh.nodes)
  24. assert.NotNil(t, sh.files)
  25. assert.Equal(t, sh.Name(), "Shotness")
  26. assert.Len(t, sh.Provides(), 0)
  27. assert.Equal(t, len(sh.Requires()), 2)
  28. assert.Equal(t, sh.Requires()[0], DependencyFileDiff)
  29. assert.Equal(t, sh.Requires()[1], DependencyUastChanges)
  30. assert.Len(t, sh.ListConfigurationOptions(), 2)
  31. assert.Equal(t, sh.ListConfigurationOptions()[0].Name, ConfigShotnessXpathStruct)
  32. assert.Equal(t, sh.ListConfigurationOptions()[1].Name, ConfigShotnessXpathName)
  33. sh.Configure(nil)
  34. assert.Equal(t, sh.XpathStruct, DefaultShotnessXpathStruct)
  35. assert.Equal(t, sh.XpathName, DefaultShotnessXpathName)
  36. facts := map[string]interface{}{}
  37. facts[ConfigShotnessXpathStruct] = "xpath!"
  38. facts[ConfigShotnessXpathName] = "another!"
  39. sh.Configure(facts)
  40. assert.Equal(t, sh.XpathStruct, "xpath!")
  41. assert.Equal(t, sh.XpathName, "another!")
  42. features := sh.Features()
  43. assert.Len(t, features, 1)
  44. assert.Equal(t, features[0], FeatureUast)
  45. }
  46. func TestShotnessRegistration(t *testing.T) {
  47. tp, exists := Registry.registered[(&ShotnessAnalysis{}).Name()]
  48. assert.True(t, exists)
  49. assert.Equal(t, tp.Elem().Name(), "ShotnessAnalysis")
  50. tp, exists = Registry.flags[(&ShotnessAnalysis{}).Flag()]
  51. assert.True(t, exists)
  52. assert.Equal(t, tp.Elem().Name(), "ShotnessAnalysis")
  53. }
  54. func bakeShotness(t *testing.T) (*ShotnessAnalysis, ShotnessAnalysisResult) {
  55. sh := fixtureShotness()
  56. bytes1, err := ioutil.ReadFile(path.Join("test_data", "1.java"))
  57. assert.Nil(t, err)
  58. bytes2, err := ioutil.ReadFile(path.Join("test_data", "2.java"))
  59. assert.Nil(t, err)
  60. dmp := diffmatchpatch.New()
  61. src, dst, _ := dmp.DiffLinesToRunes(string(bytes1), string(bytes2))
  62. state := map[string]interface{}{}
  63. state["commit"] = &object.Commit{}
  64. fileDiffs := map[string]FileDiffData{}
  65. const fileName = "test.java"
  66. fileDiffs[fileName] = FileDiffData{
  67. OldLinesOfCode: len(src),
  68. NewLinesOfCode: len(dst),
  69. Diffs: dmp.DiffMainRunes(src, dst, false),
  70. }
  71. state[DependencyFileDiff] = fileDiffs
  72. uastChanges := make([]UASTChange, 1)
  73. loadUast := func(name string) *uast.Node {
  74. bytes, err := ioutil.ReadFile(path.Join("test_data", name))
  75. assert.Nil(t, err)
  76. node := uast.Node{}
  77. proto.Unmarshal(bytes, &node)
  78. return &node
  79. }
  80. state[DependencyUastChanges] = uastChanges
  81. uastChanges[0] = UASTChange{
  82. Change: &object.Change{
  83. From: object.ChangeEntry{},
  84. To: object.ChangeEntry{Name: fileName}},
  85. Before: nil, After: loadUast("uast1.pb"),
  86. }
  87. iresult, err := sh.Consume(state)
  88. assert.Nil(t, err)
  89. assert.Nil(t, iresult)
  90. uastChanges[0] = UASTChange{
  91. Change: &object.Change{
  92. From: object.ChangeEntry{Name: fileName},
  93. To: object.ChangeEntry{Name: fileName}},
  94. Before: loadUast("uast1.pb"), After: loadUast("uast2.pb"),
  95. }
  96. iresult, err = sh.Consume(state)
  97. assert.Nil(t, err)
  98. assert.Nil(t, iresult)
  99. return sh, sh.Finalize().(ShotnessAnalysisResult)
  100. }
  101. func TestShotnessConsume(t *testing.T) {
  102. sh := fixtureShotness()
  103. bytes1, err := ioutil.ReadFile(path.Join("test_data", "1.java"))
  104. assert.Nil(t, err)
  105. bytes2, err := ioutil.ReadFile(path.Join("test_data", "2.java"))
  106. assert.Nil(t, err)
  107. dmp := diffmatchpatch.New()
  108. src, dst, _ := dmp.DiffLinesToRunes(string(bytes1), string(bytes2))
  109. state := map[string]interface{}{}
  110. state["commit"] = &object.Commit{}
  111. fileDiffs := map[string]FileDiffData{}
  112. const fileName = "test.java"
  113. const newfileName = "new.java"
  114. fileDiffs[fileName] = FileDiffData{
  115. OldLinesOfCode: len(src),
  116. NewLinesOfCode: len(dst),
  117. Diffs: dmp.DiffMainRunes(src, dst, false),
  118. }
  119. state[DependencyFileDiff] = fileDiffs
  120. uastChanges := make([]UASTChange, 1)
  121. loadUast := func(name string) *uast.Node {
  122. bytes, err := ioutil.ReadFile(path.Join("test_data", name))
  123. assert.Nil(t, err)
  124. node := uast.Node{}
  125. proto.Unmarshal(bytes, &node)
  126. return &node
  127. }
  128. state[DependencyUastChanges] = uastChanges
  129. uastChanges[0] = UASTChange{
  130. Change: &object.Change{
  131. From: object.ChangeEntry{},
  132. To: object.ChangeEntry{Name: fileName}},
  133. Before: nil, After: loadUast("uast1.pb"),
  134. }
  135. iresult, err := sh.Consume(state)
  136. assert.Nil(t, err)
  137. assert.Nil(t, iresult)
  138. uastChanges[0] = UASTChange{
  139. Change: &object.Change{
  140. From: object.ChangeEntry{Name: fileName},
  141. To: object.ChangeEntry{Name: newfileName}},
  142. Before: loadUast("uast1.pb"), After: loadUast("uast2.pb"),
  143. }
  144. fileDiffs[newfileName] = fileDiffs[fileName]
  145. delete(fileDiffs, fileName)
  146. iresult, err = sh.Consume(state)
  147. assert.Nil(t, err)
  148. assert.Nil(t, iresult)
  149. assert.Len(t, sh.nodes, 18)
  150. assert.Len(t, sh.files, 1)
  151. assert.Len(t, sh.files["new.java"], 18)
  152. for _, node := range sh.nodes {
  153. assert.Equal(t, node.Summary.InternalRole, "MethodDeclaration")
  154. if node.Summary.Name != "testUnpackEntryFromFile" {
  155. assert.Equal(t, node.Count, 1)
  156. if node.Summary.Name != "testUnpackEntryFromStreamToFile" {
  157. assert.Len(t, node.Couples, 16)
  158. } else {
  159. assert.Len(t, node.Couples, 1)
  160. }
  161. } else {
  162. assert.Equal(t, node.Count, 2)
  163. assert.Len(t, node.Couples, 17)
  164. }
  165. }
  166. result := sh.Finalize().(ShotnessAnalysisResult)
  167. assert.Len(t, result.Nodes, 18)
  168. assert.Len(t, result.Counters, 18)
  169. assert.Equal(t, result.Nodes[14].String(),
  170. "MethodDeclaration_testUnpackEntryFromStreamToFile_"+newfileName)
  171. assert.Equal(t, result.Counters[14], map[int]int{14: 1, 13: 1})
  172. assert.Equal(t, result.Nodes[15].String(),
  173. "MethodDeclaration_testUnpackEntryFromStream_"+newfileName)
  174. assert.Equal(t, result.Counters[15], map[int]int{
  175. 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})
  176. uastChanges[0] = UASTChange{
  177. Change: &object.Change{
  178. From: object.ChangeEntry{Name: newfileName},
  179. To: object.ChangeEntry{}},
  180. Before: loadUast("uast2.pb"), After: nil,
  181. }
  182. iresult, err = sh.Consume(state)
  183. assert.Nil(t, err)
  184. assert.Nil(t, iresult)
  185. assert.Len(t, sh.nodes, 0)
  186. assert.Len(t, sh.files, 0)
  187. }
  188. func TestShotnessSerializeText(t *testing.T) {
  189. sh, result := bakeShotness(t)
  190. buffer := &bytes.Buffer{}
  191. sh.Serialize(result, false, buffer)
  192. assert.Equal(t, buffer.String(), ` - name: testAddEntry
  193. file: test.java
  194. ir: MethodDeclaration
  195. roles: [111,59,41,45]
  196. 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}
  197. - name: testArchiveEquals
  198. file: test.java
  199. ir: MethodDeclaration
  200. roles: [111,59,41,45]
  201. 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}
  202. - name: testContainsAnyEntry
  203. file: test.java
  204. ir: MethodDeclaration
  205. roles: [111,59,41,45]
  206. 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}
  207. - name: testDuplicateEntryAtAddOrReplace
  208. file: test.java
  209. ir: MethodDeclaration
  210. roles: [111,59,41,45]
  211. 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}
  212. - name: testDuplicateEntryAtAdd
  213. file: test.java
  214. ir: MethodDeclaration
  215. roles: [111,59,41,45]
  216. 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}
  217. - name: testDuplicateEntryAtReplace
  218. file: test.java
  219. ir: MethodDeclaration
  220. roles: [111,59,41,45]
  221. 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}
  222. - name: testPackEntries
  223. file: test.java
  224. ir: MethodDeclaration
  225. roles: [111,59,41,45]
  226. 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}
  227. - name: testPackEntry
  228. file: test.java
  229. ir: MethodDeclaration
  230. roles: [111,59,41,45]
  231. 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}
  232. - name: testPreserveRoot
  233. file: test.java
  234. ir: MethodDeclaration
  235. roles: [111,59,41,45]
  236. 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}
  237. - name: testRemoveDirs
  238. file: test.java
  239. ir: MethodDeclaration
  240. roles: [111,59,41,45]
  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: testRemoveEntry
  243. file: test.java
  244. ir: MethodDeclaration
  245. roles: [111,59,41,45]
  246. 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}
  247. - name: testRepackArchive
  248. file: test.java
  249. ir: MethodDeclaration
  250. roles: [111,59,41,45]
  251. 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}
  252. - name: testUnexplode
  253. file: test.java
  254. ir: MethodDeclaration
  255. roles: [111,59,41,45]
  256. 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}
  257. - name: testUnpackEntryFromFile
  258. file: test.java
  259. ir: MethodDeclaration
  260. roles: [111,59,41,45]
  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:2,14:1,15:1,16:1,17:1}
  262. - name: testUnpackEntryFromStreamToFile
  263. file: test.java
  264. ir: MethodDeclaration
  265. roles: [111,59,41,45]
  266. counters: {13:1,14:1}
  267. - name: testUnpackEntryFromStream
  268. file: test.java
  269. ir: MethodDeclaration
  270. roles: [111,59,41,45]
  271. 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}
  272. - name: testZipException
  273. file: test.java
  274. ir: MethodDeclaration
  275. roles: [111,59,41,45]
  276. 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}
  277. - name: unexplodeWithException
  278. file: test.java
  279. ir: MethodDeclaration
  280. roles: [111,100,41,45]
  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. `)
  283. }
  284. func TestShotnessSerializeBinary(t *testing.T) {
  285. sh, result := bakeShotness(t)
  286. buffer := &bytes.Buffer{}
  287. sh.Serialize(result, true, buffer)
  288. message := pb.ShotnessAnalysisResultMessage{}
  289. err := proto.Unmarshal(buffer.Bytes(), &message)
  290. assert.Nil(t, err)
  291. assert.Len(t, message.Records, 18)
  292. assert.Equal(t, message.Records[14].Name, "testUnpackEntryFromStreamToFile")
  293. assert.Equal(t, message.Records[14].Counters, map[int32]int32{14: 1, 13: 1})
  294. }