package bluej.pkgmgr.target.role;
import bluej.Config;
import bluej.collect.DataCollector;
import bluej.compiler.CompileReason;
import bluej.compiler.CompileType;
import bluej.debugger.DebuggerObject;
import bluej.debugmgr.objectbench.ObjectWrapper;
import bluej.editor.TextEditor;
import bluej.parser.SourceLocation;
import bluej.parser.SourceSpan;
import bluej.parser.UnitTestAnalyzer;
import bluej.pkgmgr.PackageEditor;
import bluej.pkgmgr.PkgMgrFrame;
import bluej.pkgmgr.Project;
import bluej.pkgmgr.TestRunnerThread;
import bluej.pkgmgr.target.ClassTarget;
import bluej.pkgmgr.target.DependentTarget.State;
import bluej.pkgmgr.target.Target;
import bluej.terminal.Terminal;
import bluej.testmgr.TestDisplayFrame;
import bluej.testmgr.record.ExistingFixtureInvokerRecord;
import bluej.utility.Debug;
import bluej.utility.DialogManager;
import bluej.utility.JavaNames;
import bluej.utility.JavaUtils;
import bluej.utility.javafx.FXPlatformSupplier;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.dialog.InputDialog;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import org.junit.Test;
import threadchecker.OnThread;
import threadchecker.Tag;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static bluej.pkgmgr.target.ClassTarget.MENU_STYLE_INBUILT;
| A role object for Junit unit tests.
|
| @author Andrew Patterson
|
public class UnitTestClassRole
extends ClassRole{
public static final String UNITTEST_ROLE_NAME = "UnitTestTarget";
public static final String UNITTEST_ROLE_NAME_JUNIT4 = "UnitTestTargetJunit4";
private static final String testAll = Config.getString("pkgmgr.test.popup.testAll");
private static final String createTest = Config.getString("pkgmgr.test.popup.createTest");
private static final String benchToFixture = Config.getString("pkgmgr.test.popup.benchToFixture");
private static final String fixtureToBench = Config.getString("pkgmgr.test.popup.fixtureToBench");
| Whether this is a Junit 4 test class. If false, it's a Junit 3 test class.
|
private final boolean isJunit4;
| Create the unit test class role.
|
public UnitTestClassRole(boolean isJunit4)
{
this.isJunit4 = isJunit4;
}
@Override
@OnThread(Tag.Any)
public String getRoleName()
{
if (isJunit4) {
return UNITTEST_ROLE_NAME_JUNIT4;
}
else {
return UNITTEST_ROLE_NAME;
}
}
@Override
@OnThread(Tag.Any)
public String getStereotypeLabel()
{
return "unit test";
}
@SuppressWarnings("unchecked")
@OnThread(Tag.Any)
private boolean isJUnitTestMethod(Method m)
{
if (isJunit4) {
Class<?> cl = m.getDeclaringClass();
ClassLoader classLoader = cl.getClassLoader();
try {
Class<Test> testClass;
if (classLoader == null) {
testClass = org.junit.Test.class;
}
else {
testClass = (Class<Test>) classLoader.loadClass("org.junit.Test");
}
if (m.getAnnotation(testClass) != null) {
if (!Modifier.isPublic(m.getModifiers())) return false;
if (m.getParameterTypes().length != 0) return false;
return true;
}
}
catch (ClassNotFoundException cnfe) {
}
catch (LinkageError le) {
}
return false;
}
else {
if (!m.getName().startsWith("test")) return false;
if (!Modifier.isPublic(m.getModifiers())) return false;
if (m.getParameterTypes().length != 0) return false;
if (!m.getReturnType().equals(Void.TYPE)) return false;
return true;
}
}
| Generate a popup menu for this TestClassRole.
| @param cl the class object that is represented by this target
| @param editorFrame the frame in which this targets package is displayed
| @return the generated JPopupMenu
|
@Override
@OnThread(Tag.FXPlatform)
public boolean createRoleMenu(ObservableList<MenuItem> menu, ClassTarget ct, Class<?> cl, State state)
{
boolean enableTestAll = false;
if (state == State.COMPILED && cl != null && ! ct.isAbstract()) {
Method[] allMethods = cl.getMethods();
for (int i=0; i < allMethods.length; i++) {
Method m = allMethods[i];
if (isJUnitTestMethod(m)) {
enableTestAll = true;
break;
}
}
}
addMenuItem(menu, new TestAction(testAll, ct.getPackage().getEditor(),ct), enableTestAll);
menu.add(new SeparatorMenuItem());
return false;
}
@OnThread(Tag.FXPlatform)
private static void addMenuItem(ObservableList<MenuItem> menu, TargetAbstractAction testAction, boolean enableTestAll)
{
menu.add(testAction);
testAction.setDisable(!enableTestAll);
JavaFXUtil.addStyleClass(testAction, MENU_STYLE_INBUILT);
}
| Creates a class menu containing any constructors and static methods etc.
|
| @param menu the popup menu to add the class menu items to
| @param cl Class object associated with this class target
|
@Override
@OnThread(Tag.FXPlatform)
public boolean createClassConstructorMenu(ObservableList<MenuItem> menu, ClassTarget ct, Class<?> cl)
{
boolean hasEntries = false;
Method[] allMethods = cl.getMethods();
if (! ct.isAbstract()) {
int itemHeight = 28;
int itemsOnScreen = (int)Config.screenBounds.getHeight() / itemHeight;
int sizeLimit = itemsOnScreen / 2;
for (int i=0; i < allMethods.length; i++) {
Method m = allMethods[i];
if (!isJUnitTestMethod(m)) {
continue;
}
String rtype;
try {
rtype = JavaUtils.getJavaUtils().getReturnType(m).toString(true);
}
catch (ClassNotFoundException cnfe) {
rtype = m.getReturnType().getName();
}
TargetAbstractAction testAction = new TestAction(rtype + " " + m.getName() + "()",
ct.getPackage().getEditor(), ct, m.getName());
int itemCount = menu.size();
if (itemCount >= sizeLimit) {
Menu subMenu = new Menu(Config.getString("pkgmgr.classmenu.moreMethods"));
menu.add(subMenu);
menu = subMenu.getItems();
}
menu.add(testAction);
hasEntries = true;
}
if (!hasEntries) {
MenuItem item = new MenuItem(Config.getString("pkgmgr.test.popup.noTests"));
item.setDisable(true);
menu.add(item);
}
}
else {
MenuItem item = new MenuItem(Config.getString("pkgmgr.test.popup.abstract"));
item.setDisable(true);
menu.add(item);
}
return true;
}
@Override
@OnThread(Tag.FXPlatform)
public boolean createClassStaticMenu(ObservableList<MenuItem> menu, ClassTarget ct, Class<?> cl)
{
boolean enable = !ct.getPackage().getProject().inTestMode() && ct.hasSourceCode() && ! ct.isAbstract();
addMenuItem(menu, new MakeTestCaseAction(createTest,
ct.getPackage().getEditor(), ct), enable);
addMenuItem(menu, new BenchToFixtureAction(benchToFixture,
ct.getPackage().getEditor(), ct), enable);
addMenuItem(menu, new FixtureToBenchAction(fixtureToBench,
ct.getPackage().getEditor(), ct), enable);
return true;
}
@Override
@OnThread(Tag.Any)
public boolean canConvertToStride()
{
return false;
}
@Override
public void run(final PkgMgrFrame pmf, final ClassTarget ct, final String param)
{
Terminal terminal = pmf.getPackage().getProject().getTerminal();
if (terminal.clearOnMethodCall())
{
terminal.clear();
}
if (param != null) {
Project proj = pmf.getProject();
TestDisplayFrame.getTestDisplay().startTest(proj, 1);
}
new TestRunnerThread(pmf, ct, param).start();
}
| Set up a test run. This just involves going through the methods in the class
| and creating a list of those which are test methods.
|
| @param pmf The package manager frame
| @param ct The class target
| @param trt The test runner thread
| @return The list of test methods in the class, or null if we could not find out
|
public List startRunTest(PkgMgrFrame pmf, ClassTarget ct, TestRunnerThread trt)
{
Class<?> cl = pmf.getPackage().loadClass(ct.getQualifiedName());
if (cl == null)
return null;
List<String> testMethods = Arrays.stream(cl.getMethods())
.filter(this::isJUnitTestMethod)
.map(Method::getName)
.sorted()
.collect(Collectors.toList());
Project proj = pmf.getProject();
TestDisplayFrame.getTestDisplay().startTest(proj, testMethods.size());
return testMethods;
}
| Get the count of tests in the test class.
| @param ct The ClassTarget of the unit test class
| @return the number of tests in the unit test class
|
public int getTestCount(ClassTarget ct)
{
if (! ct.isCompiled()) {
return 0;
}
Class<?> cl = ct.getPackage().loadClass(ct.getQualifiedName());
if (cl == null) {
return 0;
}
Method[] allMethods = cl.getMethods();
int testCount = 0;
for (int i=0; i < allMethods.length; i++) {
if (isJUnitTestMethod(allMethods[i])) {
testCount++;
}
}
return testCount;
}
| Start the construction of a test method.
|
| This method prompts the user for a test method name and then sets up
| all the variables for constructing a new test method.
|
| @param pmf the PkgMgrFrame this is all occurring in
| @param ct the ClassTarget of the unit test class
|
@OnThread(Tag.FXPlatform)
public void doMakeTestCase(final PkgMgrFrame pmf, final ClassTarget ct)
{
String newTestName = new TestNameDialog("unittest-new-test-method", "").showAndWait().orElse(null);
if (newTestName == null)
return;
try {
Charset charset = pmf.getProject().getProjectCharset();
UnitTestAnalyzer uta = analyzeUnitTest(ct, charset);
SourceSpan existingSpan = uta.getMethodBlockSpan(newTestName);
if (existingSpan != null)
{
if (DialogManager.askQuestionFX(pmf.getFXWindow(), "unittest-method-present") == 1)
{
}
else
{
finishTestCase(pmf, ct, newTestName);
}
}
else
{
finishTestCase(pmf, ct, newTestName);
}
}
catch (IOException ioe) {
DialogManager.showErrorWithTextFX(pmf.getFXWindow(), "unittest-io-error", ioe.getLocalizedMessage());
Debug.reportError("Error reading unit test source", ioe);
finishTestCase(pmf, ct, newTestName);
}
}
@OnThread(Tag.FXPlatform)
private void finishTestCase(PkgMgrFrame pmf, ClassTarget ct, String newTestName)
{
pmf.testRecordingStarted(Config.getString("pkgmgr.test.recording") + " "
+ ct.getBaseName() + "." + newTestName + "()");
pmf.getProject().removeClassLoader();
runTestSetup(pmf, ct, false);
pmf.getObjectBench().resetRecordingInteractions();
pmf.setTestInfo(newTestName, ct);
}
| Analyze a unit test file.
| @param ct The classtarget representing the unit test class to analyze
| @return A UnitTestAnalyzer object with information about the unit test class
| @throws IOException if the source file can't be saved or read
|
private UnitTestAnalyzer analyzeUnitTest(ClassTarget ct, Charset fileEncoding) throws IOException
{
ct.ensureSaved();
UnitTestAnalyzer uta = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(ct.getSourceFile());
Reader reader = new InputStreamReader(fis, fileEncoding);
uta = new UnitTestAnalyzer(reader);
}
catch (FileNotFoundException fnfe) {
throw fnfe;
}
finally {
if (fis != null) {
try {
fis.close();
}
catch (IOException ioe) {
Debug.reportError(ioe);
}
}
}
return uta;
}
| Run the test setup.
| @param pmf The package manager frame to run the setup in
| @param ct The classtarget for the test class
|
private void runTestSetup(final PkgMgrFrame pmf, final ClassTarget ct, final boolean recordAsFixtureToBench)
{
Project project = pmf.getProject();
new Thread() {
@OnThread(value = Tag.Worker, ignoreParent = true)
public void run()
{
final FXPlatformSupplier<Map<String, DebuggerObject>> dobs = project.getDebugger().runTestSetUp(ct.getQualifiedName());
Platform.runLater(() -> {
List<DataCollector.NamedTyped> recordObjects = new ArrayList<DataCollector.NamedTyped>();
Iterator<Map.Entry<String,DebuggerObject>> it = dobs.get().entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,DebuggerObject> mapent = it.next();
DebuggerObject objVal = mapent.getValue();
if (! objVal.isNullObject()) {
String actualName = pmf.putObjectOnBench(mapent.getKey(), objVal, objVal.getGenType(), null, Optional.empty());
recordObjects.add(new DataCollector.NamedTyped(actualName, objVal.getClassName()));
}
}
if (recordAsFixtureToBench)
{
DataCollector.fixtureToObjectBench(pmf.getPackage(), ct.getSourceFile(), recordObjects);
}
});
}
}.start();
}
private static final String spaces = " ";
| Get a string of whitespace corresponding to an indentation.
|
private String getIndentString()
{
int ts = Math.min(Config.getPropInteger("bluej.editor.tabsize", 4), spaces.length());
return spaces.substring(0, ts);
}
| End the construction of a test method.
|
| This method is responsible for actually created the source code for a
| just-recorded test method.
|
| @param pmf the PkgMgrFrame this is all occurring in
| @param ct the ClassTarget of the unit test class
| @param name the name of the test method we are writing out
|
public void doEndMakeTestCase(PkgMgrFrame pmf, ClassTarget ct, String name)
{
TextEditor ed = ct.getEditor().assumeText();
String ts = getIndentString();
try {
Charset charset = pmf.getProject().getProjectCharset();
UnitTestAnalyzer uta = analyzeUnitTest(ct, charset);
SourceSpan existingSpan = uta.getMethodBlockSpan(name);
if (existingSpan != null) {
ed.setSelection(existingSpan.getStartLine(), existingSpan.getStartColumn(),
existingSpan.getEndLine(), existingSpan.getEndColumn());
ed.insertText("{\n" + pmf.getObjectBench().getTestMethod(ts + ts) + ts + "}", false);
}
else {
SourceLocation methodInsert = uta.getNewMethodInsertLocation();
if (methodInsert != null) {
ed.setSelection(methodInsert.getLine(), methodInsert.getColumn(), 1);
if (isJunit4) {
ed.insertText("\n" + ts + "@Test\n" + ts + "public void " + name + "()\n" + ts + "{\n"
+ pmf.getObjectBench().getTestMethod(ts + ts) + ts + "}\n}\n", false);
}
else {
ed.insertText("\n" + ts + "public void " + name + "()\n" + ts + "{\n"
+ pmf.getObjectBench().getTestMethod(ts + ts) + ts + "}\n}\n", false);
}
}
}
ed.save();
}
catch (IOException ioe) {
PkgMgrFrame.showMessageWithText(pmf.getPackage(), "generic-file-save-error", ioe.getLocalizedMessage());
}
}
| Turn the fixture declared in a unit test class into a set of
| objects on the object bench.
|
| @param pmf the PkgMgrFrame that will hold the object bench
| @param ct the ClassTarget of the unit test class
|
public void doFixtureToBench(PkgMgrFrame pmf, ClassTarget ct)
{
TextEditor ed = ct.getEditor().assumeText();
ExistingFixtureInvokerRecord existing = new ExistingFixtureInvokerRecord();
try {
Charset charset = pmf.getProject().getProjectCharset();
UnitTestAnalyzer uta = analyzeUnitTest(ct, charset);
List<SourceSpan> fixtureSpans = uta.getFieldSpans();
ListIterator<SourceSpan> it = fixtureSpans.listIterator();
while (it.hasNext()) {
SourceSpan variableSpan = it.next();
String fieldDecl = ed.getText(variableSpan.getStartLocation(), variableSpan.getEndLocation());
existing.addFieldDeclaration(fieldDecl);
}
SourceSpan setUpSpan = uta.getMethodBlockSpan("setUp");
if (setUpSpan != null) {
String setUpWithBrackets = ed.getText(setUpSpan.getStartLocation(), setUpSpan.getEndLocation());
String setUpWithoutBrackets =
setUpWithBrackets.substring(setUpWithBrackets.indexOf('{') + 1,
setUpWithBrackets.lastIndexOf('}')).trim();
existing.setSetupMethod(setUpWithoutBrackets);
}
}
catch (IOException ioe) {
PkgMgrFrame.showMessageWithText(pmf.getPackage(), "generic-file-save-error", ioe.getLocalizedMessage());
}
runTestSetup(pmf, ct, true);
pmf.getObjectBench().addInteraction(existing);
}
| Convert the objects on the object bench into a test fixture.
|
public void doBenchToFixture(PkgMgrFrame pmf, ClassTarget ct)
{
if (pmf.getObjectBench().getObjectCount() == 0) {
return;
}
TextEditor ed = ct.getEditor().assumeText();
try {
Charset charset = pmf.getProject().getProjectCharset();
UnitTestAnalyzer uta = analyzeUnitTest(ct, charset);
List<SourceSpan> variables = uta.getFieldSpans();
if (variables != null && variables.size() > 0) {
boolean shouldContinue = DialogManager.askQuestionFX(null, "unittest-fixture-present") != 1;
if (!shouldContinue)
return;
}
if (variables != null) {
ListIterator<SourceSpan> it = variables.listIterator(variables.size());
while (it.hasPrevious()) {
SourceSpan variableSpan = (SourceSpan) it.previous();
ed.setSelection(variableSpan.getStartLine(), variableSpan.getStartColumn(),
variableSpan.getEndLine(), variableSpan.getEndColumn());
ed.insertText("", false);
}
uta = analyzeUnitTest(ct, charset);
}
SourceLocation fixtureInsertLocation = uta.getFixtureInsertLocation();
if (fixtureInsertLocation == null) {
return;
}
{
List<String> names = new ArrayList<String>();
for (ObjectWrapper obj : pmf.getObjectBench().getObjects())
{
names.add(obj.getName());
}
DataCollector.objectBenchToFixture(pmf.getPackage(), ct.getSourceFile(), names);
}
SourceSpan setupSpan = uta.getMethodBlockSpan("setUp");
String ts = getIndentString();
if (setupSpan != null) {
ed.setSelection(setupSpan.getStartLine(), setupSpan.getStartColumn(),
setupSpan.getEndLine(), setupSpan.getEndColumn());
}
else {
ed.setSelection(fixtureInsertLocation.getLine(),
fixtureInsertLocation.getColumn(), 1);
if (isJunit4) {
ed.insertText("{\n" + ts + "@Before\n" + ts + "public void setUp()\n" + ts, false);
}
else {
ed.insertText("{\n" + ts + "public void setUp()\n" + ts, false);
}
}
ed.insertText("{\n" + pmf.getObjectBench().getFixtureSetup(ts + ts)
+ ts + "}", false);
ed.setSelection(fixtureInsertLocation.getLine(),
fixtureInsertLocation.getColumn(), 1);
ed.insertText("{\n" + pmf.getObjectBench().getFixtureDeclaration(ts), false);
ed.save();
}
catch (IOException ioe) {
PkgMgrFrame.showMessageWithText(pmf.getPackage(),
"generic-file-save-error", ioe.getLocalizedMessage());
}
pmf.getPackage().compileQuiet(ct, CompileReason.MODIFIED, CompileType.INTERNAL_COMPILE);
pmf.getProject().removeClassLoader();
pmf.getProject().newRemoteClassLoaderLeavingBreakpoints();
}
| A base class for all our actions that run on targets.
|
@OnThread(Tag.FXPlatform)
private abstract class TargetAbstractAction
extends MenuItem
{
protected ClassTarget t;
protected PackageEditor ped;
public TargetAbstractAction(String name, PackageEditor ped, ClassTarget t)
{
super(name);
this.ped = ped;
this.t = t;
setOnAction(e -> actionPerformed(e));
}
@OnThread(Tag.FXPlatform)
public abstract void actionPerformed(javafx.event.ActionEvent actionEvent);
}
| A TestAction is an action that causes a JUnit test to be run on a class.
| If testName is not provided, it is set to null which means that the whole
| test class is run; otherwise it refers to a test method that should be run
| individually.
|
@OnThread(Tag.FXPlatform)
private class TestAction
extends TargetAbstractAction
{
private String testName;
public TestAction(String actionName, PackageEditor ped, ClassTarget t)
{
super(actionName, ped, t);
this.testName = null;
}
public TestAction(String actionName, PackageEditor ped, ClassTarget t, String testName)
{
super(actionName, ped, t);
this.testName = testName;
}
@Override
@OnThread(Tag.FXPlatform)
public void actionPerformed(ActionEvent e)
{
ped.runTest(t, testName);
}
}
@OnThread(Tag.FXPlatform)
private class MakeTestCaseAction
extends TargetAbstractAction
{
public MakeTestCaseAction(String name, PackageEditor ped, ClassTarget t)
{
super(name, ped, t);
}
@Override
@OnThread(Tag.FXPlatform)
public void actionPerformed(ActionEvent e)
{
ped.makeTestCase(t);
}
}
@OnThread(Tag.FXPlatform)
private class BenchToFixtureAction
extends TargetAbstractAction
{
public BenchToFixtureAction(String name, PackageEditor ped, ClassTarget t)
{
super(name, ped, t);
}
@Override
@OnThread(Tag.FXPlatform)
public void actionPerformed(ActionEvent e)
{
ped.benchToFixture(t);
}
}
@OnThread(Tag.FXPlatform)
private class FixtureToBenchAction
extends TargetAbstractAction
{
public FixtureToBenchAction(String name, PackageEditor ped, ClassTarget t)
{
super(name, ped, t);
}
@Override
@OnThread(Tag.FXPlatform)
public void actionPerformed(ActionEvent e)
{
ped.fixtureToBench(t);
}
}
@OnThread(Tag.FXPlatform)
private class TestNameDialog extends InputDialog<String>
{
public TestNameDialog(String dialogLabel, String prompt)
{
super(dialogLabel, prompt, "test-name-dialog");
}
@Override
protected String convert(String newTestName)
{
if (!isJunit4 && !newTestName.startsWith("test"))
{
return "test" + Character.toTitleCase(newTestName.charAt(0)) + newTestName.substring(1);
}
else{ return newTestName;
}
}
@Override
protected boolean validate(String oldInput, String newTestName)
{
if (newTestName.length() == 0) {
setErrorText(Config.getString("pkgmgr.test.noTestName"));
setOKEnabled(false);
}
else if (!JavaNames.isIdentifier(convert(newTestName)))
{
setErrorText(Config.getString("pkgmgr.test.invalidTestName"));
setOKEnabled(false);
}
else
{
setErrorText("");
setOKEnabled(true);
}
return true;
}
}
}
top,
use,
map,
class UnitTestClassRole
. UnitTestClassRole
. getRoleName
. getStereotypeLabel
. isJUnitTestMethod
. createRoleMenu
. addMenuItem
. createClassConstructorMenu
. createClassStaticMenu
. canConvertToStride
. run
. startRunTest
. getTestCount
. doMakeTestCase
. finishTestCase
. analyzeUnitTest
. runTestSetup
. run
. getIndentString
. doEndMakeTestCase
. doFixtureToBench
. doBenchToFixture
top,
use,
map,
abstract class TargetAbstractAction
. TargetAbstractAction
. actionPerformed
top,
use,
map,
class TestAction
. TestAction
. TestAction
. actionPerformed
top,
use,
map,
class MakeTestCaseAction
. MakeTestCaseAction
. actionPerformed
top,
use,
map,
class BenchToFixtureAction
. BenchToFixtureAction
. actionPerformed
top,
use,
map,
class FixtureToBenchAction
. FixtureToBenchAction
. actionPerformed
. TestNameDialog
. convert
. validate
972 neLoCode
+ 50 LoComm