brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hadr...@apache.org
Subject [24/54] incubator-brooklyn git commit: [BROOKLYN-162] Renaming package brooklyn.location
Date Fri, 14 Aug 2015 03:42:53 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
new file mode 100644
index 0000000..d689512
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
@@ -0,0 +1,575 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.fail;
+
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.location.MachineLocation;
+import org.apache.brooklyn.location.NoMachinesAvailableException;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Networking;
+import brooklyn.util.stream.Streams;
+
+/**
+ * Provisions {@link SshMachineLocation}s in a specific location from a list of known machines
+ */
+public class FixedListMachineProvisioningLocationTest {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(FixedListMachineProvisioningLocationTest.class);
+
+    SshMachineLocation machine;
+    LocalManagementContext mgmt;
+    FixedListMachineProvisioningLocation<SshMachineLocation> provisioner;
+    FixedListMachineProvisioningLocation<SshMachineLocation> provisioner2;
+    
+    @SuppressWarnings("unchecked")
+    @BeforeMethod(alwaysRun=true)
+    public void createProvisioner() throws UnknownHostException {
+        mgmt = LocalManagementContextForTests.newInstance();
+        
+        machine = mgmt.getLocationManager().createLocation(MutableMap.of("address", Inet4Address.getByName("192.168.144.200")), SshMachineLocation.class);
+        provisioner = mgmt.getLocationManager().createLocation(
+                MutableMap.of("machines", MutableList.of(machine)),
+                FixedListMachineProvisioningLocation.class);
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (provisioner != null) Streams.closeQuietly(provisioner);
+        if (provisioner2 != null) Streams.closeQuietly(provisioner2);
+        Entities.destroyAll(mgmt);
+    }
+    
+    @Test
+    public void testSetsChildLocations() throws NoMachinesAvailableException {
+        // Available machines should be listed as children
+        assertEquals(ImmutableList.copyOf(provisioner.getChildren()), ImmutableList.of(machine));
+        
+        // In-use machines should also be listed as children
+        provisioner.obtain();
+        assertEquals(ImmutableList.copyOf(provisioner.getChildren()), ImmutableList.of(machine));
+    }
+
+    @Test
+    public void canObtainMachine() throws NoMachinesAvailableException {
+        SshMachineLocation obtained = provisioner.obtain();
+        assertEquals(obtained, machine);
+    }
+
+    @SuppressWarnings("unused")
+    @Test(expectedExceptions = { NoMachinesAvailableException.class })
+    public void throwsExceptionIfNoMachinesAvailable() throws NoMachinesAvailableException {
+        SshMachineLocation machine1 = provisioner.obtain();
+        SshMachineLocation machine2 = provisioner.obtain();
+        fail("Did not throw NoMachinesAvailableException as expected");
+    }
+
+    @Test
+    public void canGetAMachineReturnItAndObtainItAgain() throws NoMachinesAvailableException {
+        SshMachineLocation obtained = provisioner.obtain();
+        provisioner.release(obtained);
+        SshMachineLocation obtained2 = provisioner.obtain();
+        assertEquals(obtained2, machine);
+    }
+
+    @Test
+    public void theBuilder() throws NoMachinesAvailableException {
+        provisioner2 =
+            new FixedListMachineProvisioningLocation.Builder(mgmt.getLocationManager()).
+                user("u1").
+                addAddress("192.168.0.1").
+                addAddress("u2@192.168.0.2").
+                addAddress("192.168.0.{3,4}").
+                addAddresses("192.168.0.{6-8}").
+                addAddressMultipleTimes("192.168.0.{8,7}", 2).
+                addAddress("u3@192.168.0.{11-20}").
+                build();
+        assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0.1");
+        assertUserAndHost(provisioner2.obtain(), "u2", "192.168.0.2");
+        for (int i=3; i<=4; i++) assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0."+i);
+        for (int i=6; i<=8; i++) assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0."+i);
+        for (int j=0; j<2; j++)
+            for (int i=8; i>=7; i--) assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0."+i);
+        for (int i=11; i<=20; i++) assertUserAndHost(provisioner2.obtain(), "u3", "192.168.0."+i);
+        try { 
+            provisioner2.obtain();
+            fail("Should not have obtained");  //throws error so not caught below
+        } catch (Exception e) {
+            /** expected */
+        }
+    }
+    
+    @Test
+    public void theBuilderLegacy() throws NoMachinesAvailableException {
+        provisioner2 =
+            new FixedListMachineProvisioningLocation.Builder(mgmt.getLocationManager()).
+                user("u1").
+                addAddress("192.168.0.1").
+                addAddress("u2@192.168.0.2").
+                addAddress("192.168.0.{3,4}").
+                addAddresses("192.168.0.{6-8}").
+                addAddressMultipleTimes("192.168.0.{8,7}", 2).
+                addAddress("u3@192.168.0.{11-20}").
+                build();
+        assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0.1");
+        assertUserAndHost(provisioner2.obtain(), "u2", "192.168.0.2");
+        for (int i=3; i<=4; i++) assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0."+i);
+        for (int i=6; i<=8; i++) assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0."+i);
+        for (int j=0; j<2; j++)
+            for (int i=8; i>=7; i--) assertUserAndHost(provisioner2.obtain(), "u1", "192.168.0."+i);
+        for (int i=11; i<=20; i++) assertUserAndHost(provisioner2.obtain(), "u3", "192.168.0."+i);
+        try { 
+            provisioner2.obtain();
+            fail("Should not have obtained");  //throws error so not caught below
+        } catch (Exception e) {
+            /** expected */
+        }
+    }
+    
+    @Test(expectedExceptions = { IllegalStateException.class })
+    public void throwsExceptionIfTryingToReleaseUnallocationMachine() throws NoMachinesAvailableException, UnknownHostException {
+        @SuppressWarnings("unused")
+        SshMachineLocation obtained = provisioner.obtain();
+        provisioner.release(new SshMachineLocation(MutableMap.of("address", Inet4Address.getByName("192.168.144.201"))));
+        fail("Did not throw IllegalStateException as expected");
+    }
+    
+    @Test
+    public void testCanAddMachineToPool() throws UnknownHostException, NoMachinesAvailableException {
+        SshMachineLocation machine2 = new SshMachineLocation(
+                MutableMap.of("address", Inet4Address.getByName("192.168.144.200")));
+        provisioner2 = new FixedListMachineProvisioningLocation<SshMachineLocation>(
+                MutableMap.of("machines", MutableList.of()));
+        provisioner2.addMachine(machine2);
+        
+        assertEquals(ImmutableList.copyOf(provisioner2.getChildren()), ImmutableList.of(machine2));
+        assertEquals(ImmutableSet.copyOf(provisioner2.getAvailable()), ImmutableSet.of(machine2));
+        
+        SshMachineLocation obtained = provisioner2.obtain();
+        assertEquals(obtained, machine2);
+        
+        // Can only obtain the added machien once though (i.e. not added multiple times somehow)
+        try {
+            SshMachineLocation obtained2 = provisioner2.obtain();
+            fail("obtained="+obtained2);
+        } catch (NoMachinesAvailableException e) {
+            // success
+        }
+    }
+
+    @Test
+    public void testCanRemoveAvailableMachineFromPool() {
+        provisioner.removeMachine(machine);
+        
+        Assert.assertTrue(provisioner.getChildren().isEmpty());
+        Assert.assertTrue(provisioner.getAvailable().isEmpty());
+        
+        try {
+            SshMachineLocation obtained = provisioner.obtain();
+            fail("obtained="+obtained);
+        } catch (NoMachinesAvailableException e) {
+            // success
+        }
+    }
+
+    @Test
+    public void testCanRemoveObtainedMachineFromPoolSoNotReallocated() throws NoMachinesAvailableException {
+        SshMachineLocation obtained = provisioner.obtain();
+        provisioner.removeMachine(obtained);
+        
+        // Continue to know about the machine until it is returned
+        assertEquals(ImmutableList.copyOf(provisioner.getChildren()), ImmutableList.of(machine));
+        Assert.assertTrue(provisioner.getAvailable().isEmpty());
+
+        // When released, the machine is then removed entirely
+        provisioner.release(obtained);
+
+        Assert.assertTrue(provisioner.getChildren().isEmpty());
+        Assert.assertTrue(provisioner.getAvailable().isEmpty());
+
+        // So no machines left; cannot re-obtain
+        try {
+            SshMachineLocation obtained2 = provisioner.obtain();
+            fail("obtained="+obtained2);
+        } catch (NoMachinesAvailableException e) {
+            // success
+        }
+    }
+
+    @Test
+    public void testObtainDesiredMachineThrowsIfNotKnown() throws Exception {
+        SshMachineLocation machine2 = new SshMachineLocation(
+                MutableMap.of("address", Inet4Address.getByName("192.168.144.201")));
+        try {
+            SshMachineLocation obtained = provisioner.obtain(MutableMap.of("desiredMachine", machine2));
+            fail("obtained="+obtained);
+        } catch (IllegalStateException e) {
+            if (!e.toString().contains("machine unknown")) throw e;
+        }
+    }
+
+    @Test
+    public void testObtainDesiredMachineThrowsIfInUse() throws Exception {
+        provisioner.addMachine(new SshMachineLocation(
+                MutableMap.of("address", Inet4Address.getByName("192.168.144.201"))));
+        SshMachineLocation obtained = provisioner.obtain();
+        try {
+            SshMachineLocation obtained2 = provisioner.obtain(MutableMap.of("desiredMachine", obtained));
+            fail("obtained2="+obtained2);
+        } catch (IllegalStateException e) {
+            if (!e.toString().contains("machine in use")) throw e;
+        }
+    }
+
+    @Test
+    public void testObtainDesiredMachineReturnsDesired() throws Exception {
+        int desiredMachineIndex = 10;
+        SshMachineLocation desiredMachine = null;
+        for (int i = 0; i < 20; i++) {
+            SshMachineLocation newMachine = new SshMachineLocation(
+                    MutableMap.of("address", Inet4Address.getByName("192.168.144."+(201+i))));
+            if (i == desiredMachineIndex) desiredMachine = newMachine;
+            provisioner.addMachine(newMachine);
+        }
+        SshMachineLocation obtained = provisioner.obtain(MutableMap.of("desiredMachine", desiredMachine));
+        assertEquals(obtained, desiredMachine);
+    }
+
+    @Test
+    public void testAddAndRemoveChildUpdatesMachinesSet() throws Exception {
+        SshMachineLocation anotherMachine = new SshMachineLocation(
+                MutableMap.of("address", Inet4Address.getByName("192.168.144.201")));
+        provisioner.addChild(anotherMachine);
+        assertEquals(provisioner.getAllMachines(), ImmutableSet.of(machine, anotherMachine));
+        
+        provisioner.removeChild(anotherMachine);
+        assertEquals(provisioner.getAllMachines(), ImmutableSet.of(machine));
+    }
+    
+    @Test
+    public void testCanAddAlreadyParentedMachine() throws UnknownHostException, NoMachinesAvailableException {
+        provisioner.obtain(); // so no machines left
+        
+        FixedListMachineProvisioningLocation<SshMachineLocation> provisioner2 = new FixedListMachineProvisioningLocation.Builder(mgmt.getLocationManager())
+            .addAddress("1.2.3.4")
+            .build();
+        SshMachineLocation machine = provisioner2.obtain();
+        
+        provisioner.addMachine(machine);
+        assertEquals(provisioner.obtain(), machine);
+    }
+
+    @Test
+    public void testCanCreateWithAlreadyParentedMachine() throws UnknownHostException, NoMachinesAvailableException {
+        machine = provisioner.obtain();
+        
+        FixedListMachineProvisioningLocation<SshMachineLocation> provisioner2 = new FixedListMachineProvisioningLocation.Builder(mgmt.getLocationManager())
+            .add(machine)
+            .build();
+        assertEquals(provisioner2.obtain(), machine);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMachinesObtainedInOrder() throws Exception {
+        List<SshMachineLocation> machines = ImmutableList.of(
+                mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.1"))),
+                mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.6"))),
+                mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.3"))),
+                mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.4"))),
+                mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.5"))));
+        
+        provisioner2 = mgmt.getLocationManager().createLocation(
+                MutableMap.of("machines", machines),
+                FixedListMachineProvisioningLocation.class);
+
+        for (SshMachineLocation expected : machines) {
+            assertEquals(provisioner2.obtain(), expected);
+        }
+    }
+    
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMachineChooser() throws Exception {
+        List<SshMachineLocation> machines = Lists.newArrayList();
+        for (int i = 0; i < 10; i++) {
+            machines.add(mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1."+i))));
+        }
+        final List<SshMachineLocation> desiredOrder = randomized(machines);
+        
+        Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = new Function<Iterable<? extends MachineLocation>, MachineLocation>() {
+            @Override public MachineLocation apply(Iterable<? extends MachineLocation> input) {
+                for (SshMachineLocation contender : desiredOrder) {
+                    if (Iterables.contains(input, contender)) {
+                        return contender;
+                    }
+                }
+                Assert.fail("No intersection of input="+input+" and desiredOrder="+desiredOrder);
+                return null; // unreachable code
+            }
+        };
+        provisioner2 = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", machines)
+                .configure(FixedListMachineProvisioningLocation.MACHINE_CHOOSER, chooser));
+
+        List<SshMachineLocation> result = Lists.newArrayList();
+        for (int i = 0; i < machines.size(); i++) {
+            result.add(provisioner2.obtain());
+        }
+        assertEquals(result, desiredOrder, "result="+result+"; desired="+desiredOrder);
+        LOG.debug("chooser's desiredOrder="+desiredOrder);
+    }
+    
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMachineChooserPassedToObtain() throws Exception {
+        List<SshMachineLocation> machines = Lists.newArrayList();
+        for (int i = 0; i < 10; i++) {
+            machines.add(mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1."+i))));
+        }
+        final List<SshMachineLocation> desiredOrder = randomized(machines);
+        
+        Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = new Function<Iterable<? extends MachineLocation>, MachineLocation>() {
+            @Override public MachineLocation apply(Iterable<? extends MachineLocation> input) {
+                for (SshMachineLocation contender : desiredOrder) {
+                    if (Iterables.contains(input, contender)) {
+                        return contender;
+                    }
+                }
+                Assert.fail("No intersection of input="+input+" and desiredOrder="+desiredOrder);
+                return null; // unreachable code
+            }
+        };
+        provisioner2 = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", machines));
+
+        List<SshMachineLocation> result = Lists.newArrayList();
+        for (int i = 0; i < machines.size(); i++) {
+            result.add(provisioner2.obtain(ImmutableMap.of(FixedListMachineProvisioningLocation.MACHINE_CHOOSER, chooser)));
+        }
+        assertEquals(result, desiredOrder, "result="+result+"; desired="+desiredOrder);
+        LOG.debug("chooser's desiredOrder="+desiredOrder);
+    }
+    
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMachineChooserNotCalledWhenNoMachines() throws Exception {
+        List<SshMachineLocation> machines = ImmutableList.of(
+                mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.1"))));
+        final AtomicInteger callCount = new AtomicInteger();
+        
+        Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = new Function<Iterable<? extends MachineLocation>, MachineLocation>() {
+            @Override public MachineLocation apply(Iterable<? extends MachineLocation> input) {
+                callCount.incrementAndGet();
+                return Iterables.get(input, 0);
+            }
+        };
+        provisioner2 = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", machines)
+                .configure(FixedListMachineProvisioningLocation.MACHINE_CHOOSER, chooser));
+        provisioner2.obtain();
+
+        // When no machines available should fail gracefully, without asking the "chooser"
+        try {
+            provisioner2.obtain();
+            fail("Expected "+NoMachinesAvailableException.class.getSimpleName());
+        } catch (NoMachinesAvailableException e) {
+            // Pass; sensible exception
+        }
+        assertEquals(callCount.get(), 1);
+    }
+    
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testFailsWhenMachineChooserReturnsAlreadyAllocatedMachine() throws Exception {
+        final SshMachineLocation machine1 = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.1")));
+        final SshMachineLocation machine2 = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.2")));
+        List<SshMachineLocation> machines = ImmutableList.of(machine1, machine2);
+        
+        Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = new Function<Iterable<? extends MachineLocation>, MachineLocation>() {
+            @Override public MachineLocation apply(Iterable<? extends MachineLocation> input) {
+                return machine1;
+            }
+        };
+        provisioner2 = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", machines)
+                .configure(FixedListMachineProvisioningLocation.MACHINE_CHOOSER, chooser));
+        provisioner2.obtain();
+
+        // Should fail when tries to return same machine for a second time
+        try {
+            provisioner2.obtain();
+            fail("Expected "+IllegalStateException.class.getSimpleName());
+        } catch (IllegalStateException e) {
+            if (!e.toString().contains("Machine chooser attempted to choose ")) throw e;
+        }
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testFailsWhenMachineChooserReturnsInvalidMachine() throws Exception {
+        final SshMachineLocation machine1 = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("1.1.1.1")));
+        final SshMachineLocation machineOther = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class).configure("address", Networking.getInetAddressWithFixedName("2.2.2.1")));
+        List<SshMachineLocation> machines = ImmutableList.of(machine1);
+        
+        Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = new Function<Iterable<? extends MachineLocation>, MachineLocation>() {
+            @Override public MachineLocation apply(Iterable<? extends MachineLocation> input) {
+                return machineOther;
+            }
+        };
+        provisioner2 = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", machines)
+                .configure(FixedListMachineProvisioningLocation.MACHINE_CHOOSER, chooser));
+
+        // Call when no machines available should fail gracefully, without asking the "chooser"
+        try {
+            provisioner2.obtain();
+            fail("Expected "+IllegalStateException.class.getSimpleName());
+        } catch (IllegalStateException e) {
+            if (!e.toString().contains("Machine chooser attempted to choose ")) throw e;
+        }
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMachineCustomizerSetOnByon() throws Exception {
+        machine = mgmt.getLocationManager().createLocation(MutableMap.of("address", Inet4Address.getByName("192.168.144.200")), SshMachineLocation.class);
+        RecordingMachineLocationCustomizer customizer = new RecordingMachineLocationCustomizer();
+        
+        provisioner2 = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", ImmutableList.of(machine))
+                .configure(FixedListMachineProvisioningLocation.MACHINE_LOCATION_CUSTOMIZERS.getName(), ImmutableList.of(customizer)));
+                
+        SshMachineLocation obtained = provisioner2.obtain();
+        Assert.assertEquals(Iterables.getOnlyElement(customizer.calls), new RecordingMachineLocationCustomizer.Call("customize", ImmutableList.of(obtained)));
+        
+        provisioner2.release(obtained);
+        assertEquals(customizer.calls.size(), 2);
+        Assert.assertEquals(Iterables.get(customizer.calls, 1), new RecordingMachineLocationCustomizer.Call("preRelease", ImmutableList.of(obtained)));
+    }
+
+    @Test
+    public void testMachineCustomizerSetOnObtainCall() throws Exception {
+        RecordingMachineLocationCustomizer customizer = new RecordingMachineLocationCustomizer();
+        
+        SshMachineLocation obtained = provisioner.obtain(ImmutableMap.of(FixedListMachineProvisioningLocation.MACHINE_LOCATION_CUSTOMIZERS, ImmutableList.of(customizer)));
+        Assert.assertEquals(Iterables.getOnlyElement(customizer.calls), new RecordingMachineLocationCustomizer.Call("customize", ImmutableList.of(obtained)));
+        
+        provisioner.release(obtained);
+        assertEquals(customizer.calls.size(), 2);
+        Assert.assertEquals(customizer.calls.get(1), new RecordingMachineLocationCustomizer.Call("preRelease", ImmutableList.of(obtained)));
+    }
+
+    @Test
+    public void testMachineGivenCustomFlagForDurationOfUsage() throws Exception {
+        boolean origContains = machine.config().getBag().getAllConfig().containsKey("mykey");
+        SshMachineLocation obtained = provisioner.obtain(ImmutableMap.of("mykey", "myNewVal"));
+        Object obtainedVal = obtained.config().getBag().getAllConfig().get("mykey");
+        provisioner.release(obtained);
+        boolean releasedContains = obtained.config().getBag().getAllConfig().containsKey("mykey");
+        
+        assertEquals(obtained, machine);
+        assertFalse(origContains);
+        assertEquals(obtainedVal, "myNewVal");
+        assertFalse(releasedContains);
+    }
+    
+    @Test
+    public void testMachineConfigRestoredToDefaultsOnRelease() throws Exception {
+        ConfigKey<String> mykey = ConfigKeys.newStringConfigKey("mykey");
+        
+        boolean origContains = machine.config().getBag().getAllConfig().containsKey("mykey");
+        SshMachineLocation obtained = provisioner.obtain();
+        obtained.config().set(mykey, "myNewVal");
+        Object obtainedVal = obtained.config().getBag().getAllConfig().get("mykey");
+        
+        provisioner.release(obtained);
+        boolean releasedContains = machine.config().getBag().getAllConfig().containsKey("mykey");
+        releasedContains |= (machine.config().get(mykey) != null);
+        
+        assertEquals(obtained, machine);
+        assertFalse(origContains);
+        assertEquals(obtainedVal, "myNewVal");
+        assertFalse(releasedContains);
+    }
+    
+    @Test
+    public void testMachineGivenOverriddenFlagForDurationOfUsage() throws Exception {
+        SshMachineLocation machine2 = new SshMachineLocation(
+                MutableMap.of("address", Inet4Address.getByName("192.168.144.200"), "mykey", "myval"));
+        provisioner2 = new FixedListMachineProvisioningLocation<SshMachineLocation>(
+                MutableMap.of("machines", MutableList.of(machine2)));
+
+        Object origVal = machine2.config().getBag().getAllConfig().get("mykey");
+        SshMachineLocation obtained = provisioner2.obtain(ImmutableMap.of("mykey", "myNewVal"));
+        Object obtainedVal = obtained.config().getBag().getAllConfig().get("mykey");
+        provisioner2.release(obtained);
+        Object releasedVal = obtained.config().getBag().getAllConfig().get("mykey");
+        
+        assertEquals(obtained, machine2);
+        assertEquals(origVal, "myval");
+        assertEquals(obtainedVal, "myNewVal");
+        assertEquals(releasedVal, "myval");
+    }
+
+    private static <T> List<T> randomized(Iterable<T> list) {
+        // TODO inefficient implementation, but don't care for small tests
+        Random random = new Random();
+        List<T> result = Lists.newLinkedList();
+        for (T element : list) {
+            int index = (result.isEmpty() ? 0 : random.nextInt(result.size()));
+            result.add(index, element);
+        }
+        return result;
+    }
+    
+    private static void assertUserAndHost(SshMachineLocation l, String user, String host) {
+        assertEquals(l.getUser(), user);
+        assertEquals(l.getAddress().getHostAddress(), host);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java
new file mode 100644
index 0000000..2d66cb5
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/HostLocationResolverTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.net.InetAddress;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.brooklyn.location.MachineProvisioningLocation;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.basic.Entities;
+import brooklyn.management.internal.LocalManagementContext;
+
+import com.google.common.collect.ImmutableMap;
+
+public class HostLocationResolverTest {
+    
+    private BrooklynProperties brooklynProperties;
+    private LocalManagementContext managementContext;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstance();
+        brooklynProperties = managementContext.getBrooklynProperties();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+    
+    @Test
+    public void testThrowsOnInvalid() throws Exception {
+        assertThrowsNoSuchElement("wrongprefix:(hosts=\"1.1.1.1\")");
+        assertThrowsIllegalArgument("host");
+    }
+    
+    @Test
+    public void testThrowsOnInvalidTarget() throws Exception {
+        assertThrowsIllegalArgument("host:()");
+    }
+    
+    @Test
+    public void resolveHosts() {
+        resolve("host:(\"1.1.1.1\")");
+        resolve("host:(\"localhost\")");
+    }
+    
+    @Test(groups="Integration")
+    public void resolveRealHosts() {
+        // must be online to resolve this
+        resolve("host:(\"www.foo.com\")");
+    }
+    
+    @Test
+    public void testNamedByonLocation() throws Exception {
+        brooklynProperties.put("brooklyn.location.named.mynamed", "host:(\"1.1.1.1\")");
+        
+        MachineProvisioningLocation<SshMachineLocation> loc = resolve("named:mynamed");
+        assertEquals(loc.obtain(ImmutableMap.of()).getAddress(), InetAddress.getByName("1.1.1.1"));
+    }
+
+    @Test
+    public void testPropertyScopePrescedence() throws Exception {
+        brooklynProperties.put("brooklyn.location.named.mynamed", "host:(\"1.1.1.1\")");
+        
+        // prefer those in "named" over everything else
+        brooklynProperties.put("brooklyn.location.named.mynamed.privateKeyFile", "privateKeyFile-inNamed");
+        brooklynProperties.put("brooklyn.localhost.privateKeyFile", "privateKeyFile-inGeneric");
+
+        // prefer location-generic if nothing else
+        brooklynProperties.put("brooklyn.location.privateKeyData", "privateKeyData-inGeneric");
+
+        Map<String, Object> conf = resolve("named:mynamed").obtain(ImmutableMap.of()).config().getBag().getAllConfig();
+        
+        assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed");
+        assertEquals(conf.get("privateKeyData"), "privateKeyData-inGeneric");
+    }
+
+    private void assertThrowsNoSuchElement(String val) {
+        try {
+            resolve(val);
+            fail();
+        } catch (NoSuchElementException e) {
+            // success
+        }
+    }
+    
+    private void assertThrowsIllegalArgument(String val) {
+        try {
+            resolve(val);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // success
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    private MachineProvisioningLocation<SshMachineLocation> resolve(String val) {
+        return (MachineProvisioningLocation<SshMachineLocation>) managementContext.getLocationRegistry().resolve(val);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java
new file mode 100644
index 0000000..10e59b3
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LegacyAbstractLocationTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.flags.SetFromFlag;
+
+import com.google.common.collect.ImmutableList;
+
+public class LegacyAbstractLocationTest {
+
+    private static class ConcreteLocation extends AbstractLocation {
+        @SetFromFlag(defaultVal="mydefault")
+        String myfield;
+
+        public ConcreteLocation() {
+            super();
+        }
+
+        public ConcreteLocation(Map properties) {
+            super(properties);
+        }
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown(){
+        // nothing to tear down; did not create a management context
+    }
+
+    @Test
+    public void testEquals() {
+        AbstractLocation l1 = new ConcreteLocation(MutableMap.of("id", "1", "name", "bob"));
+        AbstractLocation l2 = new ConcreteLocation(MutableMap.of("id", "1", "name", "frank"));
+        AbstractLocation l3 = new ConcreteLocation(MutableMap.of("id", "2", "name", "frank"));
+        assertEquals(l1, l2);
+        assertNotEquals(l2, l3);
+    }
+
+    @Test
+    public void nullNameAndParentLocationIsAcceptable() {
+        AbstractLocation location = new ConcreteLocation(MutableMap.of("name", null, "parentLocation", null));
+        assertEquals(location.getDisplayName(), null);
+        assertEquals(location.getParent(), null);
+    }
+
+    @Test
+    public void testSettingParentLocation() {
+        AbstractLocation location = new ConcreteLocation();
+        AbstractLocation locationSub = new ConcreteLocation();
+        locationSub.setParent(location);
+        
+        assertEquals(ImmutableList.copyOf(location.getChildren()), ImmutableList.of(locationSub));
+        assertEquals(locationSub.getParent(), location);
+    }
+
+    @Test
+    public void testClearingParentLocation() {
+        AbstractLocation location = new ConcreteLocation();
+        AbstractLocation locationSub = new ConcreteLocation();
+        locationSub.setParent(location);
+        
+        locationSub.setParent(null);
+        assertEquals(ImmutableList.copyOf(location.getChildren()), Collections.emptyList());
+        assertEquals(locationSub.getParent(), null);
+    }
+    
+    @Test
+    public void testContainsLocation() {
+        AbstractLocation location = new ConcreteLocation();
+        AbstractLocation locationSub = new ConcreteLocation();
+        locationSub.setParent(location);
+        
+        assertTrue(location.containsLocation(location));
+        assertTrue(location.containsLocation(locationSub));
+        assertFalse(locationSub.containsLocation(location));
+    }
+
+
+    @Test
+    public void queryingNameReturnsNameGivenInConstructor() {
+        String name = "Outer Mongolia";
+        AbstractLocation location = new ConcreteLocation(MutableMap.of("name", "Outer Mongolia"));
+        assertEquals(location.getDisplayName(), name);;
+    }
+
+    @Test
+    public void constructorParentLocationReturnsExpectedLocation() {
+        AbstractLocation parent = new ConcreteLocation(MutableMap.of("name", "Middle Earth"));
+        AbstractLocation child = new ConcreteLocation(MutableMap.of("name", "The Shire", "parentLocation", parent));
+        assertEquals(child.getParent(), parent);
+        assertEquals(ImmutableList.copyOf(parent.getChildren()), ImmutableList.of(child));
+    }
+
+    @Test
+    public void setParentLocationReturnsExpectedLocation() {
+        AbstractLocation parent = new ConcreteLocation(MutableMap.of("name", "Middle Earth"));
+        AbstractLocation child = new ConcreteLocation(MutableMap.of("name", "The Shire"));
+        child.setParent(parent);
+        assertEquals(child.getParent(), parent);
+        assertEquals(ImmutableList.copyOf(parent.getChildren()), ImmutableList.of(child));
+    }
+    
+    @Test
+    public void testAddChildToParentLocationReturnsExpectedLocation() {
+        AbstractLocation parent = new ConcreteLocation(MutableMap.of("id", "1"));
+        AbstractLocation child = new ConcreteLocation(MutableMap.of("id", "2"));
+        parent.addChild(child);
+        assertEquals(child.getParent(), parent);
+        assertEquals(ImmutableList.copyOf(parent.getChildren()), ImmutableList.of(child));
+    }
+
+    @Test
+    public void testFieldSetFromFlag() {
+        ConcreteLocation loc = new ConcreteLocation(MutableMap.of("myfield", "myval"));
+        assertEquals(loc.myfield, "myval");
+    }
+    
+    @Test
+    public void testFieldSetFromFlagUsesDefault() {
+        ConcreteLocation loc = new ConcreteLocation();
+        assertEquals(loc.myfield, "mydefault");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java
new file mode 100644
index 0000000..fd6f29c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostLocationResolverTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.basic.Entities;
+import org.apache.brooklyn.location.NoMachinesAvailableException;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.util.text.StringEscapes.JavaStringEscapes;
+
+import com.google.common.collect.ImmutableList;
+
+public class LocalhostLocationResolverTest {
+
+    private BrooklynProperties brooklynProperties;
+    private LocalManagementContext managementContext;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstance();
+        brooklynProperties = managementContext.getBrooklynProperties();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+    
+    @Test
+    public void testTakesLocalhostScopedProperties() {
+        brooklynProperties.put("brooklyn.location.localhost.privateKeyFile", "myprivatekeyfile");
+        brooklynProperties.put("brooklyn.location.localhost.publicKeyFile", "mypublickeyfile");
+        brooklynProperties.put("brooklyn.location.localhost.privateKeyData", "myprivateKeyData");
+        brooklynProperties.put("brooklyn.location.localhost.publicKeyData", "myPublicKeyData");
+        brooklynProperties.put("brooklyn.location.localhost.privateKeyPassphrase", "myprivateKeyPassphrase");
+
+        Map<String, Object> conf = resolve("localhost").config().getBag().getAllConfig();
+        
+        assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile");
+        assertEquals(conf.get("publicKeyFile"), "mypublickeyfile");
+        assertEquals(conf.get("privateKeyData"), "myprivateKeyData");
+        assertEquals(conf.get("publicKeyData"), "myPublicKeyData");
+        assertEquals(conf.get("privateKeyPassphrase"), "myprivateKeyPassphrase");
+    }
+
+    @Test
+    public void testTakesLocalhostDeprecatedScopedProperties() {
+        brooklynProperties.put("brooklyn.localhost.privateKeyFile", "myprivatekeyfile");
+        brooklynProperties.put("brooklyn.localhost.publicKeyFile", "mypublickeyfile");
+        brooklynProperties.put("brooklyn.localhost.privateKeyData", "myprivateKeyData");
+        brooklynProperties.put("brooklyn.localhost.publicKeyData", "myPublicKeyData");
+        brooklynProperties.put("brooklyn.localhost.privateKeyPassphrase", "myprivateKeyPassphrase");
+
+        Map<String, Object> conf = resolve("localhost").config().getBag().getAllConfig();
+        
+        assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile");
+        assertEquals(conf.get("publicKeyFile"), "mypublickeyfile");
+        assertEquals(conf.get("privateKeyData"), "myprivateKeyData");
+        assertEquals(conf.get("publicKeyData"), "myPublicKeyData");
+        assertEquals(conf.get("privateKeyPassphrase"), "myprivateKeyPassphrase");
+    }
+
+    @Test
+    public void testTakesDeprecatedProperties() {
+        brooklynProperties.put("brooklyn.localhost.private-key-file", "myprivatekeyfile");
+        brooklynProperties.put("brooklyn.localhost.public-key-file", "mypublickeyfile");
+        brooklynProperties.put("brooklyn.localhost.private-key-data", "myprivateKeyData");
+        brooklynProperties.put("brooklyn.localhost.public-key-data", "myPublicKeyData");
+        brooklynProperties.put("brooklyn.localhost.private-key-passphrase", "myprivateKeyPassphrase");
+        Map<String, Object> conf = resolve("localhost").config().getBag().getAllConfig();
+        
+        assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile");
+        assertEquals(conf.get("publicKeyFile"), "mypublickeyfile");
+        assertEquals(conf.get("privateKeyData"), "myprivateKeyData");
+        assertEquals(conf.get("publicKeyData"), "myPublicKeyData");
+        assertEquals(conf.get("privateKeyPassphrase"), "myprivateKeyPassphrase");
+    }
+    
+    @Test
+    public void testPropertyScopePrescedence() {
+        brooklynProperties.put("brooklyn.location.named.mynamed", "localhost");
+        
+        // prefer those in "named" over everything else
+        brooklynProperties.put("brooklyn.location.named.mynamed.privateKeyFile", "privateKeyFile-inNamed");
+        brooklynProperties.put("brooklyn.location.localhost.privateKeyFile", "privateKeyFile-inProviderSpecific");
+        brooklynProperties.put("brooklyn.localhost.privateKeyFile", "privateKeyFile-inGeneric");
+
+        // prefer those in provider-specific over generic
+        brooklynProperties.put("brooklyn.location.localhost.publicKeyFile", "publicKeyFile-inProviderSpecific");
+        brooklynProperties.put("brooklyn.location.publicKeyFile", "publicKeyFile-inGeneric");
+
+        // prefer location-generic if nothing else
+        brooklynProperties.put("brooklyn.location.privateKeyData", "privateKeyData-inGeneric");
+
+        Map<String, Object> conf = resolve("named:mynamed").config().getBag().getAllConfig();
+        
+        assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed");
+        assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inProviderSpecific");
+        assertEquals(conf.get("privateKeyData"), "privateKeyData-inGeneric");
+    }
+
+    @Test
+    public void testLocalhostLoads() {
+        Assert.assertTrue(resolve("localhost") instanceof LocalhostMachineProvisioningLocation);
+    }
+
+    @Test
+    public void testThrowsOnInvalid() throws Exception {
+        assertThrowsNoSuchElement("wrongprefix");
+        assertThrowsIllegalArgument("localhost(name=abc"); // no closing bracket
+        assertThrowsIllegalArgument("localhost(name)"); // no value for name
+        assertThrowsIllegalArgument("localhost(name=)"); // no value for name
+    }
+    
+
+    @Test
+    public void testAcceptsList() {
+        List<Location> l = getLocationResolver().resolve(ImmutableList.of("localhost"));
+        assertEquals(l.size(), 1, "l="+l);
+        assertTrue(l.get(0) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testRegistryCommaResolution() throws NoMachinesAvailableException {
+        List<Location> l;
+        l = getLocationResolver().resolve(JavaStringEscapes.unwrapJsonishListIfPossible("localhost,localhost,localhost"));
+        assertEquals(l.size(), 3, "l="+l);
+        assertTrue(l.get(0) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+        assertTrue(l.get(1) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+        assertTrue(l.get(2) instanceof LocalhostMachineProvisioningLocation, "l="+l);
+
+        // And check works if comma in brackets
+        l = getLocationResolver().resolve(JavaStringEscapes.unwrapJsonishListIfPossible(
+            "[ \"byon:(hosts=\\\"192.168.0.1\\\",user=bob)\", \"byon:(hosts=\\\"192.168.0.2\\\",user=bob2)\" ]"));
+        assertEquals(l.size(), 2, "l="+l);
+        assertTrue(l.get(0) instanceof FixedListMachineProvisioningLocation, "l="+l);
+        assertTrue(l.get(1) instanceof FixedListMachineProvisioningLocation, "l="+l);
+        assertEquals(((FixedListMachineProvisioningLocation<SshMachineLocation>)l.get(0)).obtain().getUser(), "bob");
+        assertEquals(((FixedListMachineProvisioningLocation<SshMachineLocation>)l.get(1)).obtain().getUser(), "bob2");
+    }
+
+    @Test(expectedExceptions={NoSuchElementException.class})
+    public void testRegistryCommaResolutionInListNotAllowed1() throws NoMachinesAvailableException {
+        // disallowed since 0.7.0
+        getLocationResolver().resolve(ImmutableList.of("localhost,localhost,localhost"));
+    }
+
+    @Test(expectedExceptions={IllegalArgumentException.class})
+    public void testRegistryCommaResolutionInListNotAllowed2() throws NoMachinesAvailableException {
+        // disallowed since 0.7.0
+        // fails because it interprets the entire string as a single spec, which does not parse
+        getLocationResolver().resolve(ImmutableList.of("localhost(),localhost()"));
+    }
+
+    @Test(expectedExceptions={IllegalArgumentException.class})
+    public void testRegistryCommaResolutionInListNotAllowed3() throws NoMachinesAvailableException {
+        // disallowed since 0.7.0
+        // fails because it interprets the entire string as a single spec, which does not parse
+        getLocationResolver().resolve(ImmutableList.of("localhost(name=a),localhost(name=b)"));
+    }
+
+    @Test(expectedExceptions={IllegalArgumentException.class})
+    public void testDoesNotAcceptsListOLists() {
+        ((BasicLocationRegistry)managementContext.getLocationRegistry()).resolve(ImmutableList.of(ImmutableList.of("localhost")));
+    }
+
+    @Test
+    public void testResolvesExplicitName() throws Exception {
+        Location location = resolve("localhost(name=myname)");
+        assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+        assertEquals(location.getDisplayName(), "myname");
+    }
+    
+    @Test
+    public void testWithOldStyleColon() throws Exception {
+        Location location = resolve("localhost:(name=myname)");
+        assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+        assertEquals(location.getDisplayName(), "myname");
+    }
+    
+    @Test
+    public void testResolvesPropertiesInSpec() throws Exception {
+        LocationInternal location = resolve("localhost(privateKeyFile=myprivatekeyfile,name=myname)");
+        assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+        assertEquals(location.getDisplayName(), "myname");
+        assertEquals(location.config().getBag().getStringKey("privateKeyFile"), "myprivatekeyfile");
+    }
+    
+    @Test
+    public void testResolvesDefaultName() throws Exception {
+        Location location = resolve("localhost");
+        assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+        assertEquals(location.getDisplayName(), "localhost");
+
+        Location location2 = resolve("localhost()");
+        assertTrue(location2 instanceof LocalhostMachineProvisioningLocation);
+        assertEquals(location2.getDisplayName(), "localhost");
+    }
+    
+    private BasicLocationRegistry getLocationResolver() {
+        return (BasicLocationRegistry) managementContext.getLocationRegistry();
+    }
+    
+    private LocationInternal resolve(String val) {
+        Location l = managementContext.getLocationRegistry().resolve(val);
+        Assert.assertNotNull(l);
+        return (LocationInternal) l;
+    }
+    
+    private void assertThrowsNoSuchElement(String val) {
+        try {
+            resolve(val);
+            fail();
+        } catch (NoSuchElementException e) {
+            // success
+        }
+
+        // and check the long form returns an Absent (not throwing)
+        Assert.assertTrue(managementContext.getLocationRegistry().resolve(val, false, null).isAbsent());
+    }
+    
+    private void assertThrowsIllegalArgument(String val) {
+        try {
+            resolve(val);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // success
+        }
+        
+        // and check the long form returns an Absent (not throwing)
+        Assert.assertTrue(managementContext.getLocationRegistry().resolve(val, false, null).isAbsent());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java
new file mode 100644
index 0000000..d948eaf
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocationTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.net.ServerSocket;
+
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.Entities;
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.location.MachineProvisioningLocation;
+import org.apache.brooklyn.location.NoMachinesAvailableException;
+import org.apache.brooklyn.location.PortRange;
+import org.apache.brooklyn.location.geo.HostGeoInfo;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Networking;
+
+public class LocalhostMachineProvisioningLocationTest {
+
+    private static final Logger log = LoggerFactory.getLogger(LocalhostMachineProvisioningLocationTest.class);
+    
+    private LocalManagementContext mgmt;
+
+    @BeforeMethod
+    @AfterClass
+    protected void clearStatics() {
+        LocalhostMachineProvisioningLocation.clearStaticData();
+    }
+    
+    @BeforeClass
+    protected void setup() {
+        mgmt = LocalManagementContextForTests.newInstance();
+    }
+    
+    @AfterClass
+    protected void teardown() {
+        Entities.destroyAll(mgmt);
+    }
+    
+    protected LocalhostMachineProvisioningLocation newLocalhostProvisioner() {
+        return mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+    }
+    
+    protected LocalhostMachineProvisioningLocation newLocalhostProvisionerWithAddress(String address) {
+        return mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class)
+            .configure("address", address));
+    }
+    
+    @Test
+    public void defaultInvocationCanProvisionALocalhostInstance() throws Exception {
+        LocalhostMachineProvisioningLocation provisioner = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+        SshMachineLocation machine = provisioner.obtain();
+        assertNotNull(machine);
+        assertEquals(machine.address, Networking.getLocalHost());
+    }
+
+    @Test
+    public void testUsesLocationNameProvided() throws Exception {
+        LocalhostMachineProvisioningLocation provisioner = newLocalhostProvisionerWithAddress("localhost");
+        assertEquals(((SshMachineLocation)provisioner.obtain()).getAddress().getHostName(), "localhost");
+
+        LocalhostMachineProvisioningLocation provisioner2 = newLocalhostProvisionerWithAddress("1.2.3.4");
+        assertEquals(((SshMachineLocation)provisioner2.obtain()).getAddress().getHostName(), "1.2.3.4");
+        
+        LocalhostMachineProvisioningLocation provisioner3 = newLocalhostProvisionerWithAddress("127.0.0.1");
+        assertEquals(((SshMachineLocation)provisioner3.obtain()).getAddress().getHostName(), "127.0.0.1");
+    }
+    
+    public void provisionWithASpecificNumberOfInstances() throws NoMachinesAvailableException {
+        LocalhostMachineProvisioningLocation provisioner = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class)
+            .configure("count", 2));
+
+        // first machine
+        SshMachineLocation first = provisioner.obtain();
+        assertNotNull(first);
+        assertEquals(first.address, Networking.getLocalHost());
+
+        // second machine
+        SshMachineLocation second = provisioner.obtain();
+        assertNotNull(second);
+        assertEquals(second.address, Networking.getLocalHost());
+
+        // third machine - fails
+        try {
+            SshMachineLocation third = provisioner.obtain();
+            fail("did not throw expected exception; got "+third);
+        } catch (NoMachinesAvailableException e) {
+            /* expected */
+        }
+    }
+    
+    @Test
+    public void obtainTwoAddressesInRangeThenDontObtain() throws Exception {
+        LocalhostMachineProvisioningLocation p = newLocalhostProvisioner();
+        SshMachineLocation m = p.obtain();
+
+        // Find two ports that are free, rather than risk false-negatives if a port was left open by something else.
+        int start = 48311;
+        while (true) {
+            if (Networking.isPortAvailable(m.getAddress(), start) && Networking.isPortAvailable(m.getAddress(), start+1)) {
+                break;
+            } else {
+                start++;
+            }
+        }
+        PortRange r = PortRanges.fromString(""+start+"-"+(start+1));
+        
+        try {
+            int i1 = m.obtainPort(r);
+            Assert.assertEquals(i1, start);
+            int i2 = m.obtainPort(r);
+            Assert.assertEquals(i2, start+1);
+            
+            //should fail
+            int i3 = m.obtainPort(r);
+            Assert.assertEquals(i3, -1);
+
+            //releasing and reapplying should succed
+            m.releasePort(i2);
+            int i4 = m.obtainPort(r);
+            Assert.assertEquals(i4, i2);
+
+        } finally {
+            m.releasePort(start);
+            m.releasePort(start+1);
+        }
+    }
+    
+    @Test
+    public void obtainLowNumberedPortsAutomatically() throws Exception {
+        LocalhostMachineProvisioningLocation p = newLocalhostProvisioner();
+        SshMachineLocation m = p.obtain();
+        int start = 983;  //random rarely used port, not that it matters
+        try {
+            int actual = m.obtainPort(PortRanges.fromInteger(start));
+            Assert.assertEquals(actual, start);
+        } finally {
+            m.releasePort(start);
+        }
+
+    }
+
+    @Test
+    public void obtainPortFailsIfInUse() throws Exception {
+        LocalhostMachineProvisioningLocation p = newLocalhostProvisioner();
+        SshMachineLocation m = p.obtain();
+        
+        // Find two ports that are free, rather than risk false-negatives if a port was left open by something else.
+        int start = 48311;
+        while (true) {
+            if (Networking.isPortAvailable(m.getAddress(), start) && Networking.isPortAvailable(m.getAddress(), start+1)) {
+                break;
+            } else {
+                start++;
+            }
+        }
+        PortRange r = PortRanges.fromString(""+start+"-"+(start+1));
+
+        ServerSocket ss = null;
+        try {
+            ss = new ServerSocket(start, 0, m.getAddress());
+            int i1 = m.obtainPort(r);
+            Assert.assertEquals(i1, start+1);
+        } finally {
+            if (ss!=null) ss.close();
+            m.releasePort(start);
+            m.releasePort(start+1);
+        }
+    }
+
+    @Test
+    public void obtainLocationWithGeography() throws Exception {
+        mgmt.getBrooklynProperties().put("brooklyn.location.named.lhx", "localhost");
+        // bogus location so very little chance of it being what maxmind returns!
+        mgmt.getBrooklynProperties().put("brooklyn.location.named.lhx.latitude", 42d);
+        mgmt.getBrooklynProperties().put("brooklyn.location.named.lhx.longitude", -20d);
+        MachineProvisioningLocation<?> p = (MachineProvisioningLocation<?>) mgmt.getLocationRegistry().resolve("named:lhx");
+        SshMachineLocation m = (SshMachineLocation) p.obtain(MutableMap.of());
+        HostGeoInfo geo = HostGeoInfo.fromLocation(m);
+        log.info("Geo info for "+m+" is: "+geo);
+        Assert.assertEquals(geo.latitude, 42d, 0.00001);
+        Assert.assertEquals(geo.longitude, -20d, 0.00001);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java
new file mode 100644
index 0000000..fad4567
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocalhostProvisioningAndAccessTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.apache.brooklyn.location.Location;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.basic.Entities;
+import brooklyn.management.internal.LocalManagementContext;
+
+public class LocalhostProvisioningAndAccessTest {
+
+    private LocalManagementContext mgmt;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        mgmt = new LocalManagementContext(BrooklynProperties.Factory.newDefault());
+    }
+    
+    @AfterMethod(alwaysRun = true)
+    public void tearDown(){
+        if (mgmt != null) Entities.destroyAll(mgmt);
+    }
+
+    @Test(groups="Integration")
+    public void testProvisionAndConnect() throws Exception {
+        Location location = mgmt.getLocationRegistry().resolve("localhost");
+        assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+        SshMachineLocation m = ((LocalhostMachineProvisioningLocation)location).obtain();
+        int result = m.execCommands("test", Arrays.asList("echo hello world"));
+        assertEquals(result, 0);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java
new file mode 100644
index 0000000..b3e4f04
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.test.entity.LocalManagementContextForTests;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.util.flags.SetFromFlag;
+
+import com.google.common.collect.ImmutableMap;
+
+public class LocationConfigTest {
+
+    // TODO Duplication of LocationConfigTest, but with locations instead of entities
+    
+    private ManagementContext managementContext;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstance();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+
+    @Test
+    public void testConfigBagContainsMatchesForConfigKeyName() throws Exception {
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("mylocation.myconfig", "myval1")
+                .configure("mylocation.myconfigwithflagname", "myval2"));
+        
+        assertEquals(loc.config().getBag().getAllConfig(), ImmutableMap.of("mylocation.myconfig", "myval1", "mylocation.myconfigwithflagname", "myval2"));
+        assertEquals(loc.config().getLocalBag().getAllConfig(), ImmutableMap.of("mylocation.myconfig", "myval1", "mylocation.myconfigwithflagname", "myval2"));
+        Assert.assertEquals(loc.getAllConfig(true), ImmutableMap.of("mylocation.myconfig", "myval1", "mylocation.myconfigwithflagname", "myval2"));
+        Assert.assertEquals(loc.getAllConfig(false), ImmutableMap.of("mylocation.myconfig", "myval1", "mylocation.myconfigwithflagname", "myval2"));
+    }
+
+    // TODO Note difference compared to Location, where both flag-name + config-key-name are in the ConfigBag
+    @Test
+    public void testConfigBagContainsMatchesForFlagName() throws Exception {
+        // Prefers flag-name, over config-key's name
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("myconfigflagname", "myval"));
+        
+        assertEquals(loc.config().getBag().getAllConfig(), ImmutableMap.of("mylocation.myconfigwithflagname", "myval", "myconfigflagname", "myval"));
+        assertEquals(loc.config().getLocalBag().getAllConfig(), ImmutableMap.of("mylocation.myconfigwithflagname", "myval", "myconfigflagname", "myval"));
+        Assert.assertEquals(loc.getAllConfig(true), ImmutableMap.of("mylocation.myconfigwithflagname", "myval", "myconfigflagname", "myval"));
+        Assert.assertEquals(loc.getAllConfig(false), ImmutableMap.of("mylocation.myconfigwithflagname", "myval", "myconfigflagname", "myval"));
+    }
+
+    @Test
+    public void testConfigBagContainsUnmatched() throws Exception {
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("notThere", "notThereVal"));
+        
+        assertEquals(loc.config().getBag().getAllConfig(), ImmutableMap.of("notThere", "notThereVal"));
+        assertEquals(loc.config().getLocalBag().getAllConfig(), ImmutableMap.of("notThere", "notThereVal"));
+        Assert.assertEquals(loc.getAllConfig(true), ImmutableMap.of("notThere", "notThereVal"));
+        Assert.assertEquals(loc.getAllConfig(false), ImmutableMap.of("notThere", "notThereVal"));
+    }
+    
+    // TODO Note difference from entity: child's bag contains both the flag-name and the config-key-name
+    @Test
+    public void testChildConfigBagInheritsUnmatchedAtParent() throws Exception {
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("mychildlocation.myconfig", "myval1")
+                .configure("notThere", "notThereVal"));
+
+        LocationInternal child = managementContext.getLocationManager().createLocation(LocationSpec.create(MyChildLocation.class)
+                .parent(loc));
+
+        assertEquals(child.config().getBag().getAllConfig(), ImmutableMap.of("mychildlocation.myconfig", "myval1", "notThere", "notThereVal"));
+        assertEquals(child.config().getLocalBag().getAllConfig(), ImmutableMap.of());
+        Assert.assertEquals(child.getAllConfig(true), ImmutableMap.of("mychildlocation.myconfig", "myval1", "notThere", "notThereVal"));
+        Assert.assertEquals(child.getAllConfig(false), ImmutableMap.of());
+    }
+    
+    // TODO Fails for location, but passes for entity; not worth fixing here; locations will soon be entities!
+    @Test(groups="WIP")
+    public void testChildConfigBagInheritsFlagNameFromParentSetsOwnConfigKey() throws Exception {
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("mychildconfigflagname", "myval"));
+
+        LocationInternal child = managementContext.getLocationManager().createLocation(LocationSpec.create(MyChildLocation.class)
+                .parent(loc));
+
+        assertEquals(child.config().getBag().getAllConfig(), ImmutableMap.of("mychildlocation.myconfigwithflagname", "myval"));
+        assertEquals(child.config().getLocalBag().getAllConfig(), ImmutableMap.of());
+        Assert.assertEquals(child.getAllConfig(true), ImmutableMap.of("mychildlocation.myconfigwithflagname", "myval"));
+        Assert.assertEquals(child.getAllConfig(false), ImmutableMap.of());
+    }
+    
+    @Test
+    public void testChildInheritsFromParent() throws Exception {
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("mylocation.myconfig", "myval1"));
+
+        LocationInternal child = managementContext.getLocationManager().createLocation(LocationSpec.create(MyChildLocation.class)
+                .parent(loc));
+
+        assertEquals(child.config().getBag().getAllConfig(), ImmutableMap.of("mylocation.myconfig", "myval1"));
+        assertEquals(child.config().getLocalBag().getAllConfig(), ImmutableMap.of());
+        Assert.assertEquals(child.getAllConfig(true), ImmutableMap.of("mylocation.myconfig", "myval1"));
+        Assert.assertEquals(child.getAllConfig(false), ImmutableMap.of());
+    }
+    
+    @Test
+    public void testChildCanOverrideConfigUsingKeyName() throws Exception {
+        LocationInternal location = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("mychildlocation.myconfigwithflagname", "myval")
+                .configure("notThere", "notThereVal"));
+
+        LocationInternal child = managementContext.getLocationManager().createLocation(LocationSpec.create(MyChildLocation.class)
+                .parent(location)
+                .configure("mychildlocation.myconfigwithflagname", "overrideMyval")
+                .configure("notThere", "overrideNotThereVal"));
+
+        assertEquals(child.config().getBag().getAllConfig(), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "notThere", "overrideNotThereVal"));
+        assertEquals(child.config().getLocalBag().getAllConfig(), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "notThere", "overrideNotThereVal"));
+        Assert.assertEquals(child.getAllConfig(true), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "notThere", "overrideNotThereVal"));
+        Assert.assertEquals(child.getAllConfig(false), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "notThere", "overrideNotThereVal"));
+    }
+    
+    // TODO Note difference compared to Location, where both flag-name + config-key-name are in the ConfigBag
+    @Test
+    public void testChildCanOverrideConfigUsingFlagName() throws Exception {
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("mychildlocation.myconfigwithflagname", "myval"));
+
+        LocationInternal child = managementContext.getLocationManager().createLocation(LocationSpec.create(MyChildLocation.class)
+                .parent(loc)
+                .configure("mychildconfigflagname", "overrideMyval"));
+
+        assertEquals(child.config().getBag().getAllConfig(), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "mychildconfigflagname", "overrideMyval"));
+        assertEquals(child.config().getLocalBag().getAllConfig(), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "mychildconfigflagname", "overrideMyval"));
+        Assert.assertEquals(child.getAllConfig(true), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "mychildconfigflagname", "overrideMyval"));
+        Assert.assertEquals(child.getAllConfig(false), ImmutableMap.of("mychildlocation.myconfigwithflagname", "overrideMyval", "mychildconfigflagname", "overrideMyval"));
+    }
+    
+    @Test
+    public void testLocationCanOverrideConfigDefaultValue() throws Exception {
+        LocationInternal loc = managementContext.getLocationManager().createLocation(LocationSpec.create(MyLocation.class));
+        LocationInternal subloc = managementContext.getLocationManager().createLocation(LocationSpec.create(MySubLocation.class));
+
+        Assert.assertEquals(loc.getConfig(MyLocation.MY_CONFIG_WITH_DEFAULT), "mydefault");
+        Assert.assertEquals(loc.getConfig(ConfigKeys.newStringConfigKey("mylocation.myconfigwithdefault", "", "differentdefault")), "mydefault");
+        
+        Assert.assertEquals(subloc.getConfig(MySubLocation.MY_CONFIG_WITH_DEFAULT), "mysubdefault");
+        Assert.assertEquals(subloc.getConfig(MyLocation.MY_CONFIG_WITH_DEFAULT), "mysubdefault");
+    }
+    
+    @SuppressWarnings("serial")
+    public static class MyLocation extends AbstractLocation {
+        public static final ConfigKey<String> MY_CONFIG = ConfigKeys.newStringConfigKey("mylocation.myconfig");
+
+        @SetFromFlag("myconfigflagname")
+        public static final ConfigKey<String> MY_CONFIG_WITH_FLAGNAME = ConfigKeys.newStringConfigKey("mylocation.myconfigwithflagname");
+        
+        public static final ConfigKey<String> MY_CONFIG_WITH_DEFAULT = ConfigKeys.newStringConfigKey("mylocation.myconfigwithdefault", "", "mydefault");
+    }
+    
+    @SuppressWarnings("serial")
+    public static class MyChildLocation extends AbstractLocation {
+        public static final ConfigKey<String> MY_CHILD_CONFIG = ConfigKeys.newStringConfigKey("mychildlocation.myconfig");
+
+        @SetFromFlag("mychildconfigflagname")
+        public static final ConfigKey<String> MY_CHILD_CONFIG_WITH_FLAGNAME = ConfigKeys.newStringConfigKey("mychildlocation.myconfigwithflagname");
+    }
+    
+    @SuppressWarnings("serial")
+    public static class MySubLocation extends MyLocation {
+        public static final ConfigKey<String> MY_CONFIG_WITH_DEFAULT = ConfigKeys.newConfigKeyWithDefault(MyLocation.MY_CONFIG_WITH_DEFAULT, "mysubdefault");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java
new file mode 100644
index 0000000..e9197a1
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/location/basic/LocationConfigUtilsTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.brooklyn.location.basic;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.util.config.ConfigBag;
+
+@Test
+public class LocationConfigUtilsTest {
+
+    // set these system properties differently if needed to fix your tests
+    public static final String SSH_PRIVATE_KEY_FILE_WITH_TILDE = System.getProperty("sshPrivateKey", "~/.ssh/id_rsa");
+    public static final String SSH_PUBLIC_KEY_FILE_WITH_TILDE = System.getProperty("sshPublicKey", "~/.ssh/id_rsa.pub");
+    // these should work as they are on classpath
+    public static final String SSH_PRIVATE_KEY_FILE_WITH_PASSPHRASE = System.getProperty("sshPrivateKeyWithPassphrase", "/brooklyn/util/crypto/sample_rsa_passphrase.pem");
+    public static final String SSH_PRIVATE_KEY_FILE = System.getProperty("sshPrivateKeySample", "/org/apache/brooklyn/location/basic/sample_id_rsa");
+    public static final String SSH_PUBLIC_KEY_FILE = System.getProperty("sshPublicKeySample", "/org/apache/brooklyn/location/basic/sample_id_rsa.pub");
+    
+    public void testPreferPrivateKeyDataOverFile() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PRIVATE_KEY_DATA, "mydata");
+        config.put(LocationConfigKeys.PRIVATE_KEY_FILE, SSH_PRIVATE_KEY_FILE);
+        
+        LocationConfigUtils.OsCredential creds = LocationConfigUtils.getOsCredential(config).doKeyValidation(false);
+        Assert.assertTrue(creds.hasKey());
+        // warnings, as it is malformed
+        Assert.assertFalse(creds.getWarningMessages().isEmpty());
+
+        String data = creds.getPrivateKeyData();
+        assertEquals(data, "mydata");
+    }
+
+    @Test(expectedExceptions=IllegalStateException.class)
+    public void testInvalidKeyData() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PRIVATE_KEY_DATA, "mydata");
+        
+        LocationConfigUtils.OsCredential creds = LocationConfigUtils.getOsCredential(config).doKeyValidation(false);
+        Assert.assertTrue(creds.hasKey());
+        Assert.assertFalse(creds.getWarningMessages().isEmpty());
+        
+        creds.checkNoErrors();
+    }
+
+    public void testPreferPublicKeyDataOverFileAndNoPrivateKeyRequired() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PUBLIC_KEY_DATA, "mydata");
+        config.put(LocationConfigKeys.PUBLIC_KEY_FILE, SSH_PUBLIC_KEY_FILE);
+        config.put(LocationConfigKeys.PRIVATE_KEY_FILE, "");
+        
+        LocationConfigUtils.OsCredential creds = LocationConfigUtils.getOsCredential(config);
+        String data = creds.getPublicKeyData();
+        assertEquals(data, "mydata");
+        Assert.assertNull(creds.getPreferredCredential());
+        Assert.assertFalse(creds.hasPassword());
+        Assert.assertFalse(creds.hasKey());
+        // and not even any warnings here
+        Assert.assertTrue(creds.getWarningMessages().isEmpty());
+    }
+    
+    @Test(groups="Integration")  // requires ~/.ssh/id_rsa
+    public void testReadsPrivateKeyFileWithTildePath() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PRIVATE_KEY_FILE, SSH_PRIVATE_KEY_FILE_WITH_TILDE);
+
+        // don't mind if it has a passphrase
+        String data = LocationConfigUtils.getOsCredential(config).doKeyValidation(false).getPreferredCredential();
+        assertTrue(data != null && data.length() > 0);
+    }
+    
+    @Test(groups="Integration")
+    public void testReadsPrivateKeyFileWithPassphrase() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PRIVATE_KEY_FILE, SSH_PRIVATE_KEY_FILE_WITH_PASSPHRASE);
+
+        LocationConfigUtils.OsCredential cred = LocationConfigUtils.getOsCredential(config).doKeyValidation(false);
+        String data = cred.getPreferredCredential();
+        assertTrue(data != null && data.length() > 0);
+        Assert.assertFalse(data.isEmpty());
+        
+        cred.doKeyValidation(true);
+        try {
+            cred.checkNoErrors();
+            Assert.fail("check should fail as passphrase needed");
+        } catch (IllegalStateException exception) {
+        }
+
+        config.put(LocationConfigKeys.PRIVATE_KEY_PASSPHRASE, "passphrase");
+        cred.checkNoErrors();
+        
+        config.put(LocationConfigKeys.PRIVATE_KEY_PASSPHRASE, "wrong_passphrase");
+        try {
+            cred.checkNoErrors();
+            Assert.fail("check should fail as passphrase needed");
+        } catch (IllegalStateException exception) {
+        }
+    }
+    
+    public void testReadsPrivateKeyFileWithMultipleColonSeparatedFilesWithGoodLast() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PRIVATE_KEY_FILE, "/path/does/not/exist"+File.pathSeparator+SSH_PRIVATE_KEY_FILE);
+        
+        String data = LocationConfigUtils.getOsCredential(config).getPreferredCredential();
+        assertTrue(data != null && data.length() > 0);
+    }
+    
+    public void testReadsPrivateKeyFileWithMultipleColonSeparatedFilesWithGoodFirst() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PRIVATE_KEY_FILE, SSH_PRIVATE_KEY_FILE+File.pathSeparator+"/path/does/not/exist");
+
+        String data = LocationConfigUtils.getOsCredential(config).getPreferredCredential();
+        assertTrue(data != null && data.length() > 0);
+    }
+    
+    @Test(groups="Integration")  // requires ~/.ssh/id_rsa
+    public void testReadsPublicKeyFileWithTildePath() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PUBLIC_KEY_FILE, SSH_PUBLIC_KEY_FILE_WITH_TILDE);
+        
+        // don't mind if it has a passphrase
+        String data = LocationConfigUtils.getOsCredential(config).doKeyValidation(false).getPublicKeyData();
+        assertTrue(data != null && data.length() > 0);
+    }
+    
+    public void testInfersPublicKeyFileFromPrivateKeyFile() throws Exception {
+        ConfigBag config = ConfigBag.newInstance();
+        config.put(LocationConfigKeys.PRIVATE_KEY_FILE, SSH_PRIVATE_KEY_FILE);
+        
+        String data = LocationConfigUtils.getOsCredential(config).getPublicKeyData();
+        assertTrue(data != null && data.length() > 0);
+    }
+}


Mime
View raw message