diff --git a/diylc/diylc-core/src/org/diylc/common/ComponentType.java b/diylc/diylc-core/src/org/diylc/common/ComponentType.java index 87b5bda9..59355ab4 100644 --- a/diylc/diylc-core/src/org/diylc/common/ComponentType.java +++ b/diylc/diylc-core/src/org/diylc/common/ComponentType.java @@ -17,6 +17,8 @@ */ package org.diylc.common; +import java.io.Serializable; + import javax.swing.Icon; import org.diylc.core.CreationMethod; @@ -31,8 +33,11 @@ import org.diylc.core.annotations.KeywordPolicy; * * @see IDIYComponent */ -public class ComponentType { +public class ComponentType implements Serializable { + + private static final long serialVersionUID = 1L; + private String name; private String description; private CreationMethod creationMethod; @@ -49,6 +54,9 @@ public class ComponentType { private KeywordPolicy keywordPolicy; private String keywordTag; private boolean enableCache; + + public ComponentType() { + } public ComponentType(String name, String description, CreationMethod creationMethod, String category, String namePrefix, String author, Icon icon, Class> instanceClass, double zOrder, diff --git a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java index 2ad75703..d6506744 100644 --- a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java +++ b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java @@ -40,6 +40,7 @@ import org.diylc.core.IDIYComponent; import org.diylc.core.Project; import org.diylc.core.Template; import org.diylc.core.Theme; +import org.diylc.test.ITestProcessor; /** * Interface for communication between plug-ins and the application. Plug-ins may acquire @@ -54,7 +55,7 @@ import org.diylc.core.Theme; * @see EventType */ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyProcessor, IVariantProcessor, - IBlockProcessor, INetlistProcessor { + IBlockProcessor, INetlistProcessor, ITestProcessor { public static final String ANTI_ALIASING_KEY = "antiAliasing"; public static final String HI_QUALITY_RENDER_KEY = "hiQualityRender"; diff --git a/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java b/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java index 275b8ffe..4dcd0afd 100644 --- a/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java +++ b/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java @@ -195,11 +195,12 @@ public abstract class AbstractComponent implements IDIYComponent { Field[] fields = clazz.getDeclaredFields(); clazz = clazz.getSuperclass(); // fields = this.getClass().getDeclaredFields(); - // Copy over all non-static, non-final fields that are declared + // Copy over all non-static, non-final and non-transient fields that are declared // in // AbstractComponent or one of it's child classes for (Field field : fields) { - if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) { + if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers()) && + !Modifier.isTransient(field.getModifiers())) { field.setAccessible(true); try { Object value = field.get(this); diff --git a/diylc/diylc-core/src/org/diylc/core/IView.java b/diylc/diylc-core/src/org/diylc/core/IView.java index ebb7700a..8cff063e 100644 --- a/diylc/diylc-core/src/org/diylc/core/IView.java +++ b/diylc/diylc-core/src/org/diylc/core/IView.java @@ -56,7 +56,9 @@ public interface IView { int showConfirmDialog(String message, String title, int optionType, int messageType); - boolean editProperties(List properties, Set defaultedProperties); + boolean editProperties(List properties, Set defaultedProperties); + + String showInputDialog(String message, String title); File promptFileSave(); } diff --git a/diylc/diylc-core/src/org/diylc/core/Project.java b/diylc/diylc-core/src/org/diylc/core/Project.java index c25a5bba..0bb50ab0 100644 --- a/diylc/diylc-core/src/org/diylc/core/Project.java +++ b/diylc/diylc-core/src/org/diylc/core/Project.java @@ -277,7 +277,7 @@ public class Project implements Serializable, Cloneable { if (font == null) { if (other.font != null) return false; - } else if (!font.equals(other.font)) + } else if (!font.getFamily().equals(other.font.getFamily()) || font.getSize() != other.font.getSize() || font.getStyle() != other.font.getStyle()) return false; if (groups == null) { if (other.groups != null) diff --git a/diylc/diylc-core/src/org/diylc/presenter/AreaConverter.java b/diylc/diylc-core/src/org/diylc/presenter/AreaConverter.java new file mode 100644 index 00000000..ce59cfa9 --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/presenter/AreaConverter.java @@ -0,0 +1,135 @@ +/* + + DIY Layout Creator (DIYLC). + Copyright (c) 2009-2018 held jointly by the individual authors. + + This file is part of DIYLC. + + DIYLC 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, either version 3 of the License, or + (at your option) any later version. + + DIYLC 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. + + You should have received a copy of the GNU General Public License + along with DIYLC. If not, see . + +*/ +package org.diylc.presenter; + +import java.awt.Point; +import java.awt.geom.Area; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.util.ArrayList; +import java.util.List; +import java.awt.geom.Path2D.Double; + +import org.diylc.utils.Constants; + +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +/** + * Serializes {@link Point} objects by converting coordinates from pixels to inches, thus avoiding + * the resolution to affect point placement. + * + * @author Branislav Stojkovic + */ +public class AreaConverter implements Converter { + + @Override + public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { + Area area = (Area) object; + PathIterator iterator = area.getPathIterator(null); +// writer.startNode("area"); + writer.addAttribute("wind", String.valueOf(iterator.getWindingRule())); + while (!iterator.isDone()) { + writer.startNode("segment"); + double[] coord = new double[6]; + int type = iterator.currentSegment(coord); + writer.addAttribute("type", String.valueOf(type)); + int n = 0; + switch (type) { + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + n = 2; + break; + case PathIterator.SEG_CUBICTO: + n = 6; + break; + case PathIterator.SEG_QUADTO: + n = 4; + break; + } + for (int i = 0; i < n; i++) { + writer.startNode("coordinate"); + writer.setValue(String.valueOf(coord[i])); + writer.endNode(); + } + writer.endNode(); + iterator.next(); + } +// writer.endNode(); + } + + @Override + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + Area area = new Area(); +// reader.moveDown(); + int wind = Integer.parseInt(reader.getAttribute("wind")); + Path2D path = null; + while (reader.hasMoreChildren()) { + reader.moveDown(); + int type = Integer.parseInt(reader.getAttribute("type")); + List values = new ArrayList(); + while (reader.hasMoreChildren()) { + reader.moveDown(); + values.add(java.lang.Double.parseDouble(reader.getValue())); + reader.moveUp(); + } + java.lang.Double[] coord = values.toArray(new java.lang.Double[0]); + switch (type) { + case PathIterator.SEG_MOVETO: + if (path != null) { + Area partArea = new Area(path); + area.add(partArea); + } + path = new Path2D.Double(wind); + path.moveTo(coord[0], coord[1]); + break; + case PathIterator.SEG_LINETO: + path.lineTo(coord[0], coord[1]); + break; + case PathIterator.SEG_CUBICTO: + path.curveTo(coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]); + break; + case PathIterator.SEG_QUADTO: + path.quadTo(coord[0], coord[1], coord[2], coord[3]); + break; + } + reader.moveUp(); + } +// reader.moveUp(); + + // add residual area + if (path != null) { + Area partArea = new Area(path); + area.add(partArea); + } + return area; + } + + @SuppressWarnings("rawtypes") + @Override + public boolean canConvert(Class clazz) { + return Area.class.isAssignableFrom(clazz); + } +} diff --git a/diylc/diylc-core/src/org/diylc/presenter/ComponentArea.java b/diylc/diylc-core/src/org/diylc/presenter/ComponentArea.java index 3194f62b..c6140807 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/ComponentArea.java +++ b/diylc/diylc-core/src/org/diylc/presenter/ComponentArea.java @@ -22,13 +22,19 @@ package org.diylc.presenter; import java.awt.geom.Area; +import java.io.Serializable; import java.util.Collection; -public class ComponentArea { +public class ComponentArea implements Serializable { + private static final long serialVersionUID = 1L; + private Area outlineArea; private Collection continuityPositiveAreas; private Collection continuityNegativeAreas; + + public ComponentArea() { + } public ComponentArea(Area outlineArea, Collection continuityPositiveAreas, Collection continuityNegativeAreas) { super(); diff --git a/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java b/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java index e892f3f0..ce9a1e13 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java +++ b/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java @@ -53,6 +53,7 @@ import java.awt.image.renderable.RenderableImage; import java.io.PrintWriter; import java.io.StringWriter; import java.text.AttributedCharacterIterator; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -159,7 +160,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { canvasGraphics.setComposite(originalComposite); canvasGraphics.setFont(originalFont); // Map sorted = sortByComparator(trackingStacks, false); - return new ComponentArea(currentArea, continuityPositiveAreas.values(), continuityNegativeAreas.values()); + return new ComponentArea(currentArea, new ArrayList(continuityPositiveAreas.values()), new ArrayList(continuityNegativeAreas.values())); } // start - used for caching diff --git a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java index 93e631bb..625b75c4 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java @@ -87,6 +87,8 @@ import org.diylc.netlist.NetlistAnalyzer; import org.diylc.netlist.Node; import org.diylc.netlist.Position; import org.diylc.netlist.SwitchSetup; +import org.diylc.test.DIYTest; +import org.diylc.test.Snapshot; import org.diylc.utils.Constants; import com.thoughtworks.xstream.XStream; @@ -175,6 +177,8 @@ public class Presenter implements IPlugInPort { private Project preDragProject = null; private int dragAction; private Point previousScaledPoint; + + private DIYTest test = null; public Presenter(IView view) { super(); @@ -547,6 +551,19 @@ public class Presenter implements IPlugInPort { @Override public void mouseClicked(Point point, int button, boolean ctrlDown, boolean shiftDown, boolean altDown, int clickCount) { LOG.debug(String.format("mouseClicked(%s, %s, %s, %s, %s)", point, button, ctrlDown, shiftDown, altDown)); + + // record a test step if needed + if (test != null) { + Map params = new HashMap(); + params.put("point", point); + params.put("button", button); + params.put("ctrlDown", ctrlDown); + params.put("shiftDown", shiftDown); + params.put("altDown", altDown); + params.put("clickCount", clickCount); + test.addStep(DIYTest.MOUSE_CLICKED, params); + } + Point scaledPoint = scalePoint(point); if (clickCount >= 2) { editSelection(); @@ -827,6 +844,16 @@ public class Presenter implements IPlugInPort { public void mouseMoved(Point point, boolean ctrlDown, boolean shiftDown, boolean altDown) { if (point == null) return; + + // record a test step if needed + if (test != null) { + Map params = new HashMap(); + params.put("point", point); + params.put("ctrlDown", ctrlDown); + params.put("shiftDown", shiftDown); + params.put("altDown", altDown); + test.addStep(DIYTest.MOUSE_MOVED, params); + } if (shiftDown) { dragAction = IPlugInPort.DND_TOGGLE_SNAP; @@ -1035,6 +1062,16 @@ public class Presenter implements IPlugInPort { @Override public void dragStarted(Point point, int dragAction, boolean forceSelectionRect) { LOG.debug(String.format("dragStarted(%s, %s)", point, dragAction)); + + // record a test step if needed + if (test != null) { + Map params = new HashMap(); + params.put("point", point); + params.put("dragAction", dragAction); + params.put("forceSelectionRect", forceSelectionRect); + test.addStep(DIYTest.DRAG_START, params); + } + if (instantiationManager.getComponentTypeSlot() != null) { LOG.debug("Cannot start drag because a new component is being created."); mouseClicked(point, IPlugInPort.BUTTON1, dragAction == DnDConstants.ACTION_COPY, @@ -1051,7 +1088,7 @@ public class Presenter implements IPlugInPort { Point scaledPoint = scalePoint(point); this.previousDragPoint = scaledPoint; List> components = forceSelectionRect ? null : findComponentsAtScaled(scaledPoint); - if (!this.controlPointMap.isEmpty()) { + if (controlPointMap != null && !this.controlPointMap.isEmpty()) { // If we're dragging control points reset selection. updateSelection(new ArrayList>(this.controlPointMap.keySet())); // messageDispatcher.dispatchMessage(EventType.SELECTION_CHANGED, @@ -1193,11 +1230,18 @@ public class Presenter implements IPlugInPort { @Override public boolean dragOver(Point point) { + // record a test step if needed + if (test != null) { + Map params = new HashMap(); + params.put("point", point); + test.addStep(DIYTest.DRAG_OVER, params); + } + if (point == null || ConfigurationManager.getInstance().readBoolean(HIGHLIGHT_CONTINUITY_AREA, false)) { return false; } Point scaledPoint = scalePoint(point); - if (!controlPointMap.isEmpty()) { + if (controlPointMap != null && !controlPointMap.isEmpty()) { // We're dragging control point(s). int dx = (scaledPoint.x - previousDragPoint.x); int dy = (scaledPoint.y - previousDragPoint.y); @@ -1522,6 +1566,14 @@ public class Presenter implements IPlugInPort { @Override public void dragEnded(Point point) { LOG.debug(String.format("dragEnded(%s)", point)); + + // record a test step if needed + if (test != null) { + Map params = new HashMap(); + params.put("point", point); + params.put("dragAction", dragAction); + test.addStep(DIYTest.DRAG_END, params); + } Point scaledPoint = scalePoint(point); @@ -2320,7 +2372,17 @@ public class Presenter implements IPlugInPort { @Override public void setNewComponentTypeSlot(ComponentType componentType, Template template, boolean forceInstatiate) { - LOG.info(String.format("setNewComponentSlot(%s)", componentType == null ? null : componentType.getName())); + LOG.info(String.format("setNewComponentSlot(%s)", componentType == null ? null : componentType.getName())); + + // record a test step if needed + if (test != null) { + Map params = new HashMap(); + params.put("componentType", componentType); + params.put("template", template); + params.put("forceInstatiate", forceInstatiate); + test.addStep(DIYTest.SET_COMPONENT_SLOT, params); + } + if (componentType != null && componentType.getInstanceClass() == null) { LOG.info("Cannot set new component type slot for type " + componentType.getName()); setNewComponentTypeSlot(null, null, false); @@ -3214,4 +3276,34 @@ public class Presenter implements IPlugInPort { return null; } } + + // Test stuff + + @Override + public void startRecording(String name) { + test = new DIYTest(name); + } + + @Override + public DIYTest stopRecording() { + DIYTest res = test; + test = null; + return res; + } + + @Override + public void addValidation(Map params) { + if (test != null) + test.addStep(DIYTest.VALIDATION, params); + } + + @Override + public Snapshot createTestSnapshot() { + Map, ComponentArea> areas = new HashMap, ComponentArea>(); + for (IDIYComponent c : currentProject.getComponents()) { + ComponentArea componentArea = drawingManager.getComponentArea(c); + areas.put(c, componentArea); + } + return new Snapshot(currentProject.clone(), new HashSet>(selectedComponents), areas, new HashSet(drawingManager.getContinuityAreas(currentProject))); + } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/ProjectFileManager.java b/diylc/diylc-core/src/org/diylc/presenter/ProjectFileManager.java index 9dbf3e57..f3d457ed 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/ProjectFileManager.java +++ b/diylc/diylc-core/src/org/diylc/presenter/ProjectFileManager.java @@ -48,6 +48,7 @@ import org.diylc.appframework.update.VersionNumber; import org.diylc.common.EventType; import org.diylc.core.Project; import org.diylc.parsing.IOldFileParser; +import org.diylc.test.DIYTest; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -104,6 +105,7 @@ public class ProjectFileManager { xStream.registerConverter(new ColorConverter()); xStream.registerConverter(new FontConverter()); xStream.registerConverter(new MeasureConverter()); + xStream.registerConverter(new AreaConverter()); xStream.addImmutableType(Color.class); xStream.addImmutableType(java.awt.Point.class); xStream.addImmutableType(org.diylc.core.measures.Voltage.class); @@ -192,6 +194,27 @@ public class ProjectFileManager { this.currentFileName = fileName; this.modified = false; return project; + } + + public void serializeTestToFile(DIYTest test, String fileName) throws IOException { + LOG.info(String.format("serializeTestToFile(%s)", fileName)); + FileOutputStream fos; + fos = new FileOutputStream(fileName); + Writer writer = new OutputStreamWriter(fos, "UTF-8"); + writer.write("\n"); + xStream.toXML(test, writer); + fos.close(); + } + + public DIYTest deserializeTestFromFile(String fileName) + throws SAXException, IOException, ParserConfigurationException { + LOG.info(String.format("deserializeTestFromFile(%s)", fileName)); + DIYTest project = null; + FileInputStream fis = new FileInputStream(fileName); + Reader reader = new InputStreamReader(fis, "UTF-8"); + project = (DIYTest) xStream.fromXML(reader); + fis.close(); + return project; } public void notifyFileChange() { diff --git a/diylc/diylc-core/src/org/diylc/test/DIYTest.java b/diylc/diylc-core/src/org/diylc/test/DIYTest.java new file mode 100644 index 00000000..36c00f72 --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/test/DIYTest.java @@ -0,0 +1,78 @@ +package org.diylc.test; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class DIYTest implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String MOUSE_MOVED = "mouseMoved"; + public static final String MOUSE_CLICKED = "mouseClicked"; + public static final String SET_COMPONENT_SLOT = "setComponentSlot"; + public static final String VALIDATION = "validation"; + public static final String DRAG_START = "dragStart"; + public static final String DRAG_OVER = "dragOver"; + public static final String DRAG_END = "dragEnd"; + + // validations + public static final String SNAPSHOT = "snapshot"; + public static final String COMPLETE = "complete"; + public static final String PROJECT = "project"; + public static final String SELECTION = "selection"; + + private String name; + private List steps; + + public DIYTest() { + } + + public DIYTest(String name) { + this.name = name; + steps = new ArrayList(); + } + + public void addStep(String action, Map params) { + steps.add(new TestStep(action, params)); + } + + public String getName() { + return name; + } + + public List getSteps() { + return steps; + } + + public class TestStep implements Serializable { + + private static final long serialVersionUID = 1L; + + private String action; + private Map params; + + public TestStep() { + } + + public TestStep(String action, Map params) { + super(); + this.action = action; + this.params = params; + } + + public String getAction() { + return action; + } + + public Map getParams() { + return params; + } + + @Override + public String toString() { + return action; + } + } +} diff --git a/diylc/diylc-core/src/org/diylc/test/ITestProcessor.java b/diylc/diylc-core/src/org/diylc/test/ITestProcessor.java new file mode 100644 index 00000000..612c474e --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/test/ITestProcessor.java @@ -0,0 +1,35 @@ +/* + + DIY Layout Creator (DIYLC). + Copyright (c) 2009-2018 held jointly by the individual authors. + + This file is part of DIYLC. + + DIYLC 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, either version 3 of the License, or + (at your option) any later version. + + DIYLC 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. + + You should have received a copy of the GNU General Public License + along with DIYLC. If not, see . + +*/ +package org.diylc.test; + +import java.util.Map; + +public interface ITestProcessor { + + void startRecording(String name); + + DIYTest stopRecording(); + + void addValidation(Map params); + + Snapshot createTestSnapshot(); +} diff --git a/diylc/diylc-core/src/org/diylc/test/Snapshot.java b/diylc/diylc-core/src/org/diylc/test/Snapshot.java new file mode 100644 index 00000000..83e00bc6 --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/test/Snapshot.java @@ -0,0 +1,76 @@ +package org.diylc.test; + +import java.awt.geom.Area; +import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.diylc.core.IDIYComponent; +import org.diylc.core.Project; +import org.diylc.presenter.ComponentArea; + +public class Snapshot implements Serializable { + + private static final long serialVersionUID = 1L; + + private Project project; + private Set> selection; + private Map, ComponentArea> areas; + private Set continuityAreas; + + public Snapshot() { + } + + public Snapshot(Project project, Set> selection, + Map, ComponentArea> areas, Set continuityAreas) { + super(); + this.project = project; + this.selection = selection; + this.areas = areas; + this.continuityAreas = continuityAreas; + } + + public Project getProject() { + return project; + } + + public Set> getSelection() { + return selection; + } + + public Map, ComponentArea> getAreas() { + return areas; + } + + public Set getContinuityAreas() { + return continuityAreas; + } + + public boolean equalsTo(Snapshot other) { + if (!project.equals(other.getProject())) + return false; + if (selection == null) { + if (other.selection != null) + return false; + } else if (selection.size() != other.selection.size()) { + return false; + } else { +// IDIYComponent c1= selection.iterator().next(); +// IDIYComponent c2=other.selection.iterator().next(); +// if (!c1.equalsTo(c2)) +// return false; +// for(IDIYComponent c : selection) +// if (!other.selection.contains(c)) +// return false; +// for(IDIYComponent c : other.selection) +// if (!selection.contains(c)) +// return false; + } +// if (!areas.equals(other.getAreas())) +// return false; +// if (!continuityAreas.equals(other.getContinuityAreas())) +// return false; + return true; + } +} diff --git a/diylc/diylc-core/src/org/diylc/test/TestPlayer.java b/diylc/diylc-core/src/org/diylc/test/TestPlayer.java new file mode 100644 index 00000000..8b391c97 --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/test/TestPlayer.java @@ -0,0 +1,112 @@ +package org.diylc.test; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.diylc.common.ComponentType; +import org.diylc.common.IPlugInPort; +import org.diylc.core.Template; +import org.diylc.test.DIYTest.TestStep; + +public class TestPlayer { + + private static final Logger LOG = Logger.getLogger(TestPlayer.class); + + public static final int DELAY_MS = 5; + + public List play(IPlugInPort plugInPort, DIYTest test) { + LOG.info("Playing test: " + test.getName()); + List steps = test.getSteps(); + List res = new ArrayList(); + for (int i = 0; i < steps.size(); i++) { + TestStep step = steps.get(i); + try { + switch(step.getAction()) { + case DIYTest.SET_COMPONENT_SLOT: + plugInPort.setNewComponentTypeSlot((ComponentType)step.getParams().get("componentType"), (Template)step.getParams().get("template"), (boolean)step.getParams().get("forceInstatiate")); + res.add(new StepResult(i, step.getAction(), "OK", false)); + break; + case DIYTest.MOUSE_MOVED: + plugInPort.mouseMoved((Point)step.getParams().get("point"), (boolean)step.getParams().get("ctrlDown"), (boolean)step.getParams().get("shiftDown"), (boolean)step.getParams().get("altDown")); + res.add(new StepResult(i, step.getAction(), "OK", false)); + break; + case DIYTest.MOUSE_CLICKED: + plugInPort.mouseClicked((Point)step.getParams().get("point"), (int)step.getParams().get("button"), (boolean)step.getParams().get("ctrlDown"), + (boolean)step.getParams().get("shiftDown"), (boolean)step.getParams().get("altDown"), (int)step.getParams().get("clickCount")); + res.add(new StepResult(i, step.getAction(), "OK", false)); + break; + case DIYTest.DRAG_START: + plugInPort.dragStarted((Point)step.getParams().get("point"), (int)step.getParams().get("dragAction"), (boolean)step.getParams().get("forceSelectionRect")); + res.add(new StepResult(i, step.getAction(), "OK", false)); + break; + case DIYTest.DRAG_OVER: + plugInPort.dragOver((Point)step.getParams().get("point")); + res.add(new StepResult(i, step.getAction(), "OK", false)); + break; + case DIYTest.DRAG_END: + plugInPort.dragEnded((Point)step.getParams().get("point")); + res.add(new StepResult(i, step.getAction(), "OK", false)); + break; + case DIYTest.VALIDATION: + res.add(runValidation(i, plugInPort, step.getParams())); + break; + default: + res.add(new StepResult(i, step.getAction(), "Invalid action: " + step.getAction(), true)); + } + } catch (Exception e) { + res.add(new StepResult(i, step.getAction(), "Step failed with error: " + e.getMessage(), true)); + } + + try { + Thread.sleep(DELAY_MS); + } catch (InterruptedException e) { + } + } + LOG.info("Finished test: " + test.getName()); + return res; + } + + private StepResult runValidation(int step, IPlugInPort plugInPort, Map params) { + if (params.containsKey(DIYTest.SNAPSHOT)) { + Snapshot snap = (Snapshot) params.get(DIYTest.SNAPSHOT); + if (!snap.equalsTo(plugInPort.createTestSnapshot())) { + return new StepResult(step, DIYTest.VALIDATION, "Snapshots do not match", true); + } + } + return new StepResult(step, DIYTest.VALIDATION, "OK", false); + } + + public class StepResult { + private int step; + private String action; + private String status; + private boolean hasError; + + public StepResult(int step, String action, String status, boolean hasError) { + super(); + this.step = step; + this.action = action; + this.status = status; + this.hasError = hasError; + } + + public int getStep() { + return step; + } + + public String getAction() { + return action; + } + + public String getStatus() { + return status; + } + + public boolean hasError() { + return hasError; + } + } +} diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar index 4f8ea87d..cf105cd4 100644 Binary files a/diylc/diylc-library/lib/diylc-core.jar and b/diylc/diylc-library/lib/diylc-core.jar differ diff --git a/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java b/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java index d0a4810e..e21888a8 100644 --- a/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java +++ b/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java @@ -85,8 +85,8 @@ public abstract class AbstractLeadedComponent extends AbstractTransparentComp protected boolean moveLabel = false; // parameters for adjusting the label control point - protected Double gamma = null; - protected Double r = null; + protected transient Double gamma = null; + protected transient Double r = null; protected AbstractLeadedComponent() { super(); @@ -576,11 +576,11 @@ public abstract class AbstractLeadedComponent extends AbstractTransparentComp double beta = Math.atan2(getPoints()[2].y - y, getPoints()[2].x - x); gamma = beta + (Math.PI / 2 - theta); r = getPoints()[2].distance(x, y); - } else { // in case when we are copy pasting we don't want to recalculate 3rd point position as they will all move in unison - // when we moved the first point, gamma and r were initialized, so now we are canceling - gamma = null; - r = null; } + } else { // in case when we are copy pasting we don't want to recalculate 3rd point position as they will all move in unison + // when we moved the first point, gamma and r were initialized, so now we are canceling + gamma = null; + r = null; } getPoints()[index].setLocation(point); diff --git a/diylc/diylc-swing/DIYLCStarter.launch b/diylc/diylc-swing/DIYLCStarter.launch index 71b0b502..d631a4c5 100644 --- a/diylc/diylc-swing/DIYLCStarter.launch +++ b/diylc/diylc-swing/DIYLCStarter.launch @@ -12,5 +12,5 @@ - + diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar index 4f8ea87d..cf105cd4 100644 Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar index d4d42b81..c0761a4f 100644 Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ diff --git a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java index 080d5ed7..a9f5c3df 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java +++ b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java @@ -399,6 +399,11 @@ public class ActionFactory { public void showMessage(String message, String title, int messageType) { JOptionPane.showMessageDialog(null, message, title, messageType); } + + @Override + public String showInputDialog(String message, String title) { + return JOptionPane.showInputDialog(null, message, title, JOptionPane.QUESTION_MESSAGE); + } @Override public File promptFileSave() { diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java b/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java index bea862bc..845d8af2 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java +++ b/diylc/diylc-swing/src/org/diylc/swing/gui/DummyView.java @@ -46,5 +46,10 @@ public class DummyView implements IView { @Override public boolean editProperties(List properties, Set defaultedProperties) { return false; + } + + @Override + public String showInputDialog(String message, String title) { + return null; } } diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java b/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java index 05f5cdbd..59d21c24 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java +++ b/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java @@ -82,6 +82,7 @@ import org.diylc.swing.plugins.file.FileMenuPlugin; import org.diylc.swing.plugins.help.HelpMenuPlugin; import org.diylc.swing.plugins.layers.LayersMenuPlugin; import org.diylc.swing.plugins.statusbar.StatusBar; +import org.diylc.swing.plugins.test.TestMenuPlugin; import org.diylc.swing.plugins.toolbox.ToolBox; import org.diylc.swing.plugins.tree.ComponentTree; import org.diylc.swingframework.ButtonDialog; @@ -126,7 +127,9 @@ public class MainFrame extends JFrame implements ISwingUI { presenter.installPlugin(new EditMenuPlugin(this)); presenter.installPlugin(new ConfigPlugin(this)); presenter.installPlugin(new LayersMenuPlugin(this)); - presenter.installPlugin(new CloudPlugIn(this)); + presenter.installPlugin(new CloudPlugIn(this)); + if ("true".equalsIgnoreCase(System.getProperty("org.diylc.enableTests"))) + presenter.installPlugin(new TestMenuPlugin(this)); presenter.installPlugin(new HelpMenuPlugin(this)); presenter.installPlugin(new ActionBarPlugin(this)); @@ -282,6 +285,11 @@ public class MainFrame extends JFrame implements ISwingUI { @Override public int showConfirmDialog(String message, String title, int optionType, int messageType) { return JOptionPane.showConfirmDialog(this, message, title, optionType, messageType); + } + + @Override + public String showInputDialog(String message, String title) { + return JOptionPane.showInputDialog(null, message, title, JOptionPane.QUESTION_MESSAGE); } @Override diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java b/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java index daec44d3..e016c90c 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java +++ b/diylc/diylc-swing/src/org/diylc/swing/gui/TemplateDialog.java @@ -96,6 +96,11 @@ public class TemplateDialog extends JDialog { @Override public void showMessage(String message, String title, int messageType) { JOptionPane.showMessageDialog(TemplateDialog.this, message, title, messageType); + } + + @Override + public String showInputDialog(String message, String title) { + return JOptionPane.showInputDialog(null, message, title, JOptionPane.QUESTION_MESSAGE); } @Override diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileFilterEnum.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileFilterEnum.java index 8117f0eb..491c5c3b 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileFilterEnum.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/file/FileFilterEnum.java @@ -29,7 +29,7 @@ public enum FileFilterEnum { PNG("PNG Images (*.png)", "png"), PDF("PDF Files (*.pdf)", "pdf"), DIY("DIY Project Files (*.diy)", "diy"), EXCEL( "Excel Workbooks (*.xls)", "xls"), CSV("Comma Separated Files (*.csv)", "csv"), HTML("HTML Files (*.html)", - "html"), IMAGES("Image Files (*.png, *.jpg, *.gif)", "png", "jpg", "gif"), XML("XML Files (*.xml)", "xml"); + "html"), IMAGES("Image Files (*.png, *.jpg, *.gif)", "png", "jpg", "gif"), XML("XML Files (*.xml)", "xml"), TEST("Test Files (*.tst)", "tst"); FileFilter filter; String[] extensions; diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/test/TestMenuPlugin.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/test/TestMenuPlugin.java new file mode 100644 index 00000000..807ad2cc --- /dev/null +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/test/TestMenuPlugin.java @@ -0,0 +1,239 @@ +/* + + DIY Layout Creator (DIYLC). + Copyright (c) 2009-2018 held jointly by the individual authors. + + This file is part of DIYLC. + + DIYLC 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, either version 3 of the License, or + (at your option) any later version. + + DIYLC 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. + + You should have received a copy of the GNU General Public License + along with DIYLC. If not, see . + +*/ +package org.diylc.swing.plugins.test; + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.io.File; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.AbstractAction; +import javax.swing.KeyStroke; + +import org.apache.log4j.Logger; +import org.diylc.MockView; +import org.diylc.common.EventType; +import org.diylc.common.IPlugIn; +import org.diylc.common.IPlugInPort; +import org.diylc.common.ITask; +import org.diylc.presenter.Presenter; +import org.diylc.presenter.ProjectFileManager; +import org.diylc.swing.ISwingUI; +import org.diylc.swing.gui.DialogFactory; +import org.diylc.swing.plugins.file.FileFilterEnum; +import org.diylc.test.DIYTest; +import org.diylc.test.TestPlayer; +import org.diylc.test.TestPlayer.StepResult; + +import junit.framework.TestResult; + +/** + * Entry point class for help-related utilities. + * + * @author Branislav Stojkovic + */ +public class TestMenuPlugin implements IPlugIn { + + private static final String TEST_TITLE = "Test"; + + private static final Logger LOG = Logger.getLogger(TestMenuPlugin.class); + + private IPlugInPort plugInPort; + private ISwingUI swingUI; + + private ProjectFileManager projectFileManager; + private TestPlayer testPlayer; + + public TestMenuPlugin(ISwingUI swingUI) { + this.swingUI = swingUI; + swingUI.injectMenuAction(new StartRecordAction(), TEST_TITLE); + swingUI.injectMenuAction(new AddValidationAction(), TEST_TITLE); + swingUI.injectMenuAction(new StopRecordAction(), TEST_TITLE); + swingUI.injectMenuAction(new PlayTestAction(), TEST_TITLE); + } + + @Override + public void connect(IPlugInPort plugInPort) { + this.plugInPort = plugInPort; + projectFileManager = new ProjectFileManager(null); + testPlayer = new TestPlayer(); + } + + @Override + public EnumSet getSubscribedEventTypes() { + return null; + } + + @Override + public void processMessage(EventType eventType, Object... params) {} + + + + class StartRecordAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + public StartRecordAction() { + super(); + putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, + ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK)); + putValue(AbstractAction.NAME, "Start Recording"); + } + + @Override + public void actionPerformed(ActionEvent e) { + String name = swingUI.showInputDialog("Test Name:", "Start Recording"); + if (name == null) + return; + + LOG.info("Starting test recording..."); + + plugInPort.startRecording(name); + } + } + + class StopRecordAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + public StopRecordAction() { + super(); + putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_T, + ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK)); + putValue(AbstractAction.NAME, "Stop Recording"); + } + + @Override + public void actionPerformed(ActionEvent e) { + DIYTest test = plugInPort.stopRecording(); + + if (test == null) + return; + + LOG.info("Stopping test recording..."); + + final File file = DialogFactory.getInstance().showSaveDialog(swingUI.getOwnerFrame(), + FileFilterEnum.TEST.getFilter(), new File(test.getName() + ".tst"), FileFilterEnum.TEST.getExtensions()[0], null); + if (file != null) { + swingUI.executeBackgroundTask(new ITask() { + + @Override + public Void doInBackground() throws Exception { + LOG.debug("Saving test to " + file.getAbsolutePath()); + projectFileManager.serializeTestToFile(test, file.getAbsolutePath()); + return null; + } + + @Override + public void complete(Void result) {} + + @Override + public void failed(Exception e) { + swingUI.showMessage("Could not save test: " + e.getMessage(), "Error", + ISwingUI.ERROR_MESSAGE); + } + }, true); + } + } + } + + class PlayTestAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + public PlayTestAction() { + super(); + putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_P, + ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK)); + putValue(AbstractAction.NAME, "Play Test"); + } + + @Override + public void actionPerformed(ActionEvent e) { + LOG.info("Starting test recording..."); + + final File file = DialogFactory.getInstance().showOpenDialog(FileFilterEnum.TEST.getFilter(), + null, FileFilterEnum.TEST.getExtensions()[0], null); + + final Presenter presenter = new Presenter(new MockView()); + + if (file != null) { + swingUI.executeBackgroundTask(new ITask>() { + + @Override + public List doInBackground() throws Exception { + DIYTest test = projectFileManager.deserializeTestFromFile(file.getAbsolutePath()); + List testResults = testPlayer.play(presenter, test); + return testResults; + } + + @Override + public void complete(List result) { + StringBuilder sb = new StringBuilder(); + boolean hasError = false; + for (StepResult r : result) { + if (r.hasError()) { + sb.append("Step ").append(r.getStep()).append(" status: ").append(r.getStatus()).append("\n"); + hasError = true; + } + } + + if (!hasError) + sb.append("Test executed SUCCESFULLY."); + + swingUI.showMessage(sb.toString(), "Test Results", hasError ? ISwingUI.ERROR_MESSAGE : ISwingUI.INFORMATION_MESSAGE); + } + + @Override + public void failed(Exception e) { + swingUI.showMessage("Could not play test: " + e.getMessage(), "Error", + ISwingUI.ERROR_MESSAGE); + } + }, true); + } + } + } + + class AddValidationAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + public AddValidationAction() { + super(); + putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, + ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK)); + putValue(AbstractAction.NAME, "Add Validation Step"); + } + + @Override + public void actionPerformed(ActionEvent e) { + LOG.info("Adding test validation step"); + Map params = new HashMap(); + params.put(DIYTest.SNAPSHOT, plugInPort.createTestSnapshot()); + plugInPort.addValidation(params); + swingUI.showMessage("Validation step added.", "Test", ISwingUI.INFORMATION_MESSAGE); + } + } +} diff --git a/diylc/diylc-swing/test/org/diylc/MockView.java b/diylc/diylc-swing/test/org/diylc/MockView.java index 69659768..756e9700 100644 --- a/diylc/diylc-swing/test/org/diylc/MockView.java +++ b/diylc/diylc-swing/test/org/diylc/MockView.java @@ -34,4 +34,10 @@ public class MockView implements IView { return null; } + @Override + public String showInputDialog(String message, String title) { + // TODO Auto-generated method stub + return null; + } + }