/* license-start
*
* Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, at <http://www.gnu.org/licenses/>.
*
* Contributors:
* Crispico - Initial API and implementation
*
* license-end
*/
package org.flowerplatform.web.tests.codesync;
import static org.junit.Assert.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.ecore.EObject;
import org.flowerplatform.codesync.code.javascript.CodeSyncCodeJavascriptPlugin;
import org.flowerplatform.codesync.code.javascript.parser.Parser;
import org.flowerplatform.codesync.code.javascript.regex_ast.RegExAstFactory;
import org.flowerplatform.codesync.code.javascript.regex_ast.RegExAstNode;
import org.flowerplatform.codesync.code.javascript.regex_ast.RegExAstNodeParameter;
import org.flowerplatform.communication.CommunicationPlugin;
import org.flowerplatform.communication.channel.CommunicationChannel;
import org.flowerplatform.communication.service.InvokeServiceMethodServerCommand;
import org.flowerplatform.communication.service.ServiceInvocationContext;
import org.flowerplatform.communication.stateful_service.StatefulServiceInvocationContext;
import org.flowerplatform.editor.EditorPlugin;
import org.flowerplatform.editor.model.remote.DiagramEditableResource;
import org.flowerplatform.editor.model.remote.DiagramEditorStatefulService;
import org.flowerplatform.emf_model.notation.ExpandableNode;
import org.flowerplatform.emf_model.notation.View;
import org.flowerplatform.web.communication.RecordingTestWebCommunicationChannel;
import org.flowerplatform.web.projects.remote.ProjectsService;
import org.flowerplatform.web.tests.TestUtil;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.crispico.flower.mp.codesync.base.CodeSyncPlugin;
import com.crispico.flower.mp.codesync.base.communication.CodeSyncEditorStatefulService;
import com.crispico.flower.mp.codesync.code.CodeSyncCodePlugin;
import com.crispico.flower.mp.model.codesync.CodeSyncElement;
/**
* @author Mariana Gheorghe
*/
public class CodeSyncJavascriptTest {
public static final String PROJECT = "codesync_javascript";
public static final String DIR = TestUtil.getResourcesDir(CodeSyncJavascriptTest.class);
public static final String INITIAL_TO_BE_COPIED = DIR + TestUtil.INITIAL_TO_BE_COPIED + "/javascript";
public static final String EXPECTED = DIR + TestUtil.EXPECTED + "/javascript/";
private static CommunicationChannel communicationChannel = new RecordingTestWebCommunicationChannel();
private static DiagramEditorStatefulService diagramEditorStatefulService;
private static CodeSyncEditorStatefulService codeSyncEditorStatefulService;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
TestUtil.copyFilesAndCreateProject(new ServiceInvocationContext(communicationChannel), INITIAL_TO_BE_COPIED, PROJECT);
diagramEditorStatefulService = (DiagramEditorStatefulService) CommunicationPlugin.getInstance().getServiceRegistry()
.getService("diagramEditorStatefulService");
assertNotNull("DiagramEditorStatefulService was not registered", diagramEditorStatefulService);
codeSyncEditorStatefulService = (CodeSyncEditorStatefulService) CommunicationPlugin.getInstance().getServiceRegistry()
.getService("codeSyncEditorStatefulService");
assertNotNull("CodeSyncEditorStatefulService was not registered", codeSyncEditorStatefulService);
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Before
public void setUp() throws Exception {
File to = new File(TestUtil.getWorkspaceResourceAbsolutePath("") + "/org/ws_trunk/" + PROJECT);
FileUtils.copyDirectory(new File(INITIAL_TO_BE_COPIED), to);
diagramEditorStatefulService.subscribeClientForcefully(communicationChannel, getDiagramEditableResourcePath());
}
@After
public void tearDown() throws Exception {
String codeSyncEditableResourcePath = EditorPlugin.getInstance().getFileAccessController().getPath(getProject());
codeSyncEditorStatefulService.cancelSelectedActions(codeSyncEditableResourcePath, false);
diagramEditorStatefulService.unsubscribeAllClientsForcefully(getDiagramEditableResourcePath(), false);
File to = new File(TestUtil.getWorkspaceResourceAbsolutePath("") + "/org/ws_trunk/" + PROJECT);
FileUtils.deleteDirectory(to);
}
public static final String TABLE = "Companies.html";
@Test
public void testTable() {
//////////////////////////////////////////////////
// Step 1: add to diagram
//////////////////////////////////////////////////
String fileName = TABLE;
List<RegExAstNodeParameter> tableParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(tableParameters, "name", fileName, 0, 0);
addParameter(tableParameters, "tableId", "companies-list", 79, 14);
addParameter(tableParameters, "headerRowId", "companies-list-header", 106, 21);
List<Object> table = getList(
"htmlFile",
"name",
false,
getParameters(tableParameters),
"Table",
null,
null,
null,
null);
invokeServiceMethod(table);
View tableView = findByName(fileName);
List<RegExAstNodeParameter> tableHeaderEntryParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(tableHeaderEntryParameters, "title", "Logo", 137, 4);
List<Object> tableHeaderEntry = getList(
"htmlTableHeaderEntry",
"title",
false,
getParameters(tableHeaderEntryParameters),
"TableHeaderEntry",
"TableHeaderEntry",
null,
tableView.eResource().getURIFragment(tableView),
null);
invokeServiceMethod(tableHeaderEntry);
//////////////////////////////////////////////////
// Step 2: sync -> generate Regex tree + file
//////////////////////////////////////////////////
sync(fileName);
//////////////////////////////////////////////////
// Step 3: compare generated code
//////////////////////////////////////////////////
testGeneratedFileContent(fileName);
Parser parser = new Parser();
RegExAstNode root = parser.parse(getFile(fileName));
Map<String, Integer> childrenInsertPoints = new HashMap<String, Integer>();
childrenInsertPoints.put("TableHeaderEntry", 146);
testParsedAstNode(table, 0, 211, 211, childrenInsertPoints, tableParameters, root);
assertEquals("Table children count", 1, root.getChildren().size());
childrenInsertPoints.clear();
testParsedAstNode(tableHeaderEntry, 133, 13, 146, childrenInsertPoints, tableHeaderEntryParameters, root.getChildren().get(0));
}
public static final String TABLE_ITEM = "CompaniesTableItem.html";
@Test
public void testTableItem() {
//////////////////////////////////////////////////
// Step 1: add to diagram
//////////////////////////////////////////////////
String fileName = TABLE_ITEM;
List<RegExAstNodeParameter> tableItemParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(tableItemParameters, "name", fileName, 0, 0);
addParameter(tableItemParameters, "itemUrl", "#company/<%= id %>", 130, 18);
List<Object> tableItem = getList(
"htmlFile",
"name",
false,
getParameters(tableItemParameters),
"TableItem",
null,
null,
null,
null);
invokeServiceMethod(tableItem);
View tableItemView = findByName(fileName);
List<RegExAstNodeParameter> tableItemEntryParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(tableItemEntryParameters, "valueExpression", "name", 37, 4);
List<Object> tableItemEntry = getList(
"htmlTableItemEntry",
"valueExpression",
false,
getParameters(tableItemEntryParameters),
"TableItemEntry",
"TableItemEntry",
null,
tableItemView.eResource().getURIFragment(tableItemView),
null);
invokeServiceMethod(tableItemEntry);
//////////////////////////////////////////////////
// Step 2: sync -> generate Regex tree + file
//////////////////////////////////////////////////
sync(fileName);
//////////////////////////////////////////////////
// Step 3: compare generated code
//////////////////////////////////////////////////
testGeneratedFileContent(fileName);
Parser parser = new Parser();
RegExAstNode root = parser.parse(getFile(fileName));
Map<String, Integer> childrenInsertPoints = new HashMap<String, Integer>();
childrenInsertPoints.put("TableItemEntry", 49);
testParsedAstNode(tableItem, 0, 166, 166, childrenInsertPoints, tableItemParameters, root);
assertEquals("Table item children count", 1, root.getChildren().size());
childrenInsertPoints.clear();
testParsedAstNode(tableItemEntry, 29, 20, 49, childrenInsertPoints, tableItemEntryParameters, root.getChildren().get(0));
}
public static final String FORM = "Company.html";
@Test
public void testForm() {
//////////////////////////////////////////////////
// Step 1: add to diagram
//////////////////////////////////////////////////
String fileName = FORM;
List<RegExAstNodeParameter> formParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(formParameters, "name", fileName, 0, 0);
List<Object> form = getList(
"htmlFile",
"name",
false,
getParameters(formParameters),
"Form",
null,
null,
null,
null);
invokeServiceMethod(form);
View formView = findByName(fileName);
List<RegExAstNodeParameter> formItemParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(formItemParameters, "title", "Revenue", 73, 7);
addParameter(formItemParameters, "valueExpression", "revenue", 148, 7);
addParameter(formItemParameters, "editId", "revenue", 223, 7);
List<Object> formItem = getList(
"htmlFormItem",
"title",
false,
getParameters(formItemParameters),
"FormItem",
"FormItem",
null,
formView.eResource().getURIFragment(formView),
null);
invokeServiceMethod(formItem);
//////////////////////////////////////////////////
// Step 2: sync -> generate Regex tree + file
//////////////////////////////////////////////////
sync(fileName);
//////////////////////////////////////////////////
// Step 3: compare generated code
//////////////////////////////////////////////////
testGeneratedFileContent(fileName);
Parser parser = new Parser();
RegExAstNode root = parser.parse(getFile(fileName));
Map<String, Integer> childrenInsertPoints = new HashMap<String, Integer>();
childrenInsertPoints.put("FormItem", 274);
testParsedAstNode(form, 0, 323, 323, childrenInsertPoints, formParameters, root);
assertEquals("Form item children count", 1, root.getChildren().size());
childrenInsertPoints.clear();
testParsedAstNode(formItem, 62, 212, 274, childrenInsertPoints, formItemParameters, root.getChildren().get(0));
}
public static String BACKBONE_CLASS = "Company.js";
@Test
public void testBackboneClass() {
//////////////////////////////////////////////////
// Step 1: add to diagram
//////////////////////////////////////////////////
String fileName = BACKBONE_CLASS;
List<RegExAstNodeParameter> backboneClassParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(backboneClassParameters, "name", fileName, 0, 0);
addParameter(backboneClassParameters, "superClass", "Backbone.View", 182, 13);
List<Object> backboneClass = getList(
"jsFile",
"name",
false,
getParameters(backboneClassParameters),
"BackboneClass",
null,
null,
null,
null);
invokeServiceMethod(backboneClass);
View backboneClassView = findByName(fileName);
String parentId = backboneClassView.eResource().getURIFragment(backboneClassView);
List<RegExAstNodeParameter> requireEntryParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(requireEntryParameters, "varName", "Backbone", 95, 8);
addParameter(requireEntryParameters, "dependencyPath", "backbone", 115, 8);
List<Object> requireEntry = getList(
"jsRequireEntry",
"varName",
false,
getParameters(requireEntryParameters),
"RequireEntry",
"RequireEntry",
null,
parentId,
"Require");
invokeServiceMethod(requireEntry);
List<RegExAstNodeParameter> operationParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(operationParameters, "name", "render", 217, 6);
addParameter(operationParameters, "parameters", "param", 236, 5);
List<Object> operation = getList(
"jsOperation",
"name",
false,
getParameters(operationParameters),
"Operation",
"ClassMember",
",\r\n",
parentId,
"Operation");
invokeServiceMethod(operation);
List<RegExAstNodeParameter> attributeParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(attributeParameters, "name", "name", 252, 4);
addParameter(attributeParameters, "defaultValue", "John", 259, 4);
List<Object> attribute = getList(
"jsAttribute",
"name",
false,
getParameters(attributeParameters),
"Attribute",
"ClassMember",
",\r\n",
parentId,
"Attribute");
invokeServiceMethod(attribute);
//////////////////////////////////////////////////
// Step 2: sync -> generate Regex tree + file
//////////////////////////////////////////////////
sync(fileName);
//////////////////////////////////////////////////
// Step 3: compare generated code
//////////////////////////////////////////////////
testGeneratedFileContent(fileName);
Parser parser = new Parser();
RegExAstNode root = parser.parse(getFile(fileName));
Map<String, Integer> childrenInsertPoints = new HashMap<String, Integer>();
childrenInsertPoints.put("RequireEntry", 126);
childrenInsertPoints.put("ClassMember", 263);
testParsedAstNode(backboneClass, 0, 319, 319 /* should be 320, does not contain the final semicolon */, childrenInsertPoints, backboneClassParameters, root);
assertEquals("Backbone Class categories count", 3, root.getChildren().size());
RegExAstNode requireCategory = getCategoryNode("Require", root);
assertEquals("Require category children count", 1, requireCategory.getChildren().size());
childrenInsertPoints.clear();
testParsedAstNode(requireEntry, 91, 35, 126, childrenInsertPoints, requireEntryParameters, requireCategory.getChildren().get(0));
RegExAstNode operationCategory = getCategoryNode("Operation", root);
assertEquals("Operation category children count", 1, operationCategory.getChildren().size());
testParsedAstNode(operation, 217, 32, 249, childrenInsertPoints, operationParameters, operationCategory.getChildren().get(0));
RegExAstNode attributeCategory = getCategoryNode("Attribute", root);
assertEquals("Attribute category children count", 1, attributeCategory.getChildren().size());
testParsedAstNode(attribute, 252, 11, 263, childrenInsertPoints, attributeParameters, attributeCategory.getChildren().get(0));
}
@Test
public void testBackboneClassEventsAttribute() {
//////////////////////////////////////////////////
// Step 1: add to diagram
//////////////////////////////////////////////////
testBackboneClass();
String fileName = BACKBONE_CLASS;
View backboneClassView = findByName(fileName);
String parentId = backboneClassView.eResource().getURIFragment(backboneClassView);
List<RegExAstNodeParameter> eventsAttributeParameters = new ArrayList<RegExAstNodeParameter>();
addParameter(eventsAttributeParameters, "name", "events", 0, 0);
List<Object> eventsAttribute = getList(
"jsEventsAttribute",
"name",
false,
getParameters(eventsAttributeParameters),
"EventsAttribute",
"ClassMember",
",\r\n",
parentId,
"Attribute");
invokeServiceMethod(eventsAttribute);
//////////////////////////////////////////////////
// Step 2: sync -> generate Regex tree + file
//////////////////////////////////////////////////
sync(fileName);
//////////////////////////////////////////////////
// Step 3: compare generated code
//////////////////////////////////////////////////
testGeneratedFileContent(fileName, BACKBONE_CLASS + "_withEvents");
}
private void sync(String fileName) {
File file = getFile(fileName);
CodeSyncCodePlugin.getInstance().getCodeSyncElement(getProject(), file, CodeSyncCodeJavascriptPlugin.TECHNOLOGY, communicationChannel, true);
String editableResourcePath = EditorPlugin.getInstance().getFileAccessController().getPath(getProject());
StatefulServiceInvocationContext context = new StatefulServiceInvocationContext(communicationChannel);
codeSyncEditorStatefulService.synchronize(context, editableResourcePath);
codeSyncEditorStatefulService.applySelectedActions(context, editableResourcePath, false);
}
protected void testGeneratedFileContent(String fileName) {
testGeneratedFileContent(fileName, fileName);
}
protected void testGeneratedFileContent(String fileName, String expectedFileName) {
String expected = getExpectedFileContent(expectedFileName);
String actual = getActualFileContent(fileName);
assertEquals("Generated code is not as expected", expected, actual);
}
protected void testParsedAstNode(List<Object> expected, int offset, int length, int nextSiblingInsertPoint, Map<String, Integer> childrenInsertPoints, List<RegExAstNodeParameter> expectedParameters, RegExAstNode actual) {
assertEquals("Node type", expected.get(1), actual.getType());
assertEquals("Key parameter", expected.get(2), actual.getKeyParameter());
// assertEquals("Is category node", expected.get(3), actual.isCategoryNode());
assertEquals("Parameters count", expectedParameters.size(), actual.getParameters().size());
for (RegExAstNodeParameter expectedParameter : expectedParameters) {
testParsedParameter(expectedParameter, getParameter(expectedParameter.getName(), actual));
}
assertEquals("Template", expected.get(5), actual.getType());
assertEquals("Offset", offset, actual.getOffset());
assertEquals("Length", length, actual.getLength());
assertEquals("Next sibling insert point", nextSiblingInsertPoint, actual.getNextSiblingInsertPoint());
testParsedChildrenInsertPoints(childrenInsertPoints, actual.getChildrenInsertPoints());
}
private RegExAstNodeParameter getParameter(String key, RegExAstNode actual) {
for (RegExAstNodeParameter parameter : actual.getParameters()) {
if (parameter.getName().equals(key)) {
return parameter;
}
}
fail("No parameter parsed for key " + key);
return null;
}
protected void testParsedParameter(RegExAstNodeParameter expected, RegExAstNodeParameter actual) {
assertEquals("Parameter name", expected.getName(), actual.getName());
assertEquals("Parameter value", expected.getValue(), actual.getValue());
assertEquals("Parameter offset", expected.getOffset(), actual.getOffset());
assertEquals("Parameter length", expected.getLength(), actual.getLength());
}
protected void testParsedChildrenInsertPoints(Map<String, Integer> expected, EMap<String, Integer> actual) {
assertEquals("Children insert points count", expected.size(), actual.size());
for (String key : expected.keySet()) {
assertEquals("Children insert point", expected.get(key), actual.get(key));
}
}
protected RegExAstNode getCategoryNode(String category, RegExAstNode parent) {
for (RegExAstNode child : parent.getChildren()) {
// if (child.isCategoryNode() && child.getType().equals(category)) {
// return child;
// }
}
fail("Category node not found for " + category);
return null;
}
///////////////////////////
// Utils
///////////////////////////
private File getProject() {
return CodeSyncTestSuite.getProject(PROJECT);
}
private File getFile(String fileName) {
return CodeSyncTestSuite.getFile("/" + PROJECT + "/js/" + fileName);
}
private String getExpectedFileContent(String fileName) {
return TestUtil.readFile(EXPECTED + fileName);
}
private String getActualFileContent(String fileName) {
return TestUtil.readFile(EXPECTED + fileName);
}
private String getDiagramEditableResourcePath() {
return "org/ws_trunk/" + PROJECT + "/NewDiagram1.notation";
}
private void invokeServiceMethod(List<Object> parameters) {
InvokeServiceMethodServerCommand cmd = new InvokeServiceMethodServerCommand();
// TODO fix tests
// cmd.setServiceId(JavascriptClassDiagramOperationsService.SERVICE_ID);
cmd.setMethodName("addElement");
cmd.setParameters(parameters);
cmd.setCommunicationChannel(communicationChannel);
diagramEditorStatefulService.attemptUpdateEditableResourceContent(new StatefulServiceInvocationContext(communicationChannel), getDiagramEditableResourcePath(), cmd);
}
private void addParameter(List<RegExAstNodeParameter> parameters, String name, String value, int offset, int length) {
RegExAstNodeParameter parameter = RegExAstFactory.eINSTANCE.createRegExAstNodeParameter();
parameter.setName(name);
parameter.setValue(value);
parameter.setOffset(offset);
parameter.setLength(length);
parameters.add(parameter);
}
private Map<String, String> getParameters(List<RegExAstNodeParameter> parameters) {
Map<String, String> result = new HashMap<String, String>();
for (RegExAstNodeParameter parameter : parameters) {
result.put(parameter.getName(), parameter.getValue());
}
return result;
}
/**
* We can't use {@link Arrays#asList(Object...)} because the context will later be added
* to the list and we'd get an {@link UnsupportedOperationException}.
*/
private List<Object> getList(Object... params) {
List<Object> result = new ArrayList<Object>();
for (Object param : params) {
result.add(param);
}
return result;
}
private View findByName(String name) {
DiagramEditableResource der = (DiagramEditableResource) diagramEditorStatefulService.getEditableResource(getDiagramEditableResourcePath());
assertNotNull("Diagram not found", der);
for (Iterator<EObject> it = der.getMainResource().getAllContents(); it.hasNext(); ) {
EObject child = it.next();
if (child instanceof View) {
View view = (View) child;
CodeSyncElement cse = (CodeSyncElement) view.getDiagrammableElement();
if (view instanceof ExpandableNode && cse != null && cse.getName().equals(name)) {
return view;
}
}
}
fail("No view for element with name " + name);
return null;
}
}