Add unit tests for operations

Also convert project into OSGi bundle project to leverage availability
of junit with Eclipse platform. This eases setup of example project
without requiring to install Maven.

Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/org.eclipse.example.calc/.classpath b/org.eclipse.example.calc/.classpath
index 18d70f0..b646dd2 100644
--- a/org.eclipse.example.calc/.classpath
+++ b/org.eclipse.example.calc/.classpath
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="tst"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/org.eclipse.example.calc/.project b/org.eclipse.example.calc/.project
index 05f7dc5..3a1f808 100644
--- a/org.eclipse.example.calc/.project
+++ b/org.eclipse.example.calc/.project
@@ -10,8 +10,19 @@
 			<arguments>
 			</arguments>
 		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 	</buildSpec>
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
 	</natures>
 </projectDescription>
diff --git a/org.eclipse.example.calc/Calculator.launch b/org.eclipse.example.calc/Calculator.launch
index d3f8131..00bf1b2 100644
--- a/org.eclipse.example.calc/Calculator.launch
+++ b/org.eclipse.example.calc/Calculator.launch
@@ -7,6 +7,7 @@
 <listEntry value="1"/>
 </listAttribute>
 <mapAttribute key="org.eclipse.debug.core.preferred_launchers">
+<mapEntry key="[debug]" value="org.eclipse.jdt.launching.localJavaApplication"/>
 <mapEntry key="[run]" value="org.eclipse.jdt.launching.localJavaApplication"/>
 </mapAttribute>
 <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.example.calc.internal.ui.Calculator"/>
diff --git a/org.eclipse.example.calc/META-INF/MANIFEST.MF b/org.eclipse.example.calc/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f371248
--- /dev/null
+++ b/org.eclipse.example.calc/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Calculator
+Bundle-SymbolicName: org.eclipse.example.calc
+Bundle-Version: 0.1.0.qualifier
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.6
+Require-Bundle: org.junit4;bundle-version="[4.3.0,5.0.0)",
+ org.hamcrest;bundle-version="[1.1.0,2.0.0)"
+Export-Package: org.eclipse.example.calc;version="0.1.0",
+ org.eclipse.example.calc.internal.operations;version="0.1.0";x-internal:=true,
+ org.eclipse.example.calc.internal.ui;version="0.1.0";x-internal:=true
diff --git a/org.eclipse.example.calc/build.properties b/org.eclipse.example.calc/build.properties
new file mode 100644
index 0000000..1c56dd0
--- /dev/null
+++ b/org.eclipse.example.calc/build.properties
@@ -0,0 +1,4 @@
+source.. = src/,\
+           tst/
+bin.includes = META-INF/,\
+               .
diff --git a/org.eclipse.example.calc/org.eclipse.example.calc--All-Tests.launch b/org.eclipse.example.calc/org.eclipse.example.calc--All-Tests.launch
new file mode 100644
index 0000000..0723143
--- /dev/null
+++ b/org.eclipse.example.calc/org.eclipse.example.calc--All-Tests.launch
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<stringAttribute key="bad_container_name" value="org.eclipse.example.calc--All-Tests"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/org.eclipse.example.calc/tst"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="2"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=org.eclipse.example.calc/tst"/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.example.calc"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/>
+</launchConfiguration>
diff --git a/org.eclipse.example.calc/tst/org/eclipse/example/calc/OperationsTest.java b/org.eclipse.example.calc/tst/org/eclipse/example/calc/OperationsTest.java
new file mode 100644
index 0000000..cbb081a
--- /dev/null
+++ b/org.eclipse.example.calc/tst/org/eclipse/example/calc/OperationsTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.example.calc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.eclipse.example.calc.internal.operations.Equals;
+import org.eclipse.example.calc.internal.operations.Minus;
+import org.eclipse.example.calc.internal.operations.Plus;
+import org.eclipse.example.calc.internal.operations.Square;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OperationsTest {
+
+	private Square square;
+
+	@Before
+	public void setUp() throws Exception {
+		new Plus();
+		new Minus();
+		new Equals();
+		square = new Square();
+	}
+
+	@After
+	public void tearDown() throws Exception {
+		Operations.INSTANCE.reset();
+	}
+
+	@Test
+	public void testRegister() {
+		assertTrue(Operations.INSTANCE.getOperation(square.getName()) instanceof Square);
+		try {
+			new Square();
+			fail("double registration of same operation must fail");
+		} catch (AssertionError a) {
+			// expected
+		}
+	}
+
+	@Test
+	public void testGetOperation() {
+		assertTrue(Operations.INSTANCE.getOperation("+") instanceof Plus);
+		assertTrue(Operations.INSTANCE.getOperation("-") instanceof Minus);
+		assertTrue(Operations.INSTANCE.getOperation("=") instanceof Equals);
+		assertTrue(Operations.INSTANCE.getOperation("x²") instanceof Square);
+	}
+
+	@Test
+	public void testSize() {
+		assertEquals(4, Operations.INSTANCE.size());
+	}
+
+	@Test
+	public void testGetOperationName() {
+		for (int i = 0; i < Operations.INSTANCE.size(); i++) {
+			String name = Operations.INSTANCE.getOperationName(i);
+			Operation op = Operations.INSTANCE.getOperation(name);
+			assertEquals(name, op.getName());
+		}
+	}
+
+}
diff --git a/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/AbstractOperationTest.java b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/AbstractOperationTest.java
new file mode 100644
index 0000000..60eaa66
--- /dev/null
+++ b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/AbstractOperationTest.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.example.calc.internal.operations;
+
+import org.eclipse.example.calc.Operations;
+import org.junit.After;
+
+public abstract class AbstractOperationTest {
+
+	@After
+	public void tearDown() throws Exception {
+		Operations.INSTANCE.reset();
+	}
+}
diff --git a/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/EqualsTest.java b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/EqualsTest.java
new file mode 100644
index 0000000..9af36a2
--- /dev/null
+++ b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/EqualsTest.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.example.calc.internal.operations;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.example.calc.Operation;
+import org.junit.Before;
+import org.junit.Test;
+
+public class EqualsTest extends AbstractOperationTest {
+
+	private Operation op;
+
+	@Before
+	public void setUp() throws Exception {
+		op = new Equals();
+	}
+
+	@Test
+	public void testGetName() {
+		assertEquals(op.getName(), "=");
+	}
+
+}
diff --git a/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/MinusTest.java b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/MinusTest.java
new file mode 100644
index 0000000..9e5126f
--- /dev/null
+++ b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/MinusTest.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.example.calc.internal.operations;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.example.calc.BinaryOperation;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MinusTest extends AbstractOperationTest {
+
+	private BinaryOperation op;
+
+	@Before
+	public void setUp() throws Exception {
+		op = new Minus();
+	}
+
+	@Test
+	public void testPerform() {
+		assertEquals(-1.0, op.perform(1.0F, 2.0F), 0.01F);
+	}
+
+	@Test
+	public void testGetName() {
+		assertEquals(op.getName(), "-");
+	}
+}
diff --git a/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/PlusTest.java b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/PlusTest.java
new file mode 100644
index 0000000..4f4cd74
--- /dev/null
+++ b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/PlusTest.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.example.calc.internal.operations;
+
+import static org.junit.Assert.*;
+
+import org.eclipse.example.calc.BinaryOperation;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PlusTest extends AbstractOperationTest {
+
+	private BinaryOperation op;
+
+	@Before
+	public void setUp() throws Exception {
+		op = new Plus();
+	}
+
+	@Test
+	public void testPerform() {
+		assertEquals(3.0, op.perform(1.0F, 2.0F), 0.01F);
+	}
+
+	@Test
+	public void testGetName() {
+		assertEquals(op.getName(), "+");
+	}
+
+}
diff --git a/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/SquareTest.java b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/SquareTest.java
new file mode 100644
index 0000000..5ae112f
--- /dev/null
+++ b/org.eclipse.example.calc/tst/org/eclipse/example/calc/internal/operations/SquareTest.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.example.calc.internal.operations;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.example.calc.UnaryOperation;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SquareTest extends AbstractOperationTest {
+
+	private UnaryOperation op;
+
+	@Before
+	public void setUp() throws Exception {
+		op = new Square();
+	}
+
+	@Test
+	public void testPerform() {
+		assertEquals(4.0, op.perform(2.0F), 0.01F);
+	}
+
+	@Test
+	public void testGetName() {
+		assertEquals(op.getName(), "x²");
+	}
+}