Implemented restrictions on component connections in the Container.
[utils] / system / general / src / test / java / org / wamblee / system / core / ContainerTest.java
index 90723ed1c93c4f6271448cf19622ed0eb8166dbe..4c451337f7cbe027c03922687059e45f0ae88365 100644 (file)
@@ -17,421 +17,491 @@ package org.wamblee.system.core;
 
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import junit.framework.TestCase;
 
 import org.easymock.classextension.ConstructorArgs;
 import org.easymock.classextension.EasyMock;
 import org.easymock.classextension.IMocksControl;
+import org.wamblee.general.Pair;
 import org.wamblee.test.AssertionUtils;
 import org.wamblee.test.EasyMockMatchers;
 import org.wamblee.test.EventTracker;
 
 public class ContainerTest extends TestCase {
 
-       private EventTracker<String> _tracker;
-
-       @Override
-       protected void setUp() throws Exception {
-               super.setUp();
-               _tracker = new EventTracker<String>();
-       }
-
-       private static class MyMultiple implements Serializable, Runnable {
-               @Override
-               public void run() {
-                       // Empty
-               }
-       }
-
-       public void testFilterProvided() {
-               RequiredInterface req1 = new DefaultRequiredInterface("name",
-                               Runnable.class);
-               RequiredInterface req2 = new DefaultRequiredInterface("name",
-                               Serializable.class);
-               ProvidedInterface prov1 = new DefaultProvidedInterface("name",
-                               Runnable.class);
-               ProvidedInterface prov2 = new DefaultProvidedInterface("name",
-                               Serializable.class);
-               ProvidedInterface prov3 = new DefaultProvidedInterface("name",
-                               MyMultiple.class);
-
-               AssertionUtils.assertEquals(new RequiredInterface[] { req1 }, Container
-                               .filterRequiredServices(prov1, Arrays
-                                               .asList(new RequiredInterface[] { req1 })));
-               AssertionUtils.assertEquals(new RequiredInterface[] { req1 }, Container
-                               .filterRequiredServices(prov1, Arrays
-                                               .asList(new RequiredInterface[] { req1, req2 })));
-               AssertionUtils.assertEquals(new RequiredInterface[] { req1, req2 },
-                               Container.filterRequiredServices(prov3, Arrays
-                                               .asList(new RequiredInterface[] { req1, req2 })));
-
-               AssertionUtils.assertEquals(new ProvidedInterface[] { prov1 },
-                               Container.filterProvidedServices(req1, Arrays
-                                               .asList(new ProvidedInterface[] { prov1 })));
-               AssertionUtils.assertEquals(new ProvidedInterface[] { prov1 },
-                               Container.filterProvidedServices(req1, Arrays
-                                               .asList(new ProvidedInterface[] { prov1, prov2 })));
-               AssertionUtils.assertEquals(new ProvidedInterface[] { prov1, prov3 },
-                               Container.filterProvidedServices(req1, Arrays
-                                               .asList(new ProvidedInterface[] { prov1, prov3 })));
-       }
-
-       public void testEnvironmentApplication() {
-               Environment environment = new Environment(_tracker);
-               Application application = new Application(_tracker);
-               Container container = new Container("root", new Component[] {
-                               environment, application }, new ProvidedInterface[0],
-                               new RequiredInterface[0]);
-               Scope scope = container.start();
-               assertTrue(container.isSealed());
-               AssertionUtils.assertEquals(new String[] { "start.environment",
-                               "start.application" }, _tracker.getEvents(
-                               Thread.currentThread()).toArray(new String[0]));
-               assertEquals(0, scope.getProvidedInterfaces().length);
-               
-               assertEquals(environment.getString(), application.getString());
-               assertEquals(environment.getInteger(), application.getInteger());
-
-       }
-       
-       public void testEnvironmentApplicationSimpleConstructor() {
-               Environment environment = new Environment(_tracker);
-               Application application = new Application(_tracker);
-               Container container = new Container("root").
-                 addComponent(environment).addComponent(application);
-               
-               Scope scope = container.start();
-               AssertionUtils.assertEquals(new String[] { "start.environment",
-                               "start.application" }, _tracker.getEvents(
-                               Thread.currentThread()).toArray(new String[0]));
-               assertEquals(0, scope.getProvidedInterfaces().length);
-               
-               assertEquals(environment.getString(), application.getString());
-               assertEquals(environment.getInteger(), application.getInteger());
-
-       }
-
-       public void testApplicationEnvironment() {
-               try {
-                       Component environment = new Environment();
-                       Component application = new Application();
-                       Container container = new Container("root", new Component[] {
-                                       application, environment }, new ProvidedInterface[0],
-                                       new RequiredInterface[0]);
-                       container.start();
-               } catch (SystemAssemblyException e) {
-                       // e.printStackTrace();
-                       return;
-               }
-               fail();
-       }
-
-       public void testComposite() {
-               Component environment = new Environment(_tracker);
-               Component application = new Application(_tracker);
-               assertEquals(0, _tracker.getEventCount());
-
-               Container system = new Container("all", new Component[] { environment,
-                               application }, new ProvidedInterface[0],
-                               new RequiredInterface[0]);
-               Scope runtime = system.start();
-               RequiredInterface[] required = system.getRequiredInterfaces();
-               assertEquals(0, required.length);
-               ProvidedInterface[] provided = system.getProvidedInterfaces();
-               assertEquals(0, provided.length);
-
-               AssertionUtils.assertEquals(new String[] { "start.environment",
-                               "start.application" }, _tracker.getEvents(
-                               Thread.currentThread()).toArray(new String[0]));
-               _tracker.clear();
-
-               system.stop(runtime);
-               AssertionUtils.assertEquals(new String[] { "stop.application",
-                               "stop.environment" }, _tracker
-                               .getEvents(Thread.currentThread()).toArray(new String[0]));
-
-       }
-
-       public void testCompositeWithWrongProvidedInfo() {
-               try {
-                       Component environment = new Environment();
-                       Component application = new Application();
-                       Container system = new Container("all", new Component[] {
-                                       environment, application },
-                                       new ProvidedInterface[] { new DefaultProvidedInterface(
-                                                       "float", Float.class) },
-                                       new DefaultRequiredInterface[0]);
-                       system.validate(); 
-               } catch (SystemAssemblyException e) {
-                       return;
-               }
-               fail();
-       }
-
-       public void testCompositeRequiredInterfaceNotProvided() {
-               try {
-                       Component environment = new Environment();
-                       Component application = new Application();
-                       Container system = new Container("all", new Component[] {
-                                       environment, application }, new ProvidedInterface[0],
-                                       new RequiredInterface[] { new DefaultRequiredInterface(
-                                                       "string", String.class) });
-                       system.start();
-               } catch (SystemAssemblyException e) {
-                       return;
-               }
-               fail();
-       }
-
-       public void testCompositeWithSuperfluousRequiredInfo() {
-               Component environment = new Environment();
-               Component application = new Application();
-               Container system = new Container("all", new Component[] { environment,
-                               application }, new ProvidedInterface[0],
-                               new RequiredInterface[] { new DefaultRequiredInterface(
-                                               "float", Float.class) });
-               system.getRequiredInterfaces()[0]
-                               .setProvider(new DefaultProvidedInterface("hallo", Float.class));
-               system.start();
-               RequiredInterface[] required = system.getRequiredInterfaces();
-               assertEquals(1, required.length);
-               ProvidedInterface[] provided = system.getProvidedInterfaces();
-               assertEquals(0, provided.length);
-       }
-
-       public void testCompositeWithExternalDependencesNotProvided() {
-               try {
-                       Component environment = new Environment();
-                       Component application = new Application();
-                       Container system = new Container("all",
-                                       new Component[] { application }, new ProvidedInterface[0],
-                                       application.getRequiredInterfaces());
-                       system.start();
-               } catch (SystemAssemblyException e) {
-                       return;
-               }
-               fail();
-
-       }
-       
-       public void testDuplicateComponent() { 
-           try { 
-               Component comp1 = new Application(); 
-               Component comp2 = new Application();
-               Container system = new Container("top");
-               system.addComponent(comp1).addComponent(comp2);
-           } catch (SystemAssemblyException e) { 
-               return; 
-           }
-           fail();
-       }
-       
-
-       public void testInconsistentHierarchy() { 
-           try {
-               Component comp = new Application(); 
-               Container system = new Container("top").addComponent(comp);
-               Container system2 = new Container("top2").addComponent(comp);
-           } catch (SystemAssemblyException e) {
-               return;
-           }
-           fail();
-       }
-
-       public void testCompositeWithExternalDependencesProvided() {
-
-               Component environment = new Environment();
-               Component application = new Application();
-               Container system = new Container("all",
-                               new Component[] { application }, new ProvidedInterface[0],
-                               application.getRequiredInterfaces());
-               environment.start(new DefaultScope(new ProvidedInterface[0]));
-               system.getRequiredInterfaces()[0].setProvider(environment
-                               .getProvidedInterfaces()[0]);
-               system.getRequiredInterfaces()[1].setProvider(environment
-                               .getProvidedInterfaces()[1]);
-
-               system.start();
-               RequiredInterface[] required = system.getRequiredInterfaces();
-               assertEquals(2, required.length);
-               ProvidedInterface[] provided = system.getProvidedInterfaces();
-               assertEquals(0, provided.length);
-
-       }
-
-       public void testAmbiguousInterfaces() {
-               try {
-                       Component environment1 = new Environment();
-                       Component environment2 = new Environment();
-                       Component application = new Application();
-                       Container container = new Container("root", new Component[] {
-                                       environment1, environment2, application },
-                                       new ProvidedInterface[0], new RequiredInterface[0]);
-                       container.start();
-
-               } catch (SystemAssemblyException e) {
-                       return;
-               }
-               fail();
-       }
-
-       public void testIncompleteRequirements() {
-               try {
-                       Component application = new Application();
-                       Container system = new Container("all",
-                                       new Component[] { application }, new ProvidedInterface[0],
-                                       new RequiredInterface[0]);
-                       system.start();
-               } catch (SystemAssemblyException e) {
-                       return;
-               }
-               fail();
-       }
-
-       public void testEnvironmentApplicationRollbackOnException()
-                       throws Exception {
-               IMocksControl control = EasyMock.createStrictControl();
-
-               Environment environment = new Environment(_tracker);
-               Application application = control.createMock(Application.class,
-                               new ConstructorArgs(Application.class.getConstructor()),
-                               Application.class.getDeclaredMethod("doStart", Scope.class));
-
-               application.doStart(EasyMockMatchers.anyObject(Scope.class));
-               EasyMock.expectLastCall().andThrow(new RuntimeException());
-               control.replay();
-
-               try {
-                       Container container = new Container("root", new Component[] {
-                                       environment, application }, new ProvidedInterface[0],
-                                       new RequiredInterface[0]);
-
-                       container.start();
-               } catch (RuntimeException e) {
-                       AssertionUtils.assertEquals(new String[] { "start.environment",
-                                       "stop.environment" }, _tracker.getEvents(
-                                       Thread.currentThread()).toArray(new String[0]));
-                       return;
-               }
-               fail();
-       }
-
-       public void testEnvironmentApplicationRollbackOnExceptionWithExceptionOnStop()
-                       throws Exception {
-               IMocksControl control = EasyMock.createControl();
-
-               Environment environment = new Environment(_tracker);
-               // Application 1 will throw an exception while stopping.
-               Application application1 = control.createMock(Application.class,
-                               new ConstructorArgs(Application.class.getConstructor()),
-                               Application.class.getDeclaredMethod("doStop", Object.class));
-
-               application1.doStop(EasyMock.anyObject());
-               EasyMock.expectLastCall().andThrow(new RuntimeException());
-               
-               // application 2 will throw an exception while starting
-               Application application2 = control.createMock(Application.class,
-                               new ConstructorArgs(Application.class.getConstructor(String.class), "application2"),
-                               Application.class.getDeclaredMethod("doStart", Scope.class));
-
-               application2.doStart(EasyMockMatchers.anyObject(Scope.class));
-               EasyMock.expectLastCall().andThrow(new RuntimeException());
-               
-               control.replay();
-
-               try {
-                       Container container = new Container("root", new Component[] {
-                                       environment, application1, application2 }, new ProvidedInterface[0],
-                                       new RequiredInterface[0]);
-
-                       container.start();
-               } catch (RuntimeException e) {
-                       AssertionUtils.assertEquals(new String[] { "start.environment", 
-                                       "stop.environment" }, _tracker.getEvents(
-                                       Thread.currentThread()).toArray(new String[0]));
-                       return;
-               }
-               fail();
-       }
-       
-       public void testOptionalRequiredInterfaceProvidedOptionalInternal() {
-               Application application = new Application(true);
-               Container container = new Container("top", new Component[] { application }, 
-                               new ProvidedInterface[0], Application.required(true));
-               Environment env = new Environment();
-               container.getRequiredInterfaces()[0].setProvider(
-                               env.getProvidedInterfaces()[0]); 
-               container.getRequiredInterfaces()[1].setProvider(
-                               env.getProvidedInterfaces()[1]);
-               Scope external = new DefaultScope(env.getProvidedInterfaces());
-               env.start(external); 
-               
-           container.start(external);
-           assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
-           assertSame(env.getProvidedInterfaces()[1], container.getRequiredInterfaces()[1].getProvider());
-           assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
-           assertSame(env.getProvidedInterfaces()[1], application.getRequiredInterfaces()[1].getProvider());
-       }
-       
-       public void testOptionalRequiredInterfaceNotProvidedOptionalInternal() {
-               Application application = new Application(true);
-               Container container = new Container("top", new Component[] { application }, 
-                               new ProvidedInterface[0], Application.required(true));
-               Environment env = new Environment();
-               container.getRequiredInterfaces()[0].setProvider(
-                               env.getProvidedInterfaces()[0]);
-               Scope external = new DefaultScope(new ProvidedInterface[0]); 
-               external.publishInterface(env.getProvidedInterfaces()[0], env.getString());
-           container.start(external);
-           assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
-           assertNull(container.getRequiredInterfaces()[1].getProvider());
-           assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
-           assertNull(application.getRequiredInterfaces()[1].getProvider());
-       }
-       
-       public void testOptionalRequiredInterfaceProvidedMandatoryInternal() { 
-               Application application = new Application();
-               Container container = new Container("top", new Component[] { application }, 
-                               new ProvidedInterface[0], Application.required(true));
-               Environment env = new Environment();
-               container.getRequiredInterfaces()[0].setProvider(
-                               env.getProvidedInterfaces()[0]); 
-               container.getRequiredInterfaces()[1].setProvider(
-                               env.getProvidedInterfaces()[1]);
-           container.start();
-           assertSame(env.getProvidedInterfaces()[0], container.getRequiredInterfaces()[0].getProvider());
-           assertSame(env.getProvidedInterfaces()[1], container.getRequiredInterfaces()[1].getProvider());
-           assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider());
-           assertSame(env.getProvidedInterfaces()[1], application.getRequiredInterfaces()[1].getProvider());   
-       }
-       
-       public void testSealed() { 
-               final Container container = new Container("xx"); 
-               assertFalse(container.isSealed()); 
-               container.start();
-               assertTrue(container.isSealed());
-               
-               AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { 
-                       @Override
-                       public void run() throws Exception {
-                               container.addComponent(new Application());      
-                       }
-               }, SystemAssemblyException.class); 
-               
-               AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { 
-                       @Override
-                       public void run() throws Exception {
-                               container.addProvidedInterface(new DefaultProvidedInterface("xx", String.class));       
-                       }
-               }, SystemAssemblyException.class); 
-               
-               AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { 
-                       @Override
-                       public void run() throws Exception {
-                               container.addRequiredInterface(new DefaultRequiredInterface("xx", String.class));       
-                       }
-               }, SystemAssemblyException.class); 
-       }
+    private EventTracker<String> _tracker;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        _tracker = new EventTracker<String>();
+    }
+
+    private static class MyMultiple implements Serializable, Runnable {
+        @Override
+        public void run() {
+            // Empty
+        }
+    }
+
+    private List<Pair<ProvidedInterface, Component>> createProvidedInput(
+            ProvidedInterface[] aProvided, Component aProvider) {
+        List<Pair<ProvidedInterface, Component>> result = new ArrayList<Pair<ProvidedInterface, Component>>();
+        for (ProvidedInterface provided : aProvided) {
+            result.add(new Pair<ProvidedInterface, Component>(provided,
+                    aProvider));
+        }
+        return result;
+    }
+
+    public void testFilterProvided() {
+        RequiredInterface req1 = new DefaultRequiredInterface("name",
+                Runnable.class);
+        RequiredInterface req2 = new DefaultRequiredInterface("name",
+                Serializable.class);
+        ProvidedInterface prov1 = new DefaultProvidedInterface("name",
+                Runnable.class);
+        ProvidedInterface prov2 = new DefaultProvidedInterface("name",
+                Serializable.class);
+        ProvidedInterface prov3 = new DefaultProvidedInterface("name",
+                MyMultiple.class);
+        
+        Component client = new Application("client");
+        Component dummy = new Application("dummy");
+        
+        InterfaceRestriction noRestriction = new InterfaceRestriction() { 
+          @Override
+            public boolean isViolated(Component aClient,
+                    RequiredInterface aRequired, Component aServer,
+                    ProvidedInterface aProvided) {
+                return false;
+            }  
+        };
+
+        AssertionUtils.assertEquals(new ProvidedInterface[] { prov1 },
+                Container.filterProvidedServices(client, req1, 
+                        createProvidedInput(new ProvidedInterface[] { prov1 }, dummy), noRestriction));
+        AssertionUtils.assertEquals(new ProvidedInterface[] { prov1 },
+                Container.filterProvidedServices(client, req1, 
+                        createProvidedInput(new ProvidedInterface[] { prov1, prov2 }, dummy), noRestriction));
+        AssertionUtils.assertEquals(new ProvidedInterface[] { prov1, prov3 },
+                Container.filterProvidedServices(client, req1, 
+                        createProvidedInput(new ProvidedInterface[] { prov1, prov3 }, dummy), noRestriction));
+        
+        InterfaceRestriction everything = new InterfaceRestriction() { 
+            @Override
+            public boolean isViolated(Component aClient,
+                    RequiredInterface aRequired, Component aServer,
+                    ProvidedInterface aProvided) {
+                return true;
+            }
+        };
+        AssertionUtils.assertEquals(new ProvidedInterface[0],
+                Container.filterProvidedServices(client, req1, 
+                        createProvidedInput(new ProvidedInterface[] { prov1, prov3 }, dummy), everything));
+        
+    }
+
+    public void testEnvironmentApplication() {
+        Environment environment = new Environment(_tracker);
+        Application application = new Application(_tracker);
+        Container container = new Container("root", new Component[] {
+                environment, application }, new ProvidedInterface[0],
+                new RequiredInterface[0]);
+        Scope scope = container.start();
+        assertTrue(container.isSealed());
+        AssertionUtils.assertEquals(new String[] { "start.environment",
+                "start.application" }, _tracker.getEvents(
+                Thread.currentThread()).toArray(new String[0]));
+        assertEquals(0, scope.getProvidedInterfaces().length);
+
+        assertEquals(environment.getString(), application.getString());
+        assertEquals(environment.getInteger(), application.getInteger());
+
+    }
+
+    public void testEnvironmentApplicationSimpleConstructor() {
+        Environment environment = new Environment(_tracker);
+        Application application = new Application(_tracker);
+        Container container = new Container("root").addComponent(environment)
+                .addComponent(application);
+
+        Scope scope = container.start();
+        AssertionUtils.assertEquals(new String[] { "start.environment",
+                "start.application" }, _tracker.getEvents(
+                Thread.currentThread()).toArray(new String[0]));
+        assertEquals(0, scope.getProvidedInterfaces().length);
+
+        assertEquals(environment.getString(), application.getString());
+        assertEquals(environment.getInteger(), application.getInteger());
+
+    }
+
+    public void testApplicationEnvironment() {
+        try {
+            Component environment = new Environment();
+            Component application = new Application();
+            Container container = new Container("root", new Component[] {
+                    application, environment }, new ProvidedInterface[0],
+                    new RequiredInterface[0]);
+            container.start();
+        } catch (SystemAssemblyException e) {
+            // e.printStackTrace();
+            return;
+        }
+        fail();
+    }
+
+    public void testComposite() {
+        Component environment = new Environment(_tracker);
+        Component application = new Application(_tracker);
+        assertEquals(0, _tracker.getEventCount());
+
+        Container system = new Container("all", new Component[] { environment,
+                application }, new ProvidedInterface[0],
+                new RequiredInterface[0]);
+        Scope runtime = system.start();
+        RequiredInterface[] required = system.getRequiredInterfaces();
+        assertEquals(0, required.length);
+        ProvidedInterface[] provided = system.getProvidedInterfaces();
+        assertEquals(0, provided.length);
+
+        AssertionUtils.assertEquals(new String[] { "start.environment",
+                "start.application" }, _tracker.getEvents(
+                Thread.currentThread()).toArray(new String[0]));
+        _tracker.clear();
+
+        system.stop(runtime);
+        AssertionUtils.assertEquals(new String[] { "stop.application",
+                "stop.environment" }, _tracker
+                .getEvents(Thread.currentThread()).toArray(new String[0]));
+
+    }
+
+    public void testCompositeWithWrongProvidedInfo() {
+        try {
+            Component environment = new Environment();
+            Component application = new Application();
+            Container system = new Container("all", new Component[] {
+                    environment, application },
+                    new ProvidedInterface[] { new DefaultProvidedInterface(
+                            "float", Float.class) },
+                    new DefaultRequiredInterface[0]);
+            system.validate();
+        } catch (SystemAssemblyException e) {
+            return;
+        }
+        fail();
+    }
+
+    public void testCompositeRequiredInterfaceNotProvided() {
+        try {
+            Component environment = new Environment();
+            Component application = new Application();
+            Container system = new Container("all", new Component[] {
+                    environment, application }, new ProvidedInterface[0],
+                    new RequiredInterface[] { new DefaultRequiredInterface(
+                            "string", String.class) });
+            system.start();
+        } catch (SystemAssemblyException e) {
+            return;
+        }
+        fail();
+    }
+
+    public void testCompositeWithSuperfluousRequiredInfo() {
+        Component environment = new Environment();
+        Component application = new Application();
+        Container system = new Container("all", new Component[] { environment,
+                application }, new ProvidedInterface[0],
+                new RequiredInterface[] { new DefaultRequiredInterface("float",
+                        Float.class) });
+        system.getRequiredInterfaces()[0]
+                .setProvider(new DefaultProvidedInterface("hallo", Float.class));
+        system.start();
+        RequiredInterface[] required = system.getRequiredInterfaces();
+        assertEquals(1, required.length);
+        ProvidedInterface[] provided = system.getProvidedInterfaces();
+        assertEquals(0, provided.length);
+    }
+
+    public void testCompositeWithExternalDependencesNotProvided() {
+        try {
+            Component environment = new Environment();
+            Component application = new Application();
+            Container system = new Container("all",
+                    new Component[] { application }, new ProvidedInterface[0],
+                    application.getRequiredInterfaces());
+            system.start();
+        } catch (SystemAssemblyException e) {
+            return;
+        }
+        fail();
+
+    }
+
+    public void testDuplicateComponent() {
+        try {
+            Component comp1 = new Application();
+            Component comp2 = new Application();
+            Container system = new Container("top");
+            system.addComponent(comp1).addComponent(comp2);
+        } catch (SystemAssemblyException e) {
+            return;
+        }
+        fail();
+    }
+
+    public void testInconsistentHierarchy() {
+        try {
+            Component comp = new Application();
+            Container system = new Container("top").addComponent(comp);
+            Container system2 = new Container("top2").addComponent(comp);
+        } catch (SystemAssemblyException e) {
+            return;
+        }
+        fail();
+    }
+
+    public void testCompositeWithExternalDependencesProvided() {
+
+        Component environment = new Environment();
+        Component application = new Application();
+        Container system = new Container("all",
+                new Component[] { application }, new ProvidedInterface[0],
+                application.getRequiredInterfaces());
+        environment.start(new DefaultScope(new ProvidedInterface[0]));
+        system.getRequiredInterfaces()[0].setProvider(environment
+                .getProvidedInterfaces()[0]);
+        system.getRequiredInterfaces()[1].setProvider(environment
+                .getProvidedInterfaces()[1]);
+
+        system.start();
+        RequiredInterface[] required = system.getRequiredInterfaces();
+        assertEquals(2, required.length);
+        ProvidedInterface[] provided = system.getProvidedInterfaces();
+        assertEquals(0, provided.length);
+
+    }
+
+    public void testAmbiguousInterfaces() {
+        try {
+            Component environment1 = new Environment();
+            Component environment2 = new Environment();
+            Component application = new Application();
+            Container container = new Container("root", new Component[] {
+                    environment1, environment2, application },
+                    new ProvidedInterface[0], new RequiredInterface[0]);
+            container.start();
+
+        } catch (SystemAssemblyException e) {
+            return;
+        }
+        fail();
+    }
+
+    public void testIncompleteRequirements() {
+        try {
+            Component application = new Application();
+            Container system = new Container("all",
+                    new Component[] { application }, new ProvidedInterface[0],
+                    new RequiredInterface[0]);
+            system.start();
+        } catch (SystemAssemblyException e) {
+            return;
+        }
+        fail();
+    }
+
+    public void testEnvironmentApplicationRollbackOnException()
+            throws Exception {
+        IMocksControl control = EasyMock.createStrictControl();
+
+        Environment environment = new Environment(_tracker);
+        Application application = control.createMock(Application.class,
+                new ConstructorArgs(Application.class.getConstructor()),
+                Application.class.getDeclaredMethod("doStart", Scope.class));
+
+        application.doStart(EasyMockMatchers.anyObject(Scope.class));
+        EasyMock.expectLastCall().andThrow(new RuntimeException());
+        control.replay();
+
+        try {
+            Container container = new Container("root", new Component[] {
+                    environment, application }, new ProvidedInterface[0],
+                    new RequiredInterface[0]);
+
+            container.start();
+        } catch (RuntimeException e) {
+            AssertionUtils.assertEquals(new String[] { "start.environment",
+                    "stop.environment" }, _tracker.getEvents(
+                    Thread.currentThread()).toArray(new String[0]));
+            return;
+        }
+        fail();
+    }
+
+    public void testEnvironmentApplicationRollbackOnExceptionWithExceptionOnStop()
+            throws Exception {
+        IMocksControl control = EasyMock.createControl();
+
+        Environment environment = new Environment(_tracker);
+        // Application 1 will throw an exception while stopping.
+        Application application1 = control.createMock(Application.class,
+                new ConstructorArgs(Application.class.getConstructor()),
+                Application.class.getDeclaredMethod("doStop", Object.class));
+
+        application1.doStop(EasyMock.anyObject());
+        EasyMock.expectLastCall().andThrow(new RuntimeException());
+
+        // application 2 will throw an exception while starting
+        Application application2 = control.createMock(Application.class,
+                new ConstructorArgs(Application.class
+                        .getConstructor(String.class), "application2"),
+                Application.class.getDeclaredMethod("doStart", Scope.class));
+
+        application2.doStart(EasyMockMatchers.anyObject(Scope.class));
+        EasyMock.expectLastCall().andThrow(new RuntimeException());
+
+        control.replay();
+
+        try {
+            Container container = new Container("root", new Component[] {
+                    environment, application1, application2 },
+                    new ProvidedInterface[0], new RequiredInterface[0]);
+
+            container.start();
+        } catch (RuntimeException e) {
+            AssertionUtils.assertEquals(new String[] { "start.environment",
+                    "stop.environment" }, _tracker.getEvents(
+                    Thread.currentThread()).toArray(new String[0]));
+            return;
+        }
+        fail();
+    }
+
+    public void testOptionalRequiredInterfaceProvidedOptionalInternal() {
+        Application application = new Application(true);
+        Container container = new Container("top",
+                new Component[] { application }, new ProvidedInterface[0],
+                Application.required(true));
+        Environment env = new Environment();
+        container.getRequiredInterfaces()[0].setProvider(env
+                .getProvidedInterfaces()[0]);
+        container.getRequiredInterfaces()[1].setProvider(env
+                .getProvidedInterfaces()[1]);
+        Scope external = new DefaultScope(env.getProvidedInterfaces());
+        env.start(external);
+
+        container.start(external);
+        assertSame(env.getProvidedInterfaces()[0], container
+                .getRequiredInterfaces()[0].getProvider());
+        assertSame(env.getProvidedInterfaces()[1], container
+                .getRequiredInterfaces()[1].getProvider());
+        assertSame(env.getProvidedInterfaces()[0], application
+                .getRequiredInterfaces()[0].getProvider());
+        assertSame(env.getProvidedInterfaces()[1], application
+                .getRequiredInterfaces()[1].getProvider());
+    }
+
+    public void testOptionalRequiredInterfaceNotProvidedOptionalInternal() {
+        Application application = new Application(true);
+        Container container = new Container("top",
+                new Component[] { application }, new ProvidedInterface[0],
+                Application.required(true));
+        Environment env = new Environment();
+        container.getRequiredInterfaces()[0].setProvider(env
+                .getProvidedInterfaces()[0]);
+        Scope external = new DefaultScope(new ProvidedInterface[0]);
+        external.publishInterface(env.getProvidedInterfaces()[0], env
+                .getString());
+        container.start(external);
+        assertSame(env.getProvidedInterfaces()[0], container
+                .getRequiredInterfaces()[0].getProvider());
+        assertNull(container.getRequiredInterfaces()[1].getProvider());
+        assertSame(env.getProvidedInterfaces()[0], application
+                .getRequiredInterfaces()[0].getProvider());
+        assertNull(application.getRequiredInterfaces()[1].getProvider());
+    }
+
+    public void testOptionalRequiredInterfaceProvidedMandatoryInternal() {
+        Application application = new Application();
+        Container container = new Container("top",
+                new Component[] { application }, new ProvidedInterface[0],
+                Application.required(true));
+        Environment env = new Environment();
+        container.getRequiredInterfaces()[0].setProvider(env
+                .getProvidedInterfaces()[0]);
+        container.getRequiredInterfaces()[1].setProvider(env
+                .getProvidedInterfaces()[1]);
+        container.start();
+        assertSame(env.getProvidedInterfaces()[0], container
+                .getRequiredInterfaces()[0].getProvider());
+        assertSame(env.getProvidedInterfaces()[1], container
+                .getRequiredInterfaces()[1].getProvider());
+        assertSame(env.getProvidedInterfaces()[0], application
+                .getRequiredInterfaces()[0].getProvider());
+        assertSame(env.getProvidedInterfaces()[1], application
+                .getRequiredInterfaces()[1].getProvider());
+    }
+
+    public void testSealed() {
+        final Container container = new Container("xx");
+        assertFalse(container.isSealed());
+        container.start();
+        assertTrue(container.isSealed());
+
+        AssertionUtils.assertException(new AssertionUtils.ErroneousCode() {
+            @Override
+            public void run() throws Exception {
+                container.addComponent(new Application());
+            }
+        }, SystemAssemblyException.class);
+
+        AssertionUtils.assertException(new AssertionUtils.ErroneousCode() {
+            @Override
+            public void run() throws Exception {
+                container.addRestriction(new InterfaceRestriction() {
+                    @Override
+                    public boolean isViolated(Component aClient,
+                            RequiredInterface aRequired, Component aServer,
+                            ProvidedInterface aProvided) {
+                        return false;
+                    }
+                });
+            }
+        }, SystemAssemblyException.class);
+        AssertionUtils.assertException(new AssertionUtils.ErroneousCode() {
+            @Override
+            public void run() throws Exception {
+                container.addProvidedInterface(new DefaultProvidedInterface(
+                        "xx", String.class));
+            }
+        }, SystemAssemblyException.class);
+
+        AssertionUtils.assertException(new AssertionUtils.ErroneousCode() {
+            @Override
+            public void run() throws Exception {
+                container.addRequiredInterface(new DefaultRequiredInterface(
+                        "xx", String.class));
+            }
+        }, SystemAssemblyException.class);
+    }
+
+    public void testRestriction() {
+        Environment env1 = new Environment("env1");
+        Environment env2 = new Environment("env2");
+        Application app = new Application("app");
+        Container container = new Container("top").addComponent(env1)
+                .addComponent(env2).addComponent(app);
+        container.addRestriction(new DefaultInterfaceRestriction("app", null,
+                "env1", null));
+        container.start();
+        assertEquals(env1.getString(), app.getString());
+        assertFalse(env2.getString().equals(app.getString()));
+    }
 }