PageRenderTime 25ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/subprojects/groovy-console/src/test/groovy/groovy/inspect/swingui/ScriptToTreeNodeAdapterTest.groovy

https://github.com/groovy/groovy-core
Groovy | 561 lines | 437 code | 69 blank | 55 comment | 11 complexity | 00fb76c1e3482425ee915276bf4d90e0 MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package groovy.inspect.swingui
  20. import org.codehaus.groovy.control.CompilePhase
  21. import org.codehaus.groovy.control.Phases
  22. import javax.swing.tree.TreeNode
  23. import junit.framework.AssertionFailedError
  24. /**
  25. * Unit test for ScriptToTreeNodeAdapter.
  26. *
  27. * The assertions in this test case often assert against the toString() representation of
  28. * an object. Normally, this is bad form. However, the class under test is meant to display
  29. * toString() forms in a user interface. So in this case it is appropriate.
  30. *
  31. * @author Hamlet D'Arcy
  32. */
  33. class ScriptToTreeNodeAdapterTest extends GroovyTestCase {
  34. private final classLoader = new GroovyClassLoader()
  35. private createAdapter(showScriptFreeForm, showScriptClass, showClosureClasses) {
  36. def nodeMaker = new SwingTreeNodeMaker()
  37. new ScriptToTreeNodeAdapter(classLoader, showScriptFreeForm, showScriptClass, showClosureClasses, nodeMaker)
  38. }
  39. /**
  40. * Asserts that a given script produces the expected tree like
  41. * structure.
  42. */
  43. private assertTreeStructure(String script, List<Closure> specification) {
  44. ScriptToTreeNodeAdapter adapter = createAdapter(true, true, true)
  45. assertTreeStructure(script, specification, adapter)
  46. }
  47. private assertTreeStructure(String script, List<Closure> specification, ScriptToTreeNodeAdapter adapter) {
  48. assertTreeStructure(script, CompilePhase.SEMANTIC_ANALYSIS, specification, adapter)
  49. }
  50. private assertTreeStructure(String script, CompilePhase compilePhase, List<Closure> specification, ScriptToTreeNodeAdapter adapter) {
  51. TreeNode root = adapter.compile(script, compilePhase.phaseNumber)
  52. def original = root
  53. def lastSpec = 0
  54. specification.each { spec ->
  55. if (root) lastSpec++
  56. root = root?.children()?.find { spec(it) }
  57. }
  58. if (!root) {
  59. fail("Could not locate Expression in AST.${printSpecs(specification, lastSpec)}\nAST: ${printnode(original)}")
  60. }
  61. }
  62. private printSpecs(specification, index) {
  63. def len = specification.size()
  64. (index > 1 ? '\nPassed: ' : '') + specification[0..<index].collect{ extractSpecType(it.class.name) }.join(', ') +
  65. '\nFailed: ' + extractSpecType(specification[index-1].class.name) +
  66. (index < len ? '\nIgnored: ' + specification[index..-1].collect{ extractSpecType(it.class.name) }.join(', ') : '')
  67. }
  68. private extractSpecType(fullyQualified) {
  69. def start = fullyQualified.indexOf('$_') + 2
  70. def end = fullyQualified.indexOf('_', start)
  71. fullyQualified[start..end]
  72. }
  73. /**
  74. * Helper method to assert a map entry element.
  75. */
  76. private assertMapEntry(mapEntry, expectedKey, expectedValue) {
  77. assertNotNull('Could not locate 1st MapEntryExpression in AST', mapEntry)
  78. assertEquals('Wrong # map entries', 2, mapEntry.children.size())
  79. assertEquals('Wrong key', expectedKey, mapEntry.children[0].toString())
  80. assertEquals('Wrong value', expectedValue, mapEntry.children[1].toString())
  81. }
  82. /**
  83. * Returns a function that tests for Groovy Truth equality.
  84. */
  85. private eq(String target) {
  86. return { it.toString() == target }
  87. }
  88. /**
  89. * Helper method to print out the TreeNode to a test form in system out.
  90. * Warning, this uses recursion.
  91. */
  92. private printnode(TreeNode node, String prefix = '') {
  93. def buffer = new StringBuffer()
  94. buffer << '\n' << prefix << node <<
  95. node.children().each {
  96. buffer << printnode(it, prefix + ' ')
  97. }
  98. buffer
  99. }
  100. /**
  101. * Returns a function that acts much like String#startsWith for Objects.
  102. */
  103. private startsWith(String target) {
  104. return { it.toString().startsWith(target) }
  105. }
  106. /**
  107. * Returns a function that acts much like String#contains for Objects.
  108. */
  109. private contains(String target) {
  110. return {
  111. it.toString().contains(target)
  112. }
  113. }
  114. void testHelloWorld() {
  115. assertTreeStructure(
  116. '"Hello World"',
  117. [
  118. eq('BlockStatement - (1)'),
  119. startsWith('ExpressionStatement'),
  120. startsWith('Constant - Hello World : java.lang.String')
  121. ])
  122. }
  123. void testSimpleClass() {
  124. assertTreeStructure(
  125. ' class Foo { public aField } ',
  126. [
  127. eq('ClassNode - Foo'),
  128. eq('Fields'),
  129. eq('FieldNode - aField : java.lang.Object'),
  130. ]
  131. )
  132. }
  133. void testMethodWithParameter() {
  134. assertTreeStructure(
  135. ' def foo(String bar) { println bar } ',
  136. [
  137. startsWith('ClassNode - script'),
  138. eq('Methods'),
  139. eq('MethodNode - foo'),
  140. eq('Parameter - bar'),
  141. ]
  142. )
  143. }
  144. void testMethodWithParameterAndInitialValue() {
  145. assertTreeStructure(
  146. ' def foo(String bar = "some_value") { println bar } ',
  147. [
  148. startsWith('ClassNode - script'),
  149. eq('Methods'),
  150. eq('MethodNode - foo'),
  151. eq('Parameter - bar'),
  152. eq('Constant - some_value : java.lang.String'),
  153. ]
  154. )
  155. }
  156. void testClosureParameters() {
  157. assertTreeStructure(
  158. ' def x = { parm1 -> println parm1 } ',
  159. [
  160. eq('BlockStatement - (1)'),
  161. startsWith('ExpressionStatement'),
  162. startsWith('Declaration - (x ='),
  163. startsWith('ClosureExpression'),
  164. startsWith('Parameter - parm1'),
  165. ]
  166. )
  167. }
  168. void testClosureParametersWithInitialValue() {
  169. assertTreeStructure(
  170. ' def x = { parm1 = "some_value" -> println parm1 } ',
  171. [
  172. eq('BlockStatement - (1)'),
  173. startsWith('ExpressionStatement'),
  174. startsWith('Declaration - (x ='),
  175. eq('ClosureExpression'),
  176. startsWith('Parameter - parm1'),
  177. startsWith('Constant - some_value : java.lang.String'),
  178. ]
  179. )
  180. }
  181. void testNamedArgumentListExpression() {
  182. def script = "new String(foo: 'bar', baz: 'qux')"
  183. ScriptToTreeNodeAdapter adapter = createAdapter(true, true, true)
  184. TreeNode root = adapter.compile(script, Phases.SEMANTIC_ANALYSIS)
  185. def namedArgList = root.children()?.find {
  186. it.toString().startsWith('BlockStatement')
  187. }?.children()?.find {
  188. it.toString().startsWith 'ExpressionStatement'
  189. }?.children()?.find {
  190. it.toString().startsWith 'ConstructorCall'
  191. }?.children()?.find {
  192. it.toString().startsWith 'Tuple'
  193. }?.children()?.find {
  194. it.toString().startsWith 'NamedArgumentListExpression'
  195. }
  196. assertNotNull('Could not locate NamedArgumentListExpression in AST', namedArgList)
  197. assertEquals('Wrong # named arguments', 2, namedArgList.children.size())
  198. assertMapEntry(namedArgList.children[0], 'Constant - foo : java.lang.String', 'Constant - bar : java.lang.String')
  199. assertMapEntry(namedArgList.children[1], 'Constant - baz : java.lang.String', 'Constant - qux : java.lang.String')
  200. }
  201. void testDynamicVariable() {
  202. assertTreeStructure(
  203. " foo = 'bar' ",
  204. [
  205. eq('BlockStatement - (1)'),
  206. eq('ExpressionStatement - BinaryExpression'),
  207. eq('Binary - (foo = bar)'),
  208. eq('Variable - foo : java.lang.Object'),
  209. eq('DynamicVariable - foo'),
  210. ]
  211. )
  212. }
  213. void testVariableParameters() {
  214. assertTreeStructure(
  215. " 'foo' ",
  216. [
  217. startsWith('ClassNode'),
  218. eq('Methods'),
  219. startsWith('MethodNode - run'),
  220. startsWith('BlockStatement'),
  221. startsWith('ExpressionStatement'),
  222. startsWith('Constant - foo'),
  223. ]
  224. )
  225. }
  226. void testMultipleAssignments() {
  227. assertTreeStructure(
  228. ' def (x, y) = [1, 2] ',
  229. [
  230. startsWith('BlockStatement'),
  231. startsWith('ExpressionStatement'),
  232. eq('Declaration - ((x, y) = [1, 2])'),
  233. eq('ArgumentList - (x, y)'),
  234. ]
  235. )
  236. }
  237. void testEnum() {
  238. assertTreeStructure(
  239. '''enum MyEnum {
  240. FOO,
  241. BAR;
  242. }''',
  243. [
  244. eq('ClassNode - MyEnum'),
  245. eq('Fields'),
  246. startsWith('FieldNode - FOO : MyEnum'),
  247. ])
  248. }
  249. void testExpression_DuplicateDoesNotAppear() {
  250. assertTreeStructure(
  251. " 'foo' ",
  252. [
  253. startsWith('ClassNode'),
  254. eq('Methods'),
  255. eq('MethodNode - main'),
  256. startsWith('ExpressionStatement'), //notice, there is only one ExpressionStatement
  257. startsWith('MethodCall'),
  258. ]
  259. )
  260. }
  261. void testInnerClass() {
  262. assertTreeStructure(
  263. '''class Outer {
  264. private class Inner1 {
  265. }
  266. private class Inner2 {
  267. }
  268. }''',
  269. [
  270. startsWith('InnerClassNode - Outer\$Inner2'),
  271. ]
  272. )
  273. }
  274. void testInnerClassWithMethods() {
  275. assertTreeStructure(
  276. '''class Outer {
  277. private class Inner {
  278. def someMethod() {}
  279. }
  280. }''',
  281. [
  282. startsWith('InnerClassNode - Outer\$Inner'),
  283. eq('Methods'),
  284. startsWith('MethodNode - someMethod'),
  285. startsWith('BlockStatement'),
  286. ]
  287. )
  288. }
  289. void testInnerClassWithFields() {
  290. assertTreeStructure(
  291. '''class Outer {
  292. private class Inner {
  293. String field
  294. }
  295. }''',
  296. [
  297. startsWith('InnerClassNode - Outer\$Inner'),
  298. eq('Fields'),
  299. startsWith('FieldNode - field'),
  300. ]
  301. )
  302. }
  303. void testInnerClassWithAnnotations() {
  304. assertTreeStructure(
  305. '''class Outer {
  306. @Singleton
  307. private class Inner {
  308. }
  309. }''',
  310. [
  311. startsWith('InnerClassNode - Outer\$Inner'),
  312. eq('Annotations'),
  313. startsWith('AnnotationNode - groovy.lang.Singleton'),
  314. ]
  315. )
  316. }
  317. void testInnerClassWithProperties() {
  318. assertTreeStructure(
  319. '''class Outer {
  320. private class Inner {
  321. def property
  322. }
  323. }''',
  324. [
  325. startsWith('InnerClassNode - Outer\$Inner'),
  326. eq('Properties'),
  327. startsWith('PropertyNode - property'),
  328. ]
  329. )
  330. }
  331. void testScriptWithMethods() {
  332. // verify the free form script
  333. assertTreeStructure(
  334. 'def foo(String bar) {}',
  335. [
  336. eq('Methods'),
  337. eq('MethodNode - foo'),
  338. eq('Parameter - bar'),
  339. ]
  340. )
  341. // verify the script's class
  342. assertTreeStructure(
  343. 'def foo(String bar) {}',
  344. [
  345. startsWith('ClassNode - script'),
  346. eq('Methods'),
  347. eq('MethodNode - foo'),
  348. eq('Parameter - bar'),
  349. ]
  350. )
  351. }
  352. void testScriptWithAdapterThatLoadsClassButNotFreeForm() {
  353. ScriptToTreeNodeAdapter adapter = createAdapter(false, true, true)
  354. // since free standing script is not being loaded, it should fail
  355. shouldFail(AssertionFailedError) {
  356. assertTreeStructure(
  357. 'def foo(String bar) {}',
  358. [
  359. eq('Methods'),
  360. eq('MethodNode - foo'),
  361. eq('Parameter - bar'),
  362. ],
  363. adapter
  364. )
  365. }
  366. // since script class is being loaded, it should go through
  367. assertTreeStructure(
  368. 'def foo(String bar) {}',
  369. [
  370. startsWith('ClassNode - script'),
  371. eq('Methods'),
  372. eq('MethodNode - foo'),
  373. eq('Parameter - bar'),
  374. ],
  375. adapter
  376. )
  377. }
  378. void testScriptWithAdapterThatLoadsFreeFormButNotClass() {
  379. ScriptToTreeNodeAdapter adapter = createAdapter(true, false, false)
  380. // since free standing script is being loaded, it should go through
  381. assertTreeStructure(
  382. 'def foo(String bar) {}',
  383. [
  384. eq('Methods'),
  385. eq('MethodNode - foo'),
  386. eq('Parameter - bar'),
  387. ],
  388. adapter
  389. )
  390. // since script class is not being loaded, it should fail
  391. shouldFail(AssertionFailedError) {
  392. assertTreeStructure(
  393. 'def foo(String bar) {}',
  394. [
  395. startsWith('ClassNode - script'),
  396. eq('Methods'),
  397. eq('MethodNode - foo'),
  398. eq('Parameter - bar'),
  399. ],
  400. adapter
  401. )
  402. }
  403. }
  404. void testScriptWithAdapterThatLoadsNitherFreeFormNorClass() {
  405. ScriptToTreeNodeAdapter adapter = createAdapter(false, false, false)
  406. // since free standing script is not being loaded, it should fail
  407. shouldFail(AssertionFailedError) {
  408. assertTreeStructure(
  409. 'def foo(String bar) {}',
  410. [
  411. eq('Methods'),
  412. eq('MethodNode - foo'),
  413. eq('Parameter - bar'),
  414. ],
  415. adapter
  416. )
  417. }
  418. // since script class is not being loaded, it should fail
  419. shouldFail(AssertionFailedError) {
  420. assertTreeStructure(
  421. 'def foo(String bar) {}',
  422. [
  423. startsWith('ClassNode - script'),
  424. eq('Methods'),
  425. eq('MethodNode - foo'),
  426. eq('Parameter - bar'),
  427. ],
  428. adapter
  429. )
  430. }
  431. }
  432. void testScriptWithAdapterThatAddsDescriptorToMethodNodeProperties() {
  433. ScriptToTreeNodeAdapter adapter = createAdapter(true, false, false)
  434. TreeNode root = adapter.compile('''
  435. class Test {
  436. void test() {}
  437. }
  438. ''', Phases.SEMANTIC_ANALYSIS) as TreeNode
  439. def classNodeTest = root.children().find { it.toString() == 'ClassNode - Test' }
  440. def methods = classNodeTest.children().find { it.toString() == 'Methods' }
  441. def methodNodeTest = methods.children().find { it.toString() == 'MethodNode - test' }
  442. assert methodNodeTest.properties.any { name, value, type -> name == 'descriptor' && value == '()V' && type == 'String' }
  443. }
  444. void testScriptWithAdapterThatLoadsGeneratedClosureClass() {
  445. ScriptToTreeNodeAdapter adapter = createAdapter(false, true, true)
  446. assertTreeStructure(
  447. "def c = { println 'hello world' }", CompilePhase.CLASS_GENERATION,
  448. [
  449. contains('closure1'),
  450. eq('Methods'),
  451. eq('MethodNode - doCall'),
  452. ],
  453. adapter
  454. )
  455. }
  456. void testScriptWithAdapterThatLoadsMultipleGeneratedClosureClasses() {
  457. ScriptToTreeNodeAdapter adapter = createAdapter(false, true, true)
  458. def source = '''
  459. class Controller {
  460. def show = { println 'show' }
  461. def edit = { println 'edit' }
  462. def delete = { println 'delete' }
  463. }
  464. '''
  465. assertTreeStructure(source, CompilePhase.CLASS_GENERATION,
  466. [
  467. startsWith('ClassNode - Controller')
  468. ],
  469. adapter)
  470. assertTreeStructure(source, CompilePhase.CLASS_GENERATION,
  471. [
  472. contains('closure1'),
  473. eq('Methods'),
  474. eq('MethodNode - doCall'),
  475. ],
  476. adapter)
  477. assertTreeStructure(source, CompilePhase.CLASS_GENERATION,
  478. [
  479. contains('closure2'),
  480. eq('Methods'),
  481. eq('MethodNode - doCall'),
  482. ],
  483. adapter)
  484. assertTreeStructure(source, CompilePhase.CLASS_GENERATION,
  485. [
  486. contains('closure3'),
  487. eq('Methods'),
  488. eq('MethodNode - doCall'),
  489. ],
  490. adapter)
  491. }
  492. }