You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2012/12/08 15:56:02 UTC

[34/53] [partial] ISIS-188: making structure of component viewers consistent

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/view/text/TextUtilsTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/view/text/TextUtilsTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/view/text/TextUtilsTest.java
new file mode 100644
index 0000000..bde96fd
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/view/text/TextUtilsTest.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.view.text;
+
+import junit.framework.TestCase;
+
+import org.apache.isis.viewer.dnd.drawing.Text;
+import org.apache.isis.viewer.dnd.viewer.drawing.DummyText;
+
+public class TextUtilsTest extends TestCase {
+
+    public void testDrawingTextTruncated() {
+        /* Word boundaries at 4, 11, 16, 21, 24 & 34 */
+        final String title = "test string that will be truncated";
+        final Text style = new DummyText();
+
+        assertEquals("test string that will be truncated", TextUtils.limitText(title, style, 340));
+
+        assertEquals("test string that will be...", TextUtils.limitText(title, style, 339));
+
+        assertEquals("test string that will...", TextUtils.limitText(title, style, 210 + 30));
+
+        assertEquals("test string that...", TextUtils.limitText(title, style, 199 + 30));
+
+        assertEquals("test string...", TextUtils.limitText(title, style, 140));
+
+        assertEquals("test...", TextUtils.limitText(title, style, 139));
+
+        assertEquals("test...", TextUtils.limitText(title, style, 70));
+
+        assertEquals("tes...", TextUtils.limitText(title, style, 60));
+    }
+
+    public void testDrawingTextTruncatedBeforeCommasEtc() {
+        final String title = "test string, that? is truncated";
+        final Text style = new DummyText();
+
+        assertEquals("test string, that...", TextUtils.limitText(title, style, 210));
+
+        assertEquals("test string...", TextUtils.limitText(title, style, 199));
+    }
+
+    public void testNoSpace() {
+        final String title = "test string, that? is truncated";
+        final Text style = new DummyText();
+
+        assertEquals("...", TextUtils.limitText(title, style, 5));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/ViewUpdateNotifierTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/ViewUpdateNotifierTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/ViewUpdateNotifierTest.java
new file mode 100644
index 0000000..32ffec6
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/ViewUpdateNotifierTest.java
@@ -0,0 +1,209 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import java.util.Vector;
+
+import junit.framework.Assert;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.jmock.auto.Mock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.commons.debug.DebugString;
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.runtime.authentication.AuthenticationManager;
+import org.apache.isis.core.runtime.authorization.AuthorizationManager;
+import org.apache.isis.core.runtime.imageloader.TemplateImageLoader;
+import org.apache.isis.core.runtime.userprofile.UserProfileLoader;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2.Mode;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSession;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSessionFactory;
+import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransaction;
+import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransactionManager;
+import org.apache.isis.runtimes.dflt.testsupport.IsisSystemWithFixtures;
+import org.apache.isis.viewer.dnd.DummyView;
+import org.apache.isis.viewer.dnd.DummyWorkspaceView;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.base.ViewUpdateNotifierImpl;
+import org.apache.isis.viewer.dnd.view.content.RootObject;
+
+public class ViewUpdateNotifierTest {
+
+    @Rule
+    public IsisSystemWithFixtures iswf = IsisSystemWithFixtures.builder().build();
+
+    private ExposedViewUpdateNotifier notifier;
+    
+    private ObjectAdapter adapter;
+
+    @Before
+    public void setUp() throws Exception {
+        Logger.getRootLogger().setLevel(Level.OFF);
+
+        iswf.persist(iswf.fixtures.smpl1);
+        
+        adapter = iswf.adapterFor(iswf.fixtures.smpl1);
+        
+        notifier = new ExposedViewUpdateNotifier();
+    }
+    
+    @After
+    public void tearDown() {
+        IsisContext.closeSession();
+    }
+
+    private DummyView createView(final ObjectAdapter object) {
+        final DummyView view = new DummyView();
+        view.setupContent(new RootObject(object));
+        return view;
+    }
+
+    @Test
+    public void testAddViewWithNonObjectContent() {
+        final DummyView view = createView(null);
+        notifier.add(view);
+        notifier.assertEmpty();
+    }
+
+    @Test
+    public void testAddViewWithObjectContent() {
+        final DummyView view = addViewForObject();
+        notifier.assertContainsViewForObject(view, adapter);
+    }
+
+    private DummyView addViewForObject() {
+        final DummyView view = createView(adapter);
+        notifier.add(view);
+        return view;
+    }
+
+    @Test
+    public void testAddViewIsIgnoredAfterFirstCall() {
+        final DummyView view = addViewForObject();
+        try {
+            notifier.add(view);
+            fail();
+        } catch (final IsisException expected) {
+        }
+    }
+
+    @Test
+    public void testDebug() throws Exception {
+        addViewForObject();
+        final DebugString debugString = new DebugString();
+        notifier.debugData(debugString);
+        assertNotNull(debugString.toString());
+    }
+
+    @Test
+    public void testRemoveView() {
+        final Vector vector = new Vector();
+        final DummyView view = createView(adapter);
+        vector.addElement(view);
+        notifier.setupViewsForObject(adapter, vector);
+
+        notifier.remove(view);
+        notifier.assertEmpty();
+    }
+
+    @Test
+    public void testViewDirty() {
+
+        //adapter.setupResolveState(ResolveState.RESOLVED);
+
+        final Vector<View> vector = new Vector<View>();
+        final DummyView view1 = createView(adapter);
+        vector.addElement(view1);
+
+        final DummyView view2 = createView(adapter);
+        vector.addElement(view2);
+
+        notifier.setupViewsForObject(adapter, vector);
+
+        notifier.invalidateViewsForChangedObjects();
+        assertEquals(0, view1.invalidateContent);
+        assertEquals(0, view2.invalidateContent);
+
+        IsisContext.getUpdateNotifier().addChangedObject(adapter);
+        notifier.invalidateViewsForChangedObjects();
+
+        assertEquals(1, view1.invalidateContent);
+        assertEquals(1, view2.invalidateContent);
+    }
+
+    
+    @Test
+    public void testDisposedViewsRemoved() {
+        final DummyWorkspaceView workspace = new DummyWorkspaceView();
+
+        final Vector<View> vector = new Vector<View>();
+        final DummyView view1 = createView(adapter);
+        view1.setParent(workspace);
+        workspace.addView(view1);
+        vector.addElement(view1);
+
+        final DummyView view2 = createView(adapter);
+        view2.setParent(workspace);
+        workspace.addView(view2);
+        vector.addElement(view2);
+
+        notifier.setupViewsForObject(adapter, vector);
+
+        notifier.invalidateViewsForChangedObjects();
+        assertEquals(0, view1.invalidateContent);
+        assertEquals(0, view2.invalidateContent);
+
+        IsisContext.getUpdateNotifier().addDisposedObject(adapter);
+        notifier.removeViewsForDisposedObjects();
+        assertEquals(0, workspace.getSubviews().length);
+
+    }
+}
+
+class ExposedViewUpdateNotifier extends ViewUpdateNotifierImpl {
+
+    public void assertContainsViewForObject(final View view, final ObjectAdapter object) {
+        Assert.assertTrue(viewListByAdapter.containsKey(object));
+        final List<View> viewsForObject = viewListByAdapter.get(object);
+        Assert.assertTrue(viewsForObject.contains(view));
+    }
+
+    public void setupViewsForObject(final ObjectAdapter object, final Vector<View> vector) {
+        viewListByAdapter.put(object, vector);
+    }
+
+    public void assertEmpty() {
+        Assert.assertTrue("Not empty", viewListByAdapter.isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ActionFieldBuilderTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ActionFieldBuilderTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ActionFieldBuilderTest.java
new file mode 100644
index 0000000..e927532
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ActionFieldBuilderTest.java
@@ -0,0 +1,227 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.basic;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.easymock.MockControl;
+import org.jmock.Expectations;
+import org.jmock.auto.Mock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.commons.authentication.AuthenticationSession;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.runtime.authentication.AuthenticationManager;
+import org.apache.isis.core.runtime.authorization.AuthorizationManager;
+import org.apache.isis.core.runtime.imageloader.TemplateImageLoader;
+import org.apache.isis.core.runtime.userprofile.UserProfile;
+import org.apache.isis.core.runtime.userprofile.UserProfileLoader;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2.Mode;
+import org.apache.isis.runtimes.dflt.runtime.authentication.exploration.ExplorationSession;
+import org.apache.isis.runtimes.dflt.runtime.system.DeploymentType;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContextStatic;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSessionFactory;
+import org.apache.isis.runtimes.dflt.runtime.system.session.IsisSessionFactoryDefault;
+import org.apache.isis.viewer.dnd.DummyView;
+import org.apache.isis.viewer.dnd.dialog.ActionFieldBuilder;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewFactory;
+
+public class ActionFieldBuilderTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    protected TemplateImageLoader mockTemplateImageLoader;
+    @Mock
+    protected SpecificationLoaderSpi mockSpecificationLoader;
+    @Mock
+    protected PersistenceSessionFactory mockPersistenceSessionFactory;
+    @Mock
+    private UserProfileLoader mockUserProfileLoader;
+    @Mock
+    protected AuthenticationManager mockAuthenticationManager;
+    @Mock
+    protected AuthorizationManager mockAuthorizationManager;
+
+
+    private IsisConfiguration configuration;
+    private List<Object> servicesList;
+    private OidMarshaller oidMarshaller;
+
+    private ActionFieldBuilder builder;
+
+    @Before
+    public void setUp() throws Exception {
+        Logger.getRootLogger().setLevel(Level.OFF);
+
+        configuration = new IsisConfigurationDefault();
+        servicesList = Collections.emptyList();
+        
+        oidMarshaller = new OidMarshaller();
+
+        context.checking(new Expectations() {
+            {
+                ignoring(mockSpecificationLoader);
+                ignoring(mockPersistenceSessionFactory);
+
+                one(mockUserProfileLoader).getProfile(with(any(AuthenticationSession.class)));
+                will(returnValue(new UserProfile()));
+
+                ignoring(mockTemplateImageLoader);
+                ignoring(mockAuthenticationManager);
+                ignoring(mockAuthorizationManager);
+            }
+        });
+
+        final ViewFactory subviewSpec = new ViewFactory() {
+            @Override
+            public View createView(final Content content, final Axes axes, final int fieldNumber) {
+                return new DummyView();
+            }
+        };
+
+        final IsisSessionFactoryDefault sessionFactory = new IsisSessionFactoryDefault(DeploymentType.EXPLORATION, configuration, mockTemplateImageLoader, mockSpecificationLoader, mockAuthenticationManager, mockAuthorizationManager, mockUserProfileLoader, mockPersistenceSessionFactory, servicesList, oidMarshaller);
+
+        IsisContext.setConfiguration(sessionFactory.getConfiguration());
+        IsisContextStatic.createRelaxedInstance(sessionFactory);
+        IsisContextStatic.openSession(new ExplorationSession());
+
+        builder = new ActionFieldBuilder(subviewSpec);
+
+    }
+
+    @After
+    public void tearDown() {
+        IsisContext.closeSession();
+    }
+
+    @Test
+    public void testUpdateBuild() {
+        final MockControl control = MockControl.createControl(View.class);
+        final View view = (View) control.getMock();
+
+        control.expectAndDefaultReturn(view.getView(), view);
+        control.expectAndDefaultReturn(view.getContent(), null);
+
+        /*
+         * DummyView[] views = new DummyView[2]; views[1] = new DummyView();
+         * views[1].setupContent(new ObjectParameter("name", null, null, false,
+         * 1, actionContent)); view.setupSubviews(views);
+         */
+
+        control.replay();
+
+        // builder.build(view);
+
+        control.verify();
+    }
+
+    /*
+     * // TODO fails on server as cant load X11 for Text class public void
+     * xxxtestNewBuild() { view.setupSubviews(new View[0]);
+     * 
+     * view.addAction("add TextView0 null");
+     * view.addAction("add MockView1/LabelBorder"); view.addAction("add
+     * MockView2/LabelBorder");
+     * 
+     * builder.build(view);
+     * 
+     * view.verify(); } public void
+     * xxxtestUpdateBuildWhereParameterHasChangedFromNullToAnObject() {
+     * DummyView[] views = new DummyView[2]; views[1] = new DummyView();
+     * ObjectParameter objectParameter = new ObjectParameter("name", null, null,
+     * false, 1, actionContent); views[1].setupContent(objectParameter);
+     * view.setupSubviews(views);
+     * 
+     * actionContent.setParameter(0, new DummyObjectAdapter());
+     * 
+     * view.addAction("replace MockView1 with MockView2/LabelBorder");
+     * 
+     * builder.build(view);
+     * 
+     * view.verify(); }
+     * 
+     * public void
+     * xxxtestUpdateBuildWhereParameterHasChangedFromAnObjectToNull() {
+     * DummyView[] views = new DummyView[2]; views[1] = new DummyView();
+     * ObjectParameter objectParameter = new ObjectParameter("name", new
+     * DummyObjectAdapter(), null, false, 1, actionContent);
+     * views[1].setupContent(objectParameter); view.setupSubviews(views);
+     * 
+     * objectParameter.setObject(null);
+     * 
+     * view.addAction("replace MockView1 with MockView2/LabelBorder");
+     * 
+     * builder.build(view);
+     * 
+     * view.verify(); }
+     * 
+     * public void
+     * xxxtestUpdateBuildWhereParameterHasChangedFromOneObjectToAnother() {
+     * DummyView[] views = new DummyView[2]; views[1] = new DummyView();
+     * ObjectParameter objectParameter = new ObjectParameter("name", new
+     * DummyObjectAdapter(), null, false, 1, actionContent);
+     * views[1].setupContent(objectParameter); view.setupSubviews(views);
+     * 
+     * objectParameter.setObject(new DummyObjectAdapter());
+     * 
+     * view.addAction("replace MockView1 with MockView2/LabelBorder");
+     * 
+     * builder.build(view);
+     * 
+     * view.verify(); }
+     * 
+     * public void xxtestUpdateBuildWhereParameterObjectSetButToSameObject() {
+     * DummyView[] views = new DummyView[2]; views[1] = new DummyView();
+     * DummyObjectAdapter dummyObjectAdapter = new DummyObjectAdapter();
+     * ObjectParameter objectParameter = new ObjectParameter("name",
+     * dummyObjectAdapter, null, false, 1, actionContent);
+     * views[1].setupContent(objectParameter); view.setupSubviews(views);
+     * 
+     * actionContent.setParameter(0, dummyObjectAdapter); //
+     * objectParameter.setObject(dummyObjectAdapter);
+     * 
+     * builder.build(view);
+     * 
+     * view.verify(); } }
+     * 
+     * class MockActionHelper extends ActionHelper {
+     * 
+     * protected MockActionHelper( ObjectAdapter target, Action action, String[]
+     * labels, ObjectAdapter[] parameters, ObjectSpecification[] parameterTypes,
+     * boolean[] required) { super(target, action, labels, parameters,
+     * parameterTypes, required); }
+     */
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/NullColor.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/NullColor.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/NullColor.java
new file mode 100644
index 0000000..d02f949
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/NullColor.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.basic;
+
+import org.apache.isis.viewer.dnd.drawing.Color;
+
+public class NullColor implements Color {
+
+    @Override
+    public Color brighter() {
+        return null;
+    }
+
+    @Override
+    public Color darker() {
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/PlacementStrategyImplTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/PlacementStrategyImplTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/PlacementStrategyImplTest.java
new file mode 100644
index 0000000..c0db017
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/PlacementStrategyImplTest.java
@@ -0,0 +1,209 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.basic;
+
+import org.jmock.auto.Mock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2.Mode;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.dnd.DummyView;
+import org.apache.isis.viewer.dnd.DummyWorkspaceView;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.view.PlacementStrategyImpl;
+
+public class PlacementStrategyImplTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private IsisConfiguration mockConfiguration;
+    
+    private static final int ORIGINAL_Y = 70;
+    private static final int ORIGINAL_X = 50;
+    private static final int PADDING = 10;
+    private static final int NEW_VIEW_HEIGHT = 150;
+    private static final int NEW_VIEW_WIDTH = 100;
+    private static final int ROOT_VIEW_HEIGHT = 190;
+    private static final int ROOT_VIEW_WIDTH = 120;
+    private static final int WORKSPACE_HEIGHT = 800;
+    private static final int WORKSPACE_WIDTH = 1000;
+    
+    private DummyWorkspaceView workspace;
+    private DummyView existingView;
+    private DummyView newView;
+    private PlacementStrategyImpl strategy;
+    
+
+    @Before
+    public void setUp() throws Exception {
+        IsisContext.setConfiguration(mockConfiguration);
+
+        workspace = new DummyWorkspaceView();
+        workspace.setSize(new Size(WORKSPACE_WIDTH, WORKSPACE_HEIGHT));
+
+        existingView = new DummyView();
+        existingView.setSize(new Size(ROOT_VIEW_WIDTH, ROOT_VIEW_HEIGHT));
+        existingView.setLocation(new Location(ORIGINAL_X, ORIGINAL_Y));
+        existingView.setParent(workspace);
+        workspace.addView(existingView);
+
+        newView = new DummyView();
+        newView.setupRequiredSize(new Size(NEW_VIEW_WIDTH, NEW_VIEW_HEIGHT));
+
+        strategy = new PlacementStrategyImpl();
+    }
+
+    @Test
+    public void defaultWhenNoRelativeView() throws Exception {
+        final Location location = strategy.determinePlacement(workspace, null, newView);
+        Assert.assertEquals(new Location(), location);
+    }
+
+    @Test
+    public void placeToRight() throws Exception {
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(ORIGINAL_X + ROOT_VIEW_WIDTH + PADDING, 70), location);
+    }
+
+    @Test
+    public void adjustWhenOnTopOfExistingField() throws Exception {
+        final DummyView anotherView = new DummyView();
+        anotherView.setLocation(new Location(ORIGINAL_X + ROOT_VIEW_WIDTH + PADDING, ORIGINAL_Y));
+        anotherView.setSize(new Size(50, 50));
+        workspace.addView(anotherView);
+
+        newView.setupRequiredSize(new Size(NEW_VIEW_WIDTH, NEW_VIEW_HEIGHT));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(ORIGINAL_X + ROOT_VIEW_WIDTH + PADDING + PADDING * 4, ORIGINAL_Y + PADDING * 4), location);
+    }
+
+    @Test
+    public void placeBelow() throws Exception {
+        existingView.setLocation(new Location(WORKSPACE_WIDTH - 200, 100));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(WORKSPACE_WIDTH - 200, 100 + +ROOT_VIEW_HEIGHT + PADDING), location);
+    }
+
+    @Test
+    public void placeToLeft() throws Exception {
+        existingView.setLocation(new Location(WORKSPACE_WIDTH - 200, 500));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(WORKSPACE_WIDTH - 200 - PADDING - NEW_VIEW_WIDTH, 500), location);
+    }
+
+    @Test
+    public void placeAbove() throws Exception {
+        existingView.setLocation(new Location(100, 700));
+        existingView.setSize(new Size(900, 100));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(100, 700 - NEW_VIEW_HEIGHT - PADDING), location);
+    }
+
+    @Test
+    public void viewLargerThanWorkspace() throws Exception {
+        existingView.setLocation(new Location(100, 100));
+        newView.setupRequiredSize(new Size(1100, 900));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals("should be placed on top of original, but slightly offset", new Location(0, 0), location);
+    }
+
+    @Test
+    public void viewLargerThanWorkspaceAndExisitingViewInCorner() throws Exception {
+        existingView.setLocation(new Location(200, 300));
+        existingView.setLocation(new Location(0, 0));
+        newView.setupRequiredSize(new Size(1100, 900));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals("should be placed on top of original, but slightly offset", new Location(0, 0), location);
+    }
+
+    @Test
+    public void notEnoughFreeSpaceInAnyDirection() throws Exception {
+        existingView.setLocation(new Location(100, 100));
+        existingView.setSize(new Size(800, 600));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals("should be placed on top of original, but slightly offset", new Location(100 + PADDING * 6, 100 + PADDING * 6), location);
+    }
+
+    @Test
+    public void wideComponentShiftsToLeft() throws Exception {
+        newView.setupRequiredSize(new Size(1200, NEW_VIEW_HEIGHT));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(0, ORIGINAL_Y + ROOT_VIEW_HEIGHT + PADDING), location);
+    }
+
+    @Test
+    public void tallComponentShiftsUp() throws Exception {
+        newView.setupRequiredSize(new Size(NEW_VIEW_WIDTH, 1000));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(ORIGINAL_X + ROOT_VIEW_WIDTH + PADDING, 0), location);
+    }
+
+    @Test
+    public void wideComponentsDontCompletelyOverlap() throws Exception {
+        final DummyView anotherView = new DummyView();
+        anotherView.setLocation(new Location(0, 70 + ROOT_VIEW_HEIGHT + PADDING));
+        anotherView.setSize(new Size(WORKSPACE_WIDTH, 100));
+        workspace.addView(anotherView);
+
+        newView.setupRequiredSize(new Size(WORKSPACE_WIDTH, NEW_VIEW_HEIGHT));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(0, 70 + ROOT_VIEW_HEIGHT + PADDING + PADDING * 4), location);
+    }
+
+    @Test
+    public void tallComponentsDontCompletelyOverlap() throws Exception {
+        final DummyView anotherView = new DummyView();
+        anotherView.setLocation(new Location(ORIGINAL_X + ROOT_VIEW_WIDTH + PADDING, 0));
+        anotherView.setSize(new Size(100, WORKSPACE_HEIGHT));
+        workspace.addView(anotherView);
+
+        newView.setupRequiredSize(new Size(NEW_VIEW_WIDTH, WORKSPACE_HEIGHT));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(ORIGINAL_X + ROOT_VIEW_WIDTH + PADDING + PADDING * 4, 0), location);
+    }
+
+    @Test
+    public void noSpaceToMoveNewView() throws Exception {
+        newView.setupRequiredSize(new Size(WORKSPACE_WIDTH - ORIGINAL_X, WORKSPACE_HEIGHT - ORIGINAL_Y));
+
+        final Location location = strategy.determinePlacement(workspace, existingView, newView);
+        Assert.assertEquals(new Location(ORIGINAL_X, ORIGINAL_Y), location);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ScrollBorderTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ScrollBorderTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ScrollBorderTest.java
new file mode 100644
index 0000000..dbd87c1
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/ScrollBorderTest.java
@@ -0,0 +1,150 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.basic;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.runtime.authentication.AuthenticationManager;
+import org.apache.isis.core.runtime.authorization.AuthorizationManager;
+import org.apache.isis.core.runtime.imageloader.TemplateImageLoader;
+import org.apache.isis.core.runtime.userprofile.UserProfileLoader;
+import org.apache.isis.runtimes.dflt.runtime.system.DeploymentType;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContextStatic;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSessionFactory;
+import org.apache.isis.runtimes.dflt.runtime.system.session.IsisSessionFactory;
+import org.apache.isis.runtimes.dflt.runtime.system.session.IsisSessionFactoryDefault;
+import org.apache.isis.viewer.dnd.DummyView;
+import org.apache.isis.viewer.dnd.TestToolkit;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewAreaType;
+import org.apache.isis.viewer.dnd.view.border.ScrollBorder;
+
+@RunWith(JMock.class)
+public class ScrollBorderTest {
+
+    private final Mockery mockery = new JUnit4Mockery();
+
+    protected TemplateImageLoader mockTemplateImageLoader;
+    protected SpecificationLoaderSpi mockSpecificationLoader;
+    protected PersistenceSessionFactory mockPersistenceSessionFactory;
+    private UserProfileLoader mockUserProfileLoader;
+    protected AuthenticationManager mockAuthenticationManager;
+    protected AuthorizationManager mockAuthorizationManager;
+
+    @Before
+    public void setUp() throws Exception {
+        LogManager.getRootLogger().setLevel(Level.OFF);
+
+        
+
+        mockTemplateImageLoader = mockery.mock(TemplateImageLoader.class);
+        mockSpecificationLoader = mockery.mock(SpecificationLoaderSpi.class);
+        mockUserProfileLoader = mockery.mock(UserProfileLoader.class);
+        mockPersistenceSessionFactory = mockery.mock(PersistenceSessionFactory.class);
+        mockAuthenticationManager = mockery.mock(AuthenticationManager.class);
+        mockAuthorizationManager = mockery.mock(AuthorizationManager.class);
+
+        mockery.checking(new Expectations() {
+            {
+                ignoring(mockTemplateImageLoader);
+                ignoring(mockSpecificationLoader);
+                ignoring(mockUserProfileLoader);
+                ignoring(mockPersistenceSessionFactory);
+                ignoring(mockAuthenticationManager);
+                ignoring(mockAuthorizationManager);
+            }
+        });
+
+        TestToolkit.createInstance();
+
+        final IsisConfigurationDefault configuration = new IsisConfigurationDefault();
+        final IsisSessionFactory sessionFactory = new IsisSessionFactoryDefault(DeploymentType.EXPLORATION, configuration, mockTemplateImageLoader, mockSpecificationLoader, mockAuthenticationManager, mockAuthorizationManager, mockUserProfileLoader, mockPersistenceSessionFactory, Collections.emptyList(), new OidMarshaller());
+        sessionFactory.init();
+        IsisContextStatic.createRelaxedInstance(sessionFactory);
+    }
+
+    @Test
+    public void testScrollBar() {
+        final DummyView innerView = new DummyView();
+        innerView.setupRequiredSize(new Size(100, 200));
+        final View view = new ScrollBorder(innerView);
+
+        ViewAreaType type = view.viewAreaType(new Location(20, 190));
+        Assert.assertEquals(ViewAreaType.INTERNAL, type);
+
+        type = view.viewAreaType(new Location(95, 20));
+        Assert.assertEquals(ViewAreaType.INTERNAL, type);
+    }
+
+    @Test
+    public void testSetSizeSetsUpContentAndHeaderSizes() {
+        final DummyView contentView = new DummyView();
+        contentView.setupRequiredSize(new Size(300, 400));
+
+        final DummyView topHeader = new DummyView();
+        topHeader.setupRequiredSize(new Size(0, 20));
+
+        final DummyView leftHeader = new DummyView();
+        leftHeader.setupRequiredSize(new Size(30, 0));
+
+        final View scrollBorder = new ScrollBorder(contentView, leftHeader, topHeader);
+
+        scrollBorder.setSize(new Size(100, 200));
+
+        Assert.assertEquals(new Size(300, 400), contentView.getSize());
+        Assert.assertEquals(new Size(300, 20), topHeader.getSize());
+        Assert.assertEquals(new Size(30, 400), leftHeader.getSize());
+
+    }
+
+    @Test
+    public void testSetSizeSetsUpContentAndHeaderSizes2() {
+        final DummyView contentView = new DummyView();
+        contentView.setupRequiredSize(new Size(300, 400));
+
+        final DummyView topHeader = new DummyView();
+        topHeader.setupRequiredSize(new Size(0, 20));
+
+        final DummyView leftHeader = new DummyView();
+        leftHeader.setupRequiredSize(new Size(30, 0));
+
+        final View scrollBorder = new ScrollBorder(contentView, leftHeader, topHeader);
+
+        scrollBorder.setSize(new Size(100, 200));
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/SimpleCollectionSorterTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/SimpleCollectionSorterTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/SimpleCollectionSorterTest.java
new file mode 100644
index 0000000..b9dfdc0
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/SimpleCollectionSorterTest.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.basic;
+
+import static org.junit.Assert.assertEquals;
+
+import org.jmock.Expectations;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2.Mode;
+import org.apache.isis.viewer.dnd.view.collection.CollectionSorter;
+import org.apache.isis.viewer.dnd.view.collection.SimpleCollectionSorter;
+import org.apache.isis.viewer.dnd.view.collection.TitleComparator;
+
+public class SimpleCollectionSorterTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Test
+    public void testSortByTitle() {
+        final ObjectAdapter[] instances = new ObjectAdapter[] { adapterWithTitle("one"), adapterWithTitle("two"), adapterWithTitle("three"), adapterWithTitle("four"), };
+
+        final SimpleCollectionSorter sorter = new SimpleCollectionSorter();
+        sorter.sort(instances, new TitleComparator(), CollectionSorter.NORMAL);
+
+        assertEquals("four", instances[0].titleString());
+        assertEquals("one", instances[1].titleString());
+        assertEquals("three", instances[2].titleString());
+        assertEquals("two", instances[3].titleString());
+    }
+
+    @Test
+    public void testSortByTitleReversed() {
+        final ObjectAdapter[] instances = new ObjectAdapter[] { adapterWithTitle("one"), adapterWithTitle("two"), adapterWithTitle("three"), adapterWithTitle("four"), };
+
+        final SimpleCollectionSorter sorter = new SimpleCollectionSorter();
+        sorter.sort(instances, new TitleComparator(), CollectionSorter.REVERSED);
+
+        assertEquals("two", instances[0].titleString());
+        assertEquals("three", instances[1].titleString());
+        assertEquals("one", instances[2].titleString());
+        assertEquals("four", instances[3].titleString());
+    }
+
+    private ObjectAdapter adapterWithTitle(final String title) {
+        final ObjectAdapter adapter = context.mock(ObjectAdapter.class, title);
+        context.checking(new Expectations() {
+            {
+                allowing(adapter).titleString();
+                will(returnValue(title));
+            }
+        });
+        return adapter;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/TitleTextTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/TitleTextTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/TitleTextTest.java
new file mode 100644
index 0000000..77ba36d
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/basic/TitleTextTest.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.basic;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2.Mode;
+import org.apache.isis.viewer.dnd.DummyCanvas;
+import org.apache.isis.viewer.dnd.DummyView;
+import org.apache.isis.viewer.dnd.TestToolkit;
+import org.apache.isis.viewer.dnd.drawing.Color;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.drawing.Text;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.text.TitleText;
+import org.apache.isis.viewer.dnd.viewer.drawing.DummyText;
+
+public class TitleTextTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    private TitleText titleText;
+    private String title;
+    private TestCanvas canvas;
+    private DummyView view;
+
+    @Before
+    public void setUp() throws Exception {
+        
+        TestToolkit.createInstance();
+
+        view = new DummyView();
+        final Text style = new DummyText();
+        titleText = new TitleText(view, style, Toolkit.getColor(ColorsAndFonts.COLOR_BLACK)) {
+            @Override
+            protected String title() {
+                return title;
+            }
+        };
+        canvas = new TestCanvas();
+    }
+
+    // TODO these tests won't work on server that doesn't have graphics - eg a
+    // Linux box without X
+    /*
+     * public void XXtestDrawCanvas() { title = "abcde"; titleText.draw(canvas,
+     * 10, 20); }
+     * 
+     * public void testDrawCanvasDefaultColor() { titleText.draw(canvas, 10,
+     * 20); assertEquals(Toolkit.getColor(ColorsAndFonts.COLOR_BLACK),
+     * canvas.color); }
+     * 
+     * public void testDrawCanvasCanDrop() { view.getState().setCanDrop();
+     * 
+     * titleText.draw(canvas, 10, 20);
+     * assertEquals(Toolkit.getColor(ColorsAndFonts.COLOR_VALID), canvas.color);
+     * }
+     * 
+     * public void testDrawCanvasCantDrop() { view.getState().setCantDrop();
+     * 
+     * titleText.draw(canvas, 10, 20);
+     * assertEquals(ColorsAndFonts.COLOR_INVALID, canvas.color); }
+     * 
+     * public void testDrawCanvasIdentifier() {
+     * view.getState().setContentIdentified();
+     * 
+     * titleText.draw(canvas, 10, 20);
+     * assertEquals(Toolkit.getColor("primary1"), canvas.color); }
+     */
+
+    @Test
+    public void testDrawingLocation() {
+        titleText.draw(canvas, 10, 20);
+        assertEquals(10, canvas.x);
+        assertEquals(20, canvas.y);
+    }
+
+    @Test
+    public void testDrawingText() {
+        title = "test string";
+        titleText.draw(canvas, 10, 20);
+        assertEquals("test string", canvas.text);
+    }
+
+    @Test
+    public void testGetSize() {
+        title = "abcde";
+
+        assertEquals(10 * 5, titleText.getSize().getWidth());
+        assertEquals(8, titleText.getSize().getHeight());
+    }
+
+}
+
+class TestCanvas extends DummyCanvas {
+    String text;
+    int x;
+    int y;
+    Color color;
+
+    @Override
+    public void drawText(final String text, final int x, final int y, final Color color, final Text style) {
+        this.text = text;
+        this.x = x;
+        this.y = y;
+        this.color = color;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/builder/StackLayoutTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/builder/StackLayoutTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/builder/StackLayoutTest.java
new file mode 100644
index 0000000..29b7730
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/builder/StackLayoutTest.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.builder;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.isis.viewer.dnd.DummyView;
+import org.apache.isis.viewer.dnd.DummyViewSpecification;
+import org.apache.isis.viewer.dnd.DummyWorkspaceView;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.composite.StackLayout;
+
+public class StackLayoutTest {
+    private StackLayout layout;
+    private DummyView subview1;
+    private DummyView subview2;
+
+    @Before
+    public void setUp() throws Exception {
+        layout = new StackLayout();
+
+        subview1 = new DummyView();
+        subview1.setupSpecification(new DummyViewSpecification());
+        subview1.setupRequiredSize(new Size(100, 20));
+
+        subview2 = new DummyView();
+        subview2.setupSpecification(new DummyViewSpecification());
+        subview2.setupRequiredSize(new Size(120, 20));
+
+    }
+
+    @Test
+    public void noContentNoSize() throws Exception {
+        final View view = new DummyView();
+        Assert.assertEquals(new Size(), layout.getRequiredSize(view));
+    }
+
+    @Test
+    public void sameSizeAsOnlyComponent() throws Exception {
+        final View view = new DummyWorkspaceView();
+        final DummyView subview = new DummyView();
+        subview.setupRequiredSize(new Size(100, 20));
+        view.addView(subview);
+
+        Assert.assertEquals(new Size(100, 20), layout.getRequiredSize(view));
+    }
+
+    @Test
+    public void sameWidthAsWidestComponentAndHeightTotalOfAll() throws Exception {
+        final View view = new DummyWorkspaceView();
+        view.addView(subview1);
+        view.addView(subview2);
+
+        Assert.assertEquals(new Size(120, 40), layout.getRequiredSize(view));
+    }
+
+    @Test
+    public void layoutToMaxWidth() throws Exception {
+        final View view = new DummyWorkspaceView();
+        view.addView(subview1);
+        view.addView(subview2);
+
+        layout.layout(view, new Size(110, 60));
+
+        Assert.assertEquals(new Size(100, 20), subview1.getSize());
+        Assert.assertEquals(new Size(110, 20), subview2.getSize());
+    }
+
+    @Test
+    public void layoutFixedWidth() throws Exception {
+        final View view = new DummyWorkspaceView();
+        view.addView(subview1);
+        view.addView(subview2);
+
+        layout = new StackLayout(true);
+        layout.layout(view, Size.createMax());
+
+        Assert.assertEquals(new Size(120, 20), subview1.getSize());
+        Assert.assertEquals(new Size(120, 20), subview2.getSize());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/content/TextParseableField_ParseTextEntry.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/content/TextParseableField_ParseTextEntry.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/content/TextParseableField_ParseTextEntry.java
new file mode 100644
index 0000000..fcf2fa4
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/content/TextParseableField_ParseTextEntry.java
@@ -0,0 +1,172 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.content;
+
+import org.jmock.Expectations;
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Allow;
+import org.apache.isis.core.metamodel.consent.Veto;
+import org.apache.isis.core.metamodel.facets.object.parseable.InvalidEntryException;
+import org.apache.isis.core.metamodel.facets.object.parseable.ParseableFacet;
+import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2.Mode;
+import org.apache.isis.viewer.dnd.view.field.TextParseableFieldImpl;
+
+public class TextParseableField_ParseTextEntry {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private ObjectAdapter mockParent;
+    @Mock
+    private ObjectAdapter mockChild;
+    @Mock
+    private OneToOneAssociation mockOtoa;
+    @Mock
+    private ObjectSpecification mockOtoaSpec;
+    @Mock
+    private ParseableFacet mockParseableFacet;
+    @Mock
+    private ObjectAdapter mockParsedText;
+    @Mock
+    private ValidatingInteractionAdvisor mockValidatingInteractionAdvisorFacet;
+
+    private TextParseableFieldImpl fieldImpl;
+
+    @Before
+    public void setUp() throws Exception {
+
+        context.checking(new Expectations() {
+            {
+                allowing(mockOtoa).getIdentifier();
+
+                allowing(mockOtoa).getSpecification();
+                will(returnValue(mockOtoaSpec));
+
+                one(mockOtoaSpec).getFacet(ParseableFacet.class);
+                will(returnValue(mockParseableFacet));
+            }
+        });
+
+        fieldImpl = new TextParseableFieldImpl(mockParent, mockChild, mockOtoa);
+    }
+
+
+    @Test
+    public void parsedTextIsValidForSpecAndCorrespondingObjectValidAsAssociation() {
+
+        context.checking(new Expectations() {
+            {
+                one(mockParseableFacet).parseTextEntry(mockChild, "foo", null);
+                will(returnValue(mockParsedText));
+
+                atLeast(1).of(mockOtoa).isAssociationValid(mockParent, mockParsedText);
+                will(returnValue(Allow.DEFAULT));
+
+                one(mockOtoa).isMandatory();
+            }
+        });
+
+        fieldImpl.parseTextEntry("foo");
+    }
+
+    @Test(expected = InvalidEntryException.class)
+    public void parsedTextIsNullWhenMandatoryThrowsException() {
+
+        context.checking(new Expectations() {
+            {
+                one(mockParseableFacet).parseTextEntry(mockChild, "foo", null);
+                will(returnValue(null));
+
+                atLeast(1).of(mockOtoa).isAssociationValid(mockParent, null);
+                will(returnValue(Allow.DEFAULT));
+
+                one(mockOtoa).isMandatory();
+                will(returnValue(true));
+            }
+        });
+
+        fieldImpl.parseTextEntry("foo");
+    }
+
+    @Test
+    public void parsedTextIsValidAccordingToSpecificationFacet() {
+
+        context.checking(new Expectations() {
+            {
+                one(mockParseableFacet).parseTextEntry(mockChild, "foo", null);
+                will(returnValue(mockParsedText));
+
+                atLeast(1).of(mockOtoa).isAssociationValid(mockParent, mockParsedText);
+                will(returnValue(Allow.DEFAULT));
+
+                allowing(mockOtoa).isMandatory();
+                will(returnValue(true));
+            }
+        });
+
+        fieldImpl.parseTextEntry("foo");
+    }
+
+    @Test(expected = InvalidEntryException.class)
+    public void parsedTextIsInvalidAccordingToSpecification() {
+
+        context.checking(new Expectations() {
+            {
+                allowing(mockParseableFacet).parseTextEntry(mockChild, "foo", null);
+                will(returnValue(mockParsedText));
+
+                atLeast(1).of(mockOtoa).isAssociationValid(mockParent, mockParsedText);
+                will(returnValue(Veto.DEFAULT));
+
+                allowing(mockOtoa).isMandatory();
+                will(returnValue(true));
+            }
+        });
+
+        fieldImpl.parseTextEntry("foo");
+    }
+
+    @Test(expected = InvalidEntryException.class)
+    public void parsedTextIsInvalidAccordingToAssociation() {
+
+        context.checking(new Expectations() {
+            {
+                allowing(mockParseableFacet).parseTextEntry(mockChild, "foo", null);
+                will(returnValue(mockParsedText));
+
+                one(mockOtoa).isAssociationValid(mockParent, mockParsedText);
+                will(returnValue(Veto.DEFAULT));
+            }
+        });
+
+        fieldImpl.parseTextEntry("foo");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/BoundsTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/BoundsTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/BoundsTest.java
new file mode 100644
index 0000000..43515f1
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/BoundsTest.java
@@ -0,0 +1,275 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.drawing;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+import org.apache.isis.viewer.dnd.drawing.Bounds;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Padding;
+import org.apache.isis.viewer.dnd.drawing.Size;
+
+public class BoundsTest extends TestCase {
+
+    public static void main(final String[] args) {
+        junit.textui.TestRunner.run(BoundsTest.class);
+    }
+
+    private Bounds b;
+
+    @Override
+    protected void setUp() throws Exception {
+        Logger.getRootLogger().setLevel(Level.OFF);
+        b = new Bounds(5, 10, 10, 20);
+    }
+
+    public void testContains() {
+        assertTrue(b.contains(new Location(8, 15)));
+        assertTrue(b.contains(new Location(5, 10)));
+        assertFalse(b.contains(new Location(4, 10)));
+        assertFalse(b.contains(new Location(15, 10)));
+        assertTrue(b.contains(new Location(10, 29)));
+    }
+
+    public void testNotEquals() {
+        Bounds c = new Bounds(0, 10, 10, 20);
+        assertFalse(c.equals(b));
+
+        c = new Bounds(5, 0, 10, 20);
+        assertFalse(c.equals(b));
+
+        c = new Bounds(5, 10, 0, 20);
+        assertFalse(c.equals(b));
+
+        c = new Bounds(5, 10, 10, 0);
+        assertFalse(c.equals(b));
+    }
+
+    public void testEquals() {
+        final Bounds c = new Bounds(5, 10, 10, 20);
+        assertTrue(c.equals(b));
+        assertTrue(b.equals(c));
+    }
+
+    public void testContracSize() {
+        b.contract(new Size(5, 12));
+        assertEquals(5, b.getWidth());
+        assertEquals(8, b.getHeight());
+    }
+
+    public void testContractHeight() {
+        b.contractHeight(12);
+        assertEquals(8, b.getHeight());
+    }
+
+    public void testContractPadding() {
+        b.contract(new Padding(2, 4, 1, 3));
+        assertEquals(3, b.getWidth());
+        assertEquals(17, b.getHeight());
+        assertEquals(9, b.getX());
+        assertEquals(12, b.getY());
+    }
+
+    public void testContracWidth() {
+        b.contractWidth(5);
+        assertEquals(5, b.getWidth());
+    }
+
+    public void testCopyBounds() {
+        final Bounds c = new Bounds();
+        c.setBounds(b);
+
+        assertEquals(5, b.getX());
+        assertEquals(10, b.getY());
+        assertEquals(10, b.getWidth());
+        assertEquals(20, b.getHeight());
+    }
+
+    public void testDefaultBounds() {
+        final Bounds b = new Bounds();
+        assertEquals(0, b.getX());
+        assertEquals(0, b.getY());
+        assertEquals(0, b.getWidth());
+        assertEquals(0, b.getHeight());
+    }
+
+    public void testDownLeftIntersects() {
+        final Bounds c = new Bounds(b);
+        c.translate(-5, -5);
+        assertTrue(b.intersects(c));
+
+        c.translate(-b.getWidth(), 0);
+        assertFalse(b.intersects(c));
+    }
+
+    public void testEnclosingUnion() {
+        final Bounds c = new Bounds(10, 20, 5, 5);
+        final Bounds u = new Bounds(b);
+        u.union(c);
+        assertEquals(b, u);
+    }
+
+    public void testExplicitBounds() {
+        assertEquals(5, b.getX());
+        assertEquals(10, b.getY());
+        assertEquals(10, b.getWidth());
+        assertEquals(20, b.getHeight());
+
+        final Bounds b1 = new Bounds(b);
+        assertEquals(5, b1.getX());
+        assertEquals(10, b1.getY());
+        assertEquals(10, b1.getWidth());
+        assertEquals(20, b1.getHeight());
+
+        final Bounds b2 = new Bounds(new Location(10, 20), new Size(8, 16));
+        assertEquals(10, b2.getX());
+        assertEquals(20, b2.getY());
+        assertEquals(8, b2.getWidth());
+        assertEquals(16, b2.getHeight());
+
+        final Bounds b3 = new Bounds(new Size(5, 10));
+        assertEquals(0, b3.getX());
+        assertEquals(0, b3.getY());
+        assertEquals(5, b3.getWidth());
+        assertEquals(10, b3.getHeight());
+    }
+
+    public void testFarPoint() {
+        assertEquals(5, b.getX());
+        assertEquals(14, b.getX2());
+        assertEquals(10, b.getY());
+        assertEquals(29, b.getY2());
+    }
+
+    public void testgrow() {
+        b.extend(10, 5);
+        assertEquals(5, b.getX());
+        assertEquals(10, b.getY());
+        assertEquals(20, b.getWidth());
+        assertEquals(25, b.getHeight());
+    }
+
+    public void testLimitBoundsWhenTooTall() {
+        final Bounds b2 = new Bounds(10, 0, 4, 30);
+        assertTrue(b.limitBounds(b2));
+        assertEquals(new Bounds(10, 10, 4, 20), b2);
+    }
+
+    public void testLimitBoundsWhenTooWide() {
+        final Bounds b2 = new Bounds(0, 12, 20, 5);
+        assertTrue(b.limitBounds(b2));
+        assertEquals(new Bounds(5, 12, 10, 5), b2);
+    }
+
+    public void testLimitBoundsWithHorizontalOverlap() {
+        final Bounds b2 = new Bounds(10, 12, 10, 5);
+        assertTrue(b.limitBounds(b2));
+        assertEquals(new Bounds(5, 12, 10, 5), b2);
+    }
+
+    public void testLimitBoundsWithNoOverlap() {
+        final Bounds b2 = new Bounds(7, 12, 5, 5);
+        assertFalse(b.limitBounds(b2));
+        assertEquals(new Bounds(7, 12, 5, 5), b2);
+    }
+
+    public void testLimitBoundsWithVerticalOverlap() {
+        final Bounds b2 = new Bounds(5, 20, 5, 20);
+        assertTrue(b.limitBounds(b2));
+        assertEquals(new Bounds(5, 10, 5, 20), b2);
+    }
+
+    public void testNonOverlappingUnion() {
+        final Bounds c = new Bounds(20, 40, 10, 20);
+        final Bounds u = new Bounds(b);
+        u.union(c);
+        assertEquals(new Bounds(5, 10, 25, 50), u);
+    }
+
+    public void testOverlappingIntersects() {
+        Bounds c = new Bounds(b);
+        c.translate(-5, -5);
+        c.extend(10, 10);
+        assertTrue(b.intersects(c));
+
+        c = new Bounds(b);
+        c.translate(5, 5);
+        c.extend(-10, -10);
+        assertTrue(b.intersects(c));
+    }
+
+    public void testOverlappingUnion() {
+        final Bounds c = new Bounds(3, 5, 10, 10);
+        final Bounds u = new Bounds(b);
+        u.union(c);
+        assertEquals(new Bounds(3, 5, 12, 25), u);
+    }
+
+    public void testTranslate() {
+        b.translate(10, 5);
+        assertEquals(15, b.getX());
+        assertEquals(15, b.getY());
+        assertEquals(10, b.getWidth());
+        assertEquals(20, b.getHeight());
+    }
+
+    public void testUpRightIntersects() {
+        final Bounds c = new Bounds(b);
+        c.translate(5, 5);
+        assertTrue(b.intersects(c));
+
+        c.translate(b.getWidth(), 0);
+        assertFalse(b.intersects(c));
+    }
+
+    public void testXNoOverlapToLeft() {
+        final Bounds c = new Bounds(1, 15, 4, 0);
+        assertFalse(b.intersects(c));
+    }
+
+    public void testXNoOverlapToRight() {
+        final Bounds c = new Bounds(15, 15, 5, 0);
+        assertFalse(b.intersects(c));
+    }
+
+    public void testXOverlapInCenter() {
+        final Bounds c = new Bounds(6, 15, 2, 0);
+        assertTrue(b.intersects(c));
+    }
+
+    public void testXOverlapToLeft() {
+        final Bounds c = new Bounds(1, 15, 6, 0);
+        assertTrue(b.intersects(c));
+    }
+
+    public void testXOverlapToRight() {
+        final Bounds c = new Bounds(14, 15, 5, 0);
+        assertTrue(b.intersects(c));
+    }
+
+    public void testYOverlapToTop() {
+        final Bounds c = new Bounds(10, 29, 0, 5);
+        assertTrue(b.intersects(c));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/DummyText.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/DummyText.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/DummyText.java
new file mode 100644
index 0000000..e41f97f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/DummyText.java
@@ -0,0 +1,85 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.drawing;
+
+import org.apache.isis.viewer.dnd.drawing.Text;
+
+public class DummyText implements Text {
+
+    public DummyText() {
+        super();
+    }
+
+    @Override
+    public int charWidth(final char c) {
+        return 10;
+    }
+
+    @Override
+    public int getAscent() {
+        return 2;
+    }
+
+    @Override
+    public int getDescent() {
+        return 4;
+    }
+
+    @Override
+    public int getMidPoint() {
+        return 1;
+    }
+
+    @Override
+    public int getTextHeight() {
+        return 8;
+    }
+
+    @Override
+    public int getLineHeight() {
+        return getAscent() + getTextHeight() + getDescent();
+    }
+
+    @Override
+    public int getLineSpacing() {
+        return getLineHeight() + 5;
+    }
+
+    @Override
+    public int stringHeight(final String text, final int width) {
+        return getLineHeight();
+    }
+
+    @Override
+    public int stringWidth(final String text) {
+        return text.length() * charWidth('x');
+    }
+
+    @Override
+    public int stringWidth(final String message, final int maxWidth) {
+        return 0;
+    }
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/LocationTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/LocationTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/LocationTest.java
new file mode 100644
index 0000000..3f16562
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/LocationTest.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.drawing;
+
+import junit.framework.TestCase;
+
+import org.apache.isis.viewer.dnd.drawing.Location;
+
+public class LocationTest extends TestCase {
+
+    public static void main(final String[] args) {
+        junit.textui.TestRunner.run(LocationTest.class);
+    }
+
+    public void testCopy() {
+        final Location l = new Location(10, 20);
+        final Location m = new Location(l);
+        assertTrue(l != m);
+        assertEquals(l, m);
+    }
+
+    public void testTranslate() {
+        final Location l = new Location(10, 20);
+        l.move(5, 10);
+        assertEquals(new Location(15, 30), l);
+        l.move(-10, -5);
+        assertEquals(new Location(5, 25), l);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/PaddingTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/PaddingTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/PaddingTest.java
new file mode 100644
index 0000000..c17a14a
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/PaddingTest.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.drawing;
+
+import junit.framework.TestCase;
+
+import org.apache.isis.viewer.dnd.drawing.Padding;
+
+public class PaddingTest extends TestCase {
+
+    private Padding p;
+
+    public static void main(final String[] args) {
+        junit.textui.TestRunner.run(PaddingTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        p = new Padding(2, 3, 4, 5);
+    }
+
+    public void testCopy() {
+        final Padding q = new Padding(p);
+        assertTrue(p != q);
+        assertEquals(p, q);
+    }
+
+    public void testValues() {
+        assertEquals(2, p.getTop());
+        assertEquals(3, p.getLeft());
+        assertEquals(4, p.getBottom());
+        assertEquals(5, p.getRight());
+    }
+
+    public void testExtend() {
+        p.extendTop(10);
+        assertEquals(new Padding(12, 3, 4, 5), p);
+
+        p.extendLeft(10);
+        assertEquals(new Padding(12, 13, 4, 5), p);
+
+        p.extendBottom(10);
+        assertEquals(new Padding(12, 13, 14, 5), p);
+
+        p.extendRight(10);
+        assertEquals(new Padding(12, 13, 14, 15), p);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest.java
new file mode 100644
index 0000000..28ea7d4
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest.java
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.drawing;
+
+import junit.framework.TestCase;
+
+import org.apache.isis.viewer.dnd.drawing.Shape;
+
+public class ShapeTest extends TestCase {
+
+    private Shape shape;
+
+    public static void main(final String[] args) {
+        junit.textui.TestRunner.run(ShapeTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        shape = new Shape();
+    }
+
+    public void testNew() {
+        assertEquals(0, shape.count());
+        assertEquals(0, shape.getX().length);
+        assertEquals(0, shape.getY().length);
+    }
+
+    public void testAddPoint() {
+        shape.addPoint(10, 12);
+        assertEquals(1, shape.count());
+        assertEquals(10, shape.getX()[0]);
+        assertEquals(12, shape.getY()[0]);
+    }
+
+    public void testAddThreePoints() {
+        shape.addPoint(10, 12);
+        shape.addPoint(8, 5);
+        shape.addPoint(0, 2);
+        assertEquals(3, shape.count());
+        assertEquals(10, shape.getX()[0]);
+        assertEquals(12, shape.getY()[0]);
+        assertEquals(8, shape.getX()[1]);
+        assertEquals(5, shape.getY()[1]);
+        assertEquals(0, shape.getX()[2]);
+        assertEquals(2, shape.getY()[2]);
+    }
+
+    public void testCreateCopy() {
+        shape.addPoint(10, 12);
+        shape.addPoint(8, 5);
+        shape.addPoint(0, 2);
+
+        final Shape copy = new Shape(shape);
+
+        assertEquals(3, copy.count());
+        assertEquals(10, copy.getX()[0]);
+        assertEquals(12, copy.getY()[0]);
+        assertEquals(8, copy.getX()[1]);
+        assertEquals(5, copy.getY()[1]);
+        assertEquals(0, copy.getX()[2]);
+        assertEquals(2, copy.getY()[2]);
+    }
+
+    public void testTransform() {
+        shape.addPoint(10, 12);
+        shape.addPoint(8, 5);
+        shape.addPoint(0, 2);
+        shape.translate(10, 20);
+        assertEquals(3, shape.count());
+        assertEquals(20, shape.getX()[0]);
+        assertEquals(32, shape.getY()[0]);
+        assertEquals(18, shape.getX()[1]);
+        assertEquals(25, shape.getY()[1]);
+        assertEquals(10, shape.getX()[2]);
+        assertEquals(22, shape.getY()[2]);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest2.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest2.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest2.java
new file mode 100644
index 0000000..3fac41e
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/ShapeTest2.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.drawing;
+
+import junit.framework.TestCase;
+
+import org.apache.isis.viewer.dnd.drawing.Shape;
+
+public class ShapeTest2 extends TestCase {
+
+    private Shape shape;
+
+    public static void main(final String[] args) {
+        junit.textui.TestRunner.run(ShapeTest2.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        shape = new Shape(10, 20);
+    }
+
+    public void testNew() {
+        assertEquals(1, shape.count());
+        assertEquals(10, shape.getX()[0]);
+        assertEquals(20, shape.getY()[0]);
+    }
+
+    public void testAddLine() {
+        shape.addVector(5, 10);
+        assertEquals(2, shape.count());
+        assertEquals(15, shape.getX()[1]);
+        assertEquals(30, shape.getY()[1]);
+    }
+
+    public void testAddTwoLines() {
+        shape.addVector(5, 10);
+        shape.addVector(-8, -5);
+        assertEquals(3, shape.count());
+        assertEquals(7, shape.getX()[2]);
+        assertEquals(25, shape.getY()[2]);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/SizeTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/SizeTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/SizeTest.java
new file mode 100644
index 0000000..5c24921
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/drawing/SizeTest.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.drawing;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+import org.apache.isis.viewer.dnd.drawing.Padding;
+import org.apache.isis.viewer.dnd.drawing.Size;
+
+public class SizeTest extends TestCase {
+
+    private Size s;
+
+    public static void main(final String[] args) {
+        junit.textui.TestRunner.run(SizeTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        Logger.getRootLogger().setLevel(Level.OFF);
+
+        s = new Size(10, 20);
+    }
+
+    public void testCopy() {
+        final Size m = new Size(s);
+        assertTrue(s != m);
+        assertEquals(s, m);
+    }
+
+    public void testEnsure() {
+        s.ensureWidth(18);
+        assertEquals(new Size(18, 20), s);
+        s.ensureWidth(12);
+        assertEquals(new Size(18, 20), s);
+
+        s.ensureHeight(16);
+        assertEquals(new Size(18, 20), s);
+        s.ensureHeight(26);
+        assertEquals(new Size(18, 26), s);
+    }
+
+    public void addPadding() {
+        s.extend(new Padding(1, 2, 3, 4));
+        assertEquals(new Size(14, 26), s);
+    }
+
+    public void testExtend() {
+        s.extendWidth(8);
+        assertEquals(new Size(18, 20), s);
+
+        s.extendHeight(6);
+        assertEquals(new Size(18, 26), s);
+
+        s.extend(new Size(3, 5));
+        assertEquals(new Size(21, 31), s);
+
+        s.extend(5, 3);
+        assertEquals(new Size(26, 34), s);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/table/TableRowLayoutTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/table/TableRowLayoutTest.java b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/table/TableRowLayoutTest.java
new file mode 100644
index 0000000..9a26b79
--- /dev/null
+++ b/component/viewer/dnd/impl/src/test/java/org/apache/isis/viewer/dnd/viewer/table/TableRowLayoutTest.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.viewer.table;
+
+import junit.framework.Assert;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2;
+import org.apache.isis.core.testsupport.jmock.JUnitRuleMockery2.Mode;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.dnd.DummyView;
+import org.apache.isis.viewer.dnd.TestToolkit;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.table.TableAxis;
+import org.apache.isis.viewer.dnd.table.TableRowLayout;
+import org.apache.isis.viewer.dnd.view.View;
+
+public class TableRowLayoutTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private IsisConfiguration mockConfiguration;
+
+    @Test
+    public void layout() throws Exception {
+        IsisContext.setConfiguration(mockConfiguration);
+        TestToolkit.createInstance();
+
+        final DummyView row = new DummyView();
+        final DummyView cell1 = new DummyView();
+        final DummyView cell2 = new DummyView();
+        row.setupSubviews(new View[] { cell1, cell2 });
+
+        final Mockery mockery = new Mockery();
+        final TableAxis tableAxis = mockery.mock(TableAxis.class);
+
+        mockery.checking(new Expectations() {
+            {
+                one(tableAxis).getColumnWidth(0);
+                will(returnValue(80));
+                one(tableAxis).getColumnWidth(1);
+                will(returnValue(80));
+            }
+        });
+
+        final TableRowLayout layout = new TableRowLayout(tableAxis);
+
+        layout.layout(row, new Size(200, 200));
+        mockery.assertIsSatisfied();
+
+        Assert.assertEquals(new Size(80, 10), cell1.getSize());
+        Assert.assertEquals(new Size(80, 10), cell2.getSize());
+
+    }
+}