Skip to content

Commit 178ce1e

Browse files
committed
groovysh additional tests (cont'd)
1 parent 620aa31 commit 178ce1e

3 files changed

Lines changed: 256 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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 org.apache.groovy.groovysh.commands
20+
21+
import org.junit.jupiter.api.DynamicTest
22+
import org.junit.jupiter.api.TestFactory
23+
24+
import java.util.stream.Stream
25+
26+
/**
27+
* Cheap blanket coverage for every Groovy command's {@code --help} flag.
28+
* One dynamic test per registered command — adding a new command without
29+
* wiring its {@code CmdDesc} or {@code maybePrintHelp} call surfaces here
30+
* immediately rather than waiting for a user to type {@code /yourcmd --help}.
31+
*
32+
* <p>The set of names comes from {@link org.jline.console.CommandRegistry#commandNames}
33+
* at runtime, so the test self-adjusts to environment-conditional commands
34+
* (e.g. {@code /grab} only registers when Ivy is on the classpath).
35+
*/
36+
class HelpFlagTest extends SystemTestSupport {
37+
38+
@TestFactory
39+
Stream<DynamicTest> everyRegisteredCommandRespondsToHelpFlag() {
40+
def names = (groovy.commandNames() as List).toSorted()
41+
assert !names.empty, 'no Groovy commands registered'
42+
return names.stream().map { String name ->
43+
DynamicTest.dynamicTest("$name --help") {
44+
int printerBefore = printer.output.size()
45+
int terminalBefore = terminalOutput().length()
46+
try {
47+
system.execute("$name --help")
48+
} catch (Exception e) {
49+
throw new AssertionError("'$name --help' threw: $e.message", e)
50+
}
51+
int printerAfter = printer.output.size()
52+
int terminalAfter = terminalOutput().length()
53+
// Help can land on either sink: maybePrintHelp goes through
54+
// the printer; commands that delegate to JLine's parseOptions
55+
// (e.g. /doc) write to the terminal. Either is acceptable.
56+
assert printerAfter > printerBefore || terminalAfter > terminalBefore,
57+
"'$name --help' produced no output via printer or terminal"
58+
}
59+
}
60+
}
61+
62+
@TestFactory
63+
Stream<DynamicTest> everyRegisteredCommandRespondsToShortHelpFlag() {
64+
// Same enumeration, exercising the `-?` shorthand. maybePrintHelp
65+
// checks both forms; this confirms neither rots.
66+
def names = (groovy.commandNames() as List).toSorted()
67+
return names.stream().map { String name ->
68+
DynamicTest.dynamicTest("$name -?") {
69+
int printerBefore = printer.output.size()
70+
int terminalBefore = terminalOutput().length()
71+
try {
72+
system.execute("$name -?")
73+
} catch (Exception e) {
74+
throw new AssertionError("'$name -?' threw: $e.message", e)
75+
}
76+
assert printer.output.size() > printerBefore
77+
|| terminalOutput().length() > terminalBefore,
78+
"'$name -?' produced no output via printer or terminal"
79+
}
80+
}
81+
}
82+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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 org.apache.groovy.groovysh.commands
20+
21+
import org.junit.jupiter.api.Test
22+
23+
/**
24+
* Tests for the {@code /reset} command, which clears the engine's tracked
25+
* imports / types / methods / variable-snippets via {@link
26+
* org.apache.groovy.groovysh.jline.GroovyEngine#reset}.
27+
*/
28+
class ResetTest extends SystemTestSupport {
29+
30+
@Test
31+
void resetClearsTypesMethodsVariablesAndImports() {
32+
// Populate every tracked map so /reset has something to do. The
33+
// fixture engine starts empty — anything left after /reset is a leak.
34+
system.execute('class C {}')
35+
system.execute('def square(int x) { x * x }')
36+
system.execute('int n = 42')
37+
system.execute('import java.awt.Point')
38+
39+
assert engine.types.containsKey('C')
40+
assert engine.methodNames.contains('square')
41+
assert engine.variables.containsKey('n')
42+
assert !engine.imports.isEmpty()
43+
44+
system.execute('/reset')
45+
46+
assert engine.types.isEmpty()
47+
assert engine.methodNames.isEmpty()
48+
assert engine.variables.isEmpty()
49+
assert engine.imports.isEmpty()
50+
}
51+
52+
@Test
53+
void resetIsIdempotent() {
54+
// Two resets in a row on a virgin engine — no exception, state stays
55+
// empty. Guards against a future engine.reset() that assumes prior
56+
// state existed.
57+
system.execute('/reset')
58+
system.execute('/reset')
59+
assert engine.types.isEmpty()
60+
assert engine.methodNames.isEmpty()
61+
}
62+
63+
@Test
64+
void resetDoesNotEvictBindingVariables() {
65+
// /reset clears *tracked source* (the buffer) but should not touch
66+
// shared/binding variables. Users rely on this so that values
67+
// computed in the REPL survive a buffer wipe.
68+
engine.put('keepMe', 'still here')
69+
system.execute('class Tmp {}')
70+
system.execute('/reset')
71+
assert engine.types.isEmpty()
72+
assert engine.hasVariable('keepMe')
73+
assert engine.execute('keepMe') == 'still here'
74+
}
75+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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 org.apache.groovy.groovysh.commands
20+
21+
import org.junit.jupiter.api.Test
22+
23+
/**
24+
* Tests for the {@code /vars} command, which lists or removes tracked
25+
* <em>typed</em> variable declarations (the snippets matched by
26+
* {@code GroovyEngine.PATTERN_VAR_DEF}). Unscoped assignments like
27+
* {@code x = 5} create binding/shared variables and don't show up in
28+
* {@code /vars} — those are observable via {@code GroovyEngine.hasVariable},
29+
* not via this command.
30+
*/
31+
class VarsTest extends SystemTestSupport {
32+
33+
@Test
34+
void varsListsTrackedTypedDeclarations() {
35+
// Empty registry initially.
36+
system.execute('/vars')
37+
def initial = printer.output.join()
38+
assert !initial.contains('int n = 42')
39+
40+
system.execute('int n = 42')
41+
system.execute('def msg = "hi"')
42+
43+
assert engine.variables.keySet() == ['n', 'msg'] as Set
44+
45+
printer.output.clear()
46+
system.execute('/vars')
47+
def listing = printer.output.join()
48+
assert listing.contains('int n = 42')
49+
assert listing.contains('def msg = "hi"')
50+
}
51+
52+
@Test
53+
void varsByNameShowsOnlyThatSource() {
54+
system.execute('int n = 42')
55+
system.execute('def msg = "hi"')
56+
57+
printer.output.clear()
58+
system.execute('/vars n')
59+
def out = printer.output.join()
60+
assert out.contains('int n = 42')
61+
assert !out.contains('def msg = "hi"')
62+
}
63+
64+
@Test
65+
void varsDeleteRemovesTheTrackedDeclaration() {
66+
system.execute('int n = 42')
67+
system.execute('def msg = "hi"')
68+
assert engine.variables.containsKey('n')
69+
70+
system.execute('/vars -d n')
71+
72+
assert !engine.variables.containsKey('n')
73+
assert engine.variables.containsKey('msg')
74+
}
75+
76+
@Test
77+
void varsDeleteUnknownNameIsHarmless() {
78+
// /vars -d on an unknown variable should not throw or corrupt the
79+
// registry — mirrors the pattern other delete-by-name commands
80+
// promise (TypesTest, MethodsTest).
81+
system.execute('int n = 42')
82+
def before = engine.variables.keySet().toSet()
83+
system.execute('/vars -d noSuchVar')
84+
assert engine.variables.keySet().toSet() == before
85+
}
86+
87+
@Test
88+
void varsDeleteAllEmptiesTheRegistry() {
89+
// The wildcard '*' clears every tracked declaration in one go, per
90+
// maybeRemoveItem's name == '*' branch.
91+
system.execute('int n = 42')
92+
system.execute('def msg = "hi"')
93+
assert engine.variables.size() == 2
94+
95+
system.execute('/vars -d *')
96+
97+
assert engine.variables.isEmpty()
98+
}
99+
}

0 commit comments

Comments
 (0)