package org.rubypeople.rdt.testunit.wizards;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogPage;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.rubypeople.rdt.core.IMethod;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.ISourceFolder;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyConventions;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.search.IRubySearchConstants;
import org.rubypeople.rdt.core.search.IRubySearchScope;
import org.rubypeople.rdt.core.search.SearchEngine;
import org.rubypeople.rdt.internal.corext.codemanipulation.StubUtility;
import org.rubypeople.rdt.internal.corext.util.Messages;
import org.rubypeople.rdt.internal.testunit.util.LayoutUtil;
import org.rubypeople.rdt.internal.testunit.util.TestUnitStatus;
import org.rubypeople.rdt.internal.testunit.wizards.MethodStubsSelectionButtonGroup;
import org.rubypeople.rdt.internal.testunit.wizards.WizardMessages;
import org.rubypeople.rdt.internal.ui.dialogs.TypeSelectionDialog2;
import org.rubypeople.rdt.ui.wizards.NewTypeWizardPage;
public class RubyNewTestCaseWizardPage extends NewTypeWizardPage {
private final static String PAGE_NAME = "NewTestCaseCreationWizardPage"; //$NON-NLS-1$
/** Field ID of the class under test field. */
public final static String CLASS_UNDER_TEST = PAGE_NAME + ".classundertest"; //$NON-NLS-1$
private final static String TEST_SUFFIX = "Test"; //$NON-NLS-1$
private final static String PREFIX = "test"; //$NON-NLS-1$
private final static String STORE_SETUP = PAGE_NAME + ".USE_SETUP"; //$NON-NLS-1$
private final static String STORE_TEARDOWN = PAGE_NAME + ".USE_TEARDOWN"; //$NON-NLS-1$
private final static String STORE_CONSTRUCTOR = PAGE_NAME
+ ".USE_CONSTRUCTOR"; //$NON-NLS-1$
private final static int IDX_SETUP = 0;
private final static int IDX_TEARDOWN = 1;
private final static int IDX_CONSTRUCTOR = 2;
private String fClassUnderTestText;
private MethodStubsSelectionButtonGroup fMethodStubsButtons;
private Text fClassUnderTestControl;
private Button fClassUnderTestButton;
private IStatus fClassUnderTestStatus;
private IType fClassUnderTest;
private RubyNewTestCaseWizardPageTwo fPage2;
/**
* Constructor for RubyNewTestCaseWizardPage.
*
* @param pageName
*/
public RubyNewTestCaseWizardPage(RubyNewTestCaseWizardPageTwo page2) {
super(true, PAGE_NAME);
fPage2 = page2;
setTitle(WizardMessages.NewTestCaseWizardPage_title);
setDescription(WizardMessages.NewTestCaseWizardPage_description);
String[] buttonNames = new String[] {
/* IDX_SETUP */WizardMessages.NewTestCaseWizardPage_methodStub_setUp,
/* IDX_TEARDOWN */WizardMessages.NewTestCaseWizardPage_methodStub_tearDown,
/* IDX_CONSTRUCTOR */WizardMessages.NewTestCaseWizardPage_methodStub_constructor };
fMethodStubsButtons = new MethodStubsSelectionButtonGroup(SWT.CHECK,
buttonNames, 2);
fMethodStubsButtons
.setLabelText(WizardMessages.NewTestCaseWizardPage_method_Stub_label);
fClassUnderTestStatus = new TestUnitStatus();
fClassUnderTestText = ""; //$NON-NLS-1$
}
/**
* @see IDialogPage#createControl(Composite)
*/
public void createControl(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
int nColumns = 4;
GridLayout layout = new GridLayout();
layout.numColumns = nColumns;
container.setLayout(layout);
createContainerControls(container, nColumns);
// createPackageControls(container, nColumns);
createSeparator(container, nColumns);
createTypeNameControls(container, nColumns);
createSuperClassControls(container, nColumns);
createMethodStubSelectionControls(container, nColumns);
createSeparator(container, nColumns);
createClassUnderTestControls(container, nColumns);
setControl(container);
setSuperClass("Test::Unit::TestCase", true);
// set default and focus
String classUnderTest = getClassUnderTestText();
if (classUnderTest.length() > 0) {
setTypeName(classUnderTest + TEST_SUFFIX, true);
}
Dialog.applyDialogFont(container);
// TODO Uncomment when we have context help interface set up
// PlatformUI.getWorkbench().getHelpSystem().setHelp(container,
// ITestUnitHelpContextIds.NEW_TESTCASE_WIZARD_PAGE);
setFocus();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jdt.ui.wizards.NewTypeWizardPage#createTypeMembers(org.eclipse
* .jdt.core.IType,
* org.eclipse.jdt.ui.wizards.NewTypeWizardPage.ImportsManager,
* org.eclipse.core.runtime.IProgressMonitor)
*/
protected void createTypeMembers(IType type, IProgressMonitor monitor)
throws CoreException {
String lineDelimiter = getLineDelimiter();
if (fMethodStubsButtons.isSelected(IDX_CONSTRUCTOR)) {
createConstructor(type, lineDelimiter);
}
if (fMethodStubsButtons.isSelected(IDX_SETUP)) {
createSetUp(type, lineDelimiter);
}
if (fMethodStubsButtons.isSelected(IDX_TEARDOWN)) {
createTearDown(type, lineDelimiter);
}
if (fClassUnderTest != null) {
createTestMethodStubs(type);
}
}
@Override
protected List<String> addImports() {
List<String> imports = new ArrayList<String>();
imports.add("test/unit");
// TODO Add import for class under test?
return imports;
}
private void createConstructor(IType type, String lineDelimiter)
throws CoreException {
StringBuffer content = new StringBuffer();
content.append("def initialize").append(lineDelimiter);
content.append(" super").append(lineDelimiter);
content.append("end").append(lineDelimiter);
type.createMethod(content.toString(), null, true, null);
}
private void createSetUp(IType type, String lineDelimiter)
throws CoreException {
StringBuffer content = new StringBuffer();
content.append("def setup").append(lineDelimiter);
content.append(" super").append(lineDelimiter);
content.append("end").append(lineDelimiter);
type.createMethod(content.toString(), null, true, null);
}
private void createTearDown(IType type, String lineDelimiter)
throws CoreException {
StringBuffer content = new StringBuffer();
content.append("def teardown").append(lineDelimiter);
content.append(" super").append(lineDelimiter);
content.append("end").append(lineDelimiter);
type.createMethod(content.toString(), null, true, null);
}
private void createTestMethodStubs(IType type) throws CoreException {
IMethod[] methods = fPage2.getCheckedMethods();
if (methods.length == 0)
return;
IMethod[] allMethodsArray = fPage2.getAllMethods();
List allMethods = new ArrayList();
allMethods.addAll(Arrays.asList(allMethodsArray));
/*
* used when for example both sum and Sum methods are present. Then sum
* -> testSum Sum -> testSum1
*/
List names = new ArrayList();
for (int i = 0; i < methods.length; i++) {
IMethod method = methods[i];
String elementName = method.getElementName();
StringBuffer name = new StringBuffer(PREFIX).append("_").append(
elementName);
StringBuffer buffer = new StringBuffer();
String testName = name.toString();
if (names.contains(testName)) {
int suffix = 1;
while (names.contains(testName + Integer.toString(suffix)))
suffix++;
name.append(Integer.toString(suffix));
}
testName = name.toString();
names.add(testName);
// if (isAddComments()) {
// appendMethodComment(buffer, method);
// }
buffer.append(" def ");//$NON-NLS-1$
buffer.append(testName).append(getLineDelimiter());
appendTestMethodBody(buffer, testName, method, type.getRubyScript());
buffer.append(" end").append(getLineDelimiter());
type.createMethod(buffer.toString(), null, false, null);
}
}
private void appendTestMethodBody(StringBuffer buffer, String name,
IMethod method, IRubyScript targetCu) throws CoreException {
final String delimiter = getLineDelimiter();
String todoTask = ""; //$NON-NLS-1$
if (fPage2.isCreateTasks()) {
// String todoTaskTag = TestUnitStubUtility.getTodoTaskTag(targetCu
// .getRubyProject());
String todoTaskTag = "TODO";
if (todoTaskTag != null) {
todoTask = " # " + todoTaskTag; //$NON-NLS-1$
}
}
String message = WizardMessages.NewTestCaseWizardPageOne_not_yet_implemented_string;
buffer
.append(Messages.format(" flunk \"{0}\"", message)).append(todoTask).append(delimiter); //$NON-NLS-1$
}
private String getLineDelimiter() throws RubyModelException {
IType classToTest = getClassUnderTest();
if (classToTest == null && getSourceFolder() != null && getSourceFolder().exists())
{
return StubUtility.getLineDelimiterUsed(getSourceFolder());
}
if (classToTest != null && classToTest.exists()
&& classToTest.getRubyScript() != null)
return classToTest.getRubyScript().findRecommendedLineSeparator();
return "\n";
}
/**
* Creates the controls for the method stub selection buttons. Expects a
* <code>GridLayout</code> with at least 3 columns.
*
* @param composite
* the parent composite
* @param nColumns
* number of columns to span
*/
protected void createMethodStubSelectionControls(Composite composite,
int nColumns) {
LayoutUtil.setHorizontalSpan(fMethodStubsButtons
.getLabelControl(composite), nColumns);
LayoutUtil.createEmptySpace(composite, 1);
LayoutUtil.setHorizontalSpan(fMethodStubsButtons
.getSelectionButtonsGroup(composite), nColumns - 1);
}
/**
* Creates the controls for the 'class under test' field. Expects a
* <code>GridLayout</code> with at least 3 columns.
*
* @param composite
* the parent composite
* @param nColumns
* number of columns to span
*/
protected void createClassUnderTestControls(Composite composite,
int nColumns) {
Label classUnderTestLabel = new Label(composite, SWT.LEFT | SWT.WRAP);
classUnderTestLabel.setFont(composite.getFont());
classUnderTestLabel
.setText(WizardMessages.NewTestCaseWizardPage_class_to_test_label);
classUnderTestLabel.setLayoutData(new GridData());
fClassUnderTestControl = new Text(composite, SWT.SINGLE | SWT.BORDER);
fClassUnderTestControl.setEnabled(true);
fClassUnderTestControl.setFont(composite.getFont());
fClassUnderTestControl.setText(fClassUnderTestText);
fClassUnderTestControl.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
internalSetClassUnderText(((Text) e.widget).getText());
}
});
GridData gd = new GridData();
gd.horizontalAlignment = GridData.FILL;
gd.grabExcessHorizontalSpace = true;
gd.horizontalSpan = nColumns - 2;
fClassUnderTestControl.setLayoutData(gd);
fClassUnderTestButton = new Button(composite, SWT.PUSH);
fClassUnderTestButton
.setText(WizardMessages.NewTestCaseWizardPage_class_to_test_browse);
fClassUnderTestButton.setEnabled(true);
fClassUnderTestButton.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) {
classToTestButtonPressed();
}
public void widgetSelected(SelectionEvent e) {
classToTestButtonPressed();
}
});
gd = new GridData();
gd.horizontalAlignment = GridData.FILL;
gd.grabExcessHorizontalSpace = false;
gd.horizontalSpan = 1;
gd.widthHint = LayoutUtil.getButtonWidthHint(fClassUnderTestButton);
fClassUnderTestButton.setLayoutData(gd);
// ControlContentAssistHelper.createTextContentAssistant(
// fClassUnderTestControl, fClassToTestCompletionProcessor);
}
private IType chooseClassToTestType() {
ISourceFolder root = getSourceFolder();
if (root == null) {
return null;
}
IRubyElement[] elements = new IRubyElement[] { root.getRubyProject() };
IRubySearchScope scope = SearchEngine.createRubySearchScope(elements);
TypeSelectionDialog2 dialog = new TypeSelectionDialog2(getShell(),
false, getWizard().getContainer(), scope,
IRubySearchConstants.CLASS);
dialog
.setTitle(WizardMessages.NewTestCaseWizardPage_class_to_test_dialog_title);
dialog
.setMessage(WizardMessages.NewTestCaseWizardPage_class_to_test_dialog_message);
if (dialog.open() == Window.OK) {
return (IType) dialog.getFirstResult();
}
return null;
}
private void classToTestButtonPressed() {
IType type = chooseClassToTestType();
if (type != null) {
setClassUnderTest(type.getElementName());
}
}
/**
* Sets the name of the class under test.
*
* @param name
* The name to set
*/
public void setClassUnderTest(String name) {
if (fClassUnderTestControl != null
&& !fClassUnderTestControl.isDisposed()) {
fClassUnderTestControl.setText(name);
}
internalSetClassUnderText(name);
}
private void internalSetClassUnderText(String name) {
fClassUnderTestText = name;
fClassUnderTestStatus = classUnderTestChanged();
handleFieldChanged(CLASS_UNDER_TEST);
}
/**
* Hook method that gets called when the class under test has changed. The
* method class under test returns the status of the validation.
* <p>
* Subclasses may extend this method to perform their own validation.
* </p>
*
* @return the status of the validation
*/
protected IStatus classUnderTestChanged() {
TestUnitStatus status = new TestUnitStatus();
fClassUnderTest = null;
ISourceFolder root = getSourceFolder();
if (root == null) {
return status;
}
String classToTestName = getClassUnderTestText();
if (classToTestName.length() == 0) {
return status;
}
String classUnderTest = getClassUnderTestText();
if (classUnderTest.length() > 0) {
setTypeName(classUnderTest + TEST_SUFFIX, true);
}
IStatus val = RubyConventions.validateRubyTypeName(classToTestName);
if (val.getSeverity() == IStatus.ERROR) {
status
.setError(WizardMessages.NewTestCaseWizardPage_error_class_to_test_not_valid);
return status;
}
try {
IType type = resolveClassNameToType(root.getRubyProject(),
classToTestName);
if (type == null) {
status
.setError(WizardMessages.NewTestCaseWizardPage_error_class_to_test_not_exist);
return status;
}
if (type.isModule()) {
status
.setWarning(Messages
.format(
WizardMessages.NewTestCaseWizardPage_warning_class_to_test_is_interface,
classToTestName));
}
fClassUnderTest = type;
fPage2.setClassUnderTest(fClassUnderTest);
} catch (RubyModelException e) {
status
.setError(WizardMessages.NewTestCaseWizardPage_error_class_to_test_not_valid);
}
return status;
}
/**
* Returns the class to be tested.
*
* @return the class under test or <code>null</code> if the entered values
* are not valid
*/
public IType getClassUnderTest() {
return fClassUnderTest;
}
private IType resolveClassNameToType(IRubyProject rproject,
String classToTestName) throws RubyModelException {
if (!rproject.exists()) {
return null;
}
IType type = rproject.findType(classToTestName);
return type;
}
/**
* Returns the content of the class to test text field.
*
* @return the name of the class to test
*/
public String getClassUnderTestText() {
return fClassUnderTestText;
}
/**
* The wizard owning this page is responsible for calling this method with
* the current selection. The selection is used to initialize the fields of
* the wizard page.
*
* @param selection
* used to initialize the fields
*/
public void init(IStructuredSelection selection) {
IRubyElement element = getInitialRubyElement(selection);
initContainerPage(element);
initTypePage(element);
// put default class to test
if (element != null) {
IType classToTest = null;
// evaluate the enclosing type
IType typeInCompUnit = (IType) element
.getAncestor(IRubyElement.TYPE);
if (typeInCompUnit != null) {
if (typeInCompUnit.getRubyScript() != null) {
classToTest = typeInCompUnit;
}
} else {
IRubyScript cu = (IRubyScript) element
.getAncestor(IRubyElement.SCRIPT);
if (cu != null)
classToTest = cu.findPrimaryType();
}
if (classToTest != null) {
setClassUnderTest(classToTest.getFullyQualifiedName());
}
}
restoreWidgetValues();
updateStatus(getStatusList());
}
/**
* Returns all status to be consider for the validation. Clients can
* override.
*
* @return The list of status to consider for the validation.
*/
protected IStatus[] getStatusList() {
return new IStatus[] { fContainerStatus, fTypeNameStatus,
fClassUnderTestStatus, fSuperClassStatus };
}
/**
* Use the dialog store to restore widget values to the values that they
* held last time this wizard was used to completion
*/
private void restoreWidgetValues() {
IDialogSettings settings = getDialogSettings();
if (settings != null) {
fMethodStubsButtons.setSelection(IDX_SETUP, settings
.getBoolean(STORE_SETUP));
fMethodStubsButtons.setSelection(IDX_TEARDOWN, settings
.getBoolean(STORE_TEARDOWN));
fMethodStubsButtons.setSelection(IDX_CONSTRUCTOR, settings
.getBoolean(STORE_CONSTRUCTOR));
} else {
fMethodStubsButtons.setSelection(IDX_SETUP, false); // setUp
fMethodStubsButtons.setSelection(IDX_TEARDOWN, false); // tearDown
fMethodStubsButtons.setSelection(IDX_CONSTRUCTOR, false); // constructor
}
}
}