http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/basic/TestPortSupplierLocation.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/basic/TestPortSupplierLocation.java
 
b/core/src/test/java/org/apache/brooklyn/location/basic/TestPortSupplierLocation.java
deleted file mode 100644
index e7b6963..0000000
--- 
a/core/src/test/java/org/apache/brooklyn/location/basic/TestPortSupplierLocation.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.entity.EntitySpec;
-import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
-import org.apache.brooklyn.core.test.entity.TestEntity;
-import org.apache.brooklyn.sensor.core.PortAttributeSensorAndConfigKey;
-import org.apache.brooklyn.sensor.feed.ConfigToAttributes;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class TestPortSupplierLocation extends BrooklynAppUnitTestSupport {
-
-    SimulatedLocation loc;
-    PortAttributeSensorAndConfigKey ps;
-    TestEntity entity;
-    
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        loc = app.newSimulatedLocation();
-        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
-        app.start(ImmutableList.of(loc));
-        
-        ps = new PortAttributeSensorAndConfigKey("some.port", "for testing", 
"1234+");
-    }
-
-    @Test
-    public void testObtainsPort() throws Exception {
-        ConfigToAttributes.apply(entity, ps);
-        
-        int p = entity.getAttribute(ps);
-        assertEquals(p, 1234);
-        
-        //sensor access should keep the same value
-        p = entity.getAttribute(ps);
-        assertEquals(p, 1234);
-    }
-    
-    @Test
-    public void testRepeatedConvertAccessIncrements() throws Exception {
-        int p = ps.getAsSensorValue(entity);
-        assertEquals(p, 1234);
-
-        //but direct access should see it as being reserved (not required 
behaviour, but it is the current behaviour)
-        int p2 = ps.getAsSensorValue(entity);
-        assertEquals(p2, 1235);
-    }
-
-    @Test
-    public void testNullBeforeSetting() throws Exception {
-        // currently getting the attribute before explicitly setting return 
null; i.e. no "auto-set" -- 
-        // but this behaviour may be changed
-        Integer p = entity.getAttribute(ps);
-        assertEquals(p, null);
-    }
-
-    @Test
-    public void testSimulatedRestrictedPermitted() throws Exception {
-        loc.setPermittedPorts(PortRanges.fromString("1240+"));
-        
-        ConfigToAttributes.apply(entity, ps);
-        int p = entity.getAttribute(ps);
-        assertEquals((int)p, 1240);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
new file mode 100644
index 0000000..8d89a40
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
@@ -0,0 +1,429 @@
+/*
+ * 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.byon;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
+import org.apache.brooklyn.location.core.BasicLocationRegistry;
+import org.apache.brooklyn.location.core.LocationConfigKeys;
+import org.apache.brooklyn.location.core.NamedLocationResolver;
+import org.apache.brooklyn.location.core.internal.LocationInternal;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.net.UserAndHostAndPort;
+import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.StringPredicates;
+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.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class ByonLocationResolverTest {
+
+    private static final Logger log = 
LoggerFactory.getLogger(ByonLocationResolverTest.class);
+    
+    private BrooklynProperties brooklynProperties;
+    private LocalManagementContext managementContext;
+    private Predicate<CharSequence> defaultNamePredicate;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstance();
+        brooklynProperties = managementContext.getBrooklynProperties();
+        defaultNamePredicate = 
StringPredicates.startsWith(FixedListMachineProvisioningLocation.class.getSimpleName());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) Entities.destroyAll(managementContext);
+    }
+    
+    @Test
+    public void testTakesByonScopedProperties() {
+        brooklynProperties.put("brooklyn.location.byon.privateKeyFile", 
"myprivatekeyfile");
+        brooklynProperties.put("brooklyn.location.byon.publicKeyFile", 
"mypublickeyfile");
+        brooklynProperties.put("brooklyn.location.byon.privateKeyData", 
"myprivateKeyData");
+        brooklynProperties.put("brooklyn.location.byon.publicKeyData", 
"myPublicKeyData");
+        brooklynProperties.put("brooklyn.location.byon.privateKeyPassphrase", 
"myprivateKeyPassphrase");
+
+        Map<String, Object> conf = 
resolve("byon(hosts=\"1.1.1.1\")").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 testNamedByonLocation() throws Exception {
+        brooklynProperties.put("brooklyn.location.named.mynamed", 
"byon(hosts=\"1.1.1.1\")");
+        
+        FixedListMachineProvisioningLocation<MachineLocation> loc = 
resolve("named:mynamed");
+        assertEquals(loc.obtain().getAddress(), 
InetAddress.getByName("1.1.1.1"));
+    }
+
+    @Test
+    public void testPropertiesInSpec() throws Exception {
+        FixedListMachineProvisioningLocation<MachineLocation> loc = 
resolve("byon(privateKeyFile=myprivatekeyfile,hosts=\"1.1.1.1\")");
+        SshMachineLocation machine = (SshMachineLocation)loc.obtain();
+        
+        assertEquals(machine.config().getBag().getStringKey("privateKeyFile"), 
"myprivatekeyfile");
+        assertEquals(machine.getAddress(), 
Networking.getInetAddressWithFixedName("1.1.1.1"));
+    }
+
+    @Test
+    public void testPropertyScopePrecedence() throws Exception {
+        brooklynProperties.put("brooklyn.location.named.mynamed", 
"byon(hosts=\"1.1.1.1\")");
+        
+        // prefer those in "named" over everything else
+        
brooklynProperties.put("brooklyn.location.named.mynamed.privateKeyFile", 
"privateKeyFile-inNamed");
+        brooklynProperties.put("brooklyn.location.byon.privateKeyFile", 
"privateKeyFile-inProviderSpecific");
+        brooklynProperties.put("brooklyn.localhost.privateKeyFile", 
"privateKeyFile-inGeneric");
+
+        // prefer those in provider-specific over generic
+        brooklynProperties.put("brooklyn.location.byon.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 testThrowsOnInvalid() throws Exception {
+        assertThrowsNoSuchElement("wrongprefix:(hosts=\"1.1.1.1\")");
+        assertThrowsIllegalArgument("byon"); // no hosts
+        assertThrowsIllegalArgument("byon()"); // no hosts
+        assertThrowsIllegalArgument("byon(hosts=\"\")"); // empty hosts
+        assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\""); // no closing 
bracket
+        assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\", name)"); // no 
value for name
+        assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\", name=)"); // no 
value for name
+    }
+    
+    @Test(expectedExceptions={IllegalArgumentException.class})
+    public void testRegistryCommaResolutionInListNotAllowed() throws 
NoMachinesAvailableException {
+        // disallowed since 0.7.0
+        // fails because it interprets the entire string as a single byon 
spec, which does not parse
+        
managementContext.getLocationRegistry().resolve(ImmutableList.of("byon(hosts=\"192.168.0.1\",user=bob),byon(hosts=\"192.168.0.2\",user=bob2)"));
+    }
+
+    @Test
+    public void testResolvesHosts() throws Exception {
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\")"), 
ImmutableSet.of("1.1.1.1"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\")"), 
ImmutableSet.of("1.1.1.1"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1,1.1.1.2\")"), 
ImmutableSet.of("1.1.1.1","1.1.1.2"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1, 1.1.1.2\")"), 
ImmutableSet.of("1.1.1.1","1.1.1.2"));
+    }
+
+    @Test
+    public void testWithOldStyleColon() throws Exception {
+        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\")"), 
ImmutableSet.of("1.1.1.1"));
+        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\", 
name=myname)"), ImmutableSet.of("1.1.1.1"), "myname");
+    }
+
+    @Test
+    public void testUsesDisplayName() throws Exception {
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\", 
name=myname)"), ImmutableSet.of("1.1.1.1"), "myname");
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\", 
name=\"myname\")"), ImmutableSet.of("1.1.1.1"), "myname");
+    }
+
+    @Test
+    public void testResolvesHostsGlobExpansion() throws Exception {
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.{1,2}\")"), 
ImmutableSet.of("1.1.1.1","1.1.1.2"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.{1.1,2.{1,2}}\")"), 
+                ImmutableSet.of("1.1.1.1","1.1.2.1","1.1.2.2"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.{1,2}.{1,2}\")"), 
+                ImmutableSet.of("1.1.1.1","1.1.1.2","1.1.2.1","1.1.2.2"));
+    }
+
+    @Test(groups="Integration")
+    public void testNiceError() throws Exception {
+        Asserts.assertFailsWith(new Runnable() {
+            @Override public void run() {
+                FixedListMachineProvisioningLocation<MachineLocation> x =
+                        resolve("byon(hosts=\"1.1.1.{1,2}}\")");
+                log.error("got "+x+" but should have failed (your DNS is 
giving an IP for hostname '1.1.1.1}' (with the extra '}')");
+            }
+        }, new Predicate<Throwable>() {
+            @Override
+            public boolean apply(@Nullable Throwable input) {
+                String s = input.toString();
+                // words
+                if (!s.contains("Invalid host")) return false;
+                // problematic entry
+                if (!s.contains("1.1.1.1}")) return false;
+                // original spec
+                if (!s.contains("1.1.1.{1,2}}")) return false;
+                return true;
+            }
+        });
+    }
+
+    @Test
+    public void testResolvesUsernameAtHost() throws Exception {
+        
assertByonClusterWithUsersEquals(resolve("byon(hosts=\"[email protected]\")"), 
+                ImmutableSet.of(UserAndHostAndPort.fromParts("myuser", 
"1.1.1.1", 22)));
+        
assertByonClusterWithUsersEquals(resolve("byon(hosts=\"[email protected],[email protected]\")"),
 ImmutableSet.of(
+                UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 22), 
UserAndHostAndPort.fromParts("myuser2", "1.1.1.1", 22)));
+        
assertByonClusterWithUsersEquals(resolve("byon(hosts=\"[email protected],[email protected]\")"),
 ImmutableSet.of(
+                UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 22), 
UserAndHostAndPort.fromParts("myuser2", "1.1.1.2", 22)));
+    }
+
+    @Test
+    public void testResolvesUserArg() throws Exception {
+        
assertByonClusterWithUsersEquals(resolve("byon(hosts=\"1.1.1.1\",user=bob)"), 
+                ImmutableSet.of(UserAndHostAndPort.fromParts("bob", "1.1.1.1", 
22)));
+        
assertByonClusterWithUsersEquals(resolve("byon(user=\"bob\",hosts=\"[email protected],1.1.1.1\")"),
 
+                ImmutableSet.of(UserAndHostAndPort.fromParts("myuser", 
"1.1.1.1", 22), UserAndHostAndPort.fromParts("bob", "1.1.1.1", 22)));
+    }
+
+    @Test
+    public void testResolvesUserArg2() throws Exception {
+        String spec = "byon(hosts=\"1.1.1.1\",user=bob)";
+        FixedListMachineProvisioningLocation<MachineLocation> ll = 
resolve(spec);
+        SshMachineLocation l = (SshMachineLocation)ll.obtain();
+        Assert.assertEquals("bob", l.getUser());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testResolvesUserArg3() throws Exception {
+        String spec = "byon(hosts=\"1.1.1.1\")";
+        
managementContext.getLocationRegistry().getProperties().putAll(MutableMap.of(
+                "brooklyn.location.named.foo", spec,
+                "brooklyn.location.named.foo.user", "bob"));
+        
((BasicLocationRegistry)managementContext.getLocationRegistry()).updateDefinedLocations();
+        
+        MachineProvisioningLocation<SshMachineLocation> ll = 
(MachineProvisioningLocation<SshMachineLocation>)
+                new 
NamedLocationResolver().newLocationFromString(MutableMap.of(), "named:foo", 
managementContext.getLocationRegistry());
+        SshMachineLocation l = ll.obtain(MutableMap.of());
+        Assert.assertEquals("bob", l.getUser());
+    }
+
+    @Test
+    public void testResolvesPortArg() throws Exception {
+        
assertByonClusterWithUsersEquals(resolve("byon(user=bob,port=8022,hosts=\"1.1.1.1\")"),
 
+                ImmutableSet.of(UserAndHostAndPort.fromParts("bob", "1.1.1.1", 
8022)));
+        
assertByonClusterWithUsersEquals(resolve("byon(user=bob,port=8022,hosts=\"[email protected],1.1.1.2:8901\")"),
 
+                ImmutableSet.of(UserAndHostAndPort.fromParts("myuser", 
"1.1.1.1", 8022), UserAndHostAndPort.fromParts("bob", "1.1.1.2", 8901)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    /** private key should be inherited, so confirm that happens correctly */
+    public void testResolvesPrivateKeyArgInheritance() throws Exception {
+        String spec = "byon(hosts=\"1.1.1.1\")";
+        
managementContext.getLocationRegistry().getProperties().putAll(MutableMap.of(
+                "brooklyn.location.named.foo", spec,
+                "brooklyn.location.named.foo.user", "bob",
+                "brooklyn.location.named.foo.privateKeyFile", "/tmp/x"));
+        
((BasicLocationRegistry)managementContext.getLocationRegistry()).updateDefinedLocations();
+        
+        MachineProvisioningLocation<SshMachineLocation> ll = 
(MachineProvisioningLocation<SshMachineLocation>) 
+                new 
NamedLocationResolver().newLocationFromString(MutableMap.of(), "named:foo", 
managementContext.getLocationRegistry());
+        
+        Assert.assertEquals("/tmp/x", 
ll.getConfig(LocationConfigKeys.PRIVATE_KEY_FILE));
+        
Assert.assertTrue(((LocationInternal)ll).config().getLocalRaw(LocationConfigKeys.PRIVATE_KEY_FILE).isPresent());
+        Assert.assertEquals("/tmp/x", 
((LocationInternal)ll).config().getLocalBag().getStringKey(LocationConfigKeys.PRIVATE_KEY_FILE.getName()));
+        Assert.assertEquals("/tmp/x", 
((LocationInternal)ll).config().getBag().get(LocationConfigKeys.PRIVATE_KEY_FILE));
+
+        SshMachineLocation l = ll.obtain(MutableMap.of());
+        
+        Assert.assertEquals("/tmp/x", 
l.getConfig(LocationConfigKeys.PRIVATE_KEY_FILE));
+        
+        
Assert.assertTrue(l.config().getRaw(LocationConfigKeys.PRIVATE_KEY_FILE).isPresent());
+        
Assert.assertTrue(l.config().getLocalRaw(LocationConfigKeys.PRIVATE_KEY_FILE).isAbsent());
+
+        Assert.assertEquals("/tmp/x", 
l.config().getBag().getStringKey(LocationConfigKeys.PRIVATE_KEY_FILE.getName()));
+        Assert.assertEquals("/tmp/x", 
l.config().getBag().getStringKey(LocationConfigKeys.PRIVATE_KEY_FILE.getName()));
+
+        Assert.assertEquals("/tmp/x", 
l.config().getBag().get(LocationConfigKeys.PRIVATE_KEY_FILE));
+    }
+
+    @Test
+    public void testResolvesLocalTempDir() throws Exception {
+        String localTempDir = Os.mergePaths(Os.tmp(), 
"testResolvesUsernameAtHost");
+        brooklynProperties.put("brooklyn.location.byon.localTempDir", 
localTempDir);
+
+        FixedListMachineProvisioningLocation<MachineLocation> byon = 
resolve("byon(hosts=\"1.1.1.1\",osFamily=\"windows\")");
+        MachineLocation machine = byon.obtain();
+        assertEquals(machine.getConfig(SshMachineLocation.LOCAL_TEMP_DIR), 
localTempDir);
+    }
+
+    @Test
+    public void testMachinesObtainedInOrder() throws Exception {
+        List<String> ips = ImmutableList.of("1.1.1.1", "1.1.1.6", "1.1.1.3", 
"1.1.1.4", "1.1.1.5");
+        String spec = "byon(hosts=\""+Joiner.on(",").join(ips)+"\")";
+        
+        MachineProvisioningLocation<MachineLocation> ll = resolve(spec);
+
+        for (String expected : ips) {
+            MachineLocation obtained = ll.obtain(ImmutableMap.of());
+            assertEquals(obtained.getAddress().getHostAddress(), expected);
+        }
+    }
+    
+    @Test
+    public void testEmptySpec() throws Exception {
+        String spec = "byon";
+        Map<String, ?> flags = ImmutableMap.of(
+                "hosts", ImmutableList.of("1.1.1.1", "2.2.2.22"),
+                "name", "foo",
+                "user", "myuser"
+        );
+        MachineProvisioningLocation<MachineLocation> provisioner = 
resolve(spec, flags);
+        SshMachineLocation location1 = 
(SshMachineLocation)provisioner.obtain(ImmutableMap.of());
+        Assert.assertEquals("myuser", location1.getUser());
+        Assert.assertEquals("1.1.1.1", 
location1.getAddress().getHostAddress());
+    }
+
+    @Test
+    public void testWindowsMachines() throws Exception {
+        brooklynProperties.put("brooklyn.location.byon.user", "myuser");
+        brooklynProperties.put("brooklyn.location.byon.password", 
"mypassword");
+        String spec = "byon";
+        Map<String, ?> flags = ImmutableMap.of(
+                "hosts", ImmutableList.of("1.1.1.1", "2.2.2.2"),
+                "osfamily", "windows"
+        );
+        MachineProvisioningLocation<MachineLocation> provisioner = 
resolve(spec, flags);
+        WinRmMachineLocation location = (WinRmMachineLocation) 
provisioner.obtain(ImmutableMap.of());
+
+        assertEquals(location.config().get(WinRmMachineLocation.USER), 
"myuser");
+        assertEquals(location.config().get(WinRmMachineLocation.PASSWORD), 
"mypassword");
+        
assertEquals(location.config().get(WinRmMachineLocation.ADDRESS).getHostAddress(),
 "1.1.1.1");
+    }
+
+    @Test
+    public void testNoneWindowsMachines() throws Exception {
+        String spec = "byon";
+        Map<String, ?> flags = ImmutableMap.of(
+                "hosts", ImmutableList.of("1.1.1.1", "2.2.2.2"),
+                "osfamily", "linux"
+        );
+        MachineProvisioningLocation<MachineLocation> provisioner = 
resolve(spec, flags);
+        MachineLocation location = provisioner.obtain(ImmutableMap.of());
+        assertTrue(location instanceof SshMachineLocation, "Expected location 
to be SshMachineLocation, found " + location);
+    }
+
+    @Test
+    public void testAdditionalConfig() throws Exception {
+        FixedListMachineProvisioningLocation<MachineLocation> loc = 
resolve("byon(mykey=myval,hosts=\"1.1.1.1\")");
+        MachineLocation machine = loc.obtain(ImmutableMap.of());
+        assertEquals(machine.getConfig(ConfigKeys.newConfigKey(String.class, 
"mykey")), "myval");
+    }
+
+    private void 
assertByonClusterEquals(FixedListMachineProvisioningLocation<? extends 
MachineLocation> cluster, Set<String> expectedHosts) {
+        assertByonClusterEquals(cluster, expectedHosts, defaultNamePredicate);
+    }
+    
+    private void 
assertByonClusterEquals(FixedListMachineProvisioningLocation<? extends 
MachineLocation> cluster, Set<String> expectedHosts, String expectedName) {
+        assertByonClusterEquals(cluster, expectedHosts, 
Predicates.equalTo(expectedName));
+    }
+    
+    private void 
assertByonClusterEquals(FixedListMachineProvisioningLocation<? extends 
MachineLocation> cluster, Set<String> expectedHosts, Predicate<? super String> 
expectedName) {
+        Set<String> actualHosts = 
ImmutableSet.copyOf(Iterables.transform(cluster.getMachines(), new 
Function<MachineLocation, String>() {
+            @Override public String apply(MachineLocation input) {
+                return input.getAddress().getHostName();
+            }}));
+        assertEquals(actualHosts, expectedHosts);
+        assertTrue(expectedName.apply(cluster.getDisplayName()), 
"name="+cluster.getDisplayName());
+    }
+
+    private void 
assertByonClusterWithUsersEquals(FixedListMachineProvisioningLocation<? extends 
MachineLocation> cluster, Set<UserAndHostAndPort> expectedHosts) {
+        assertByonClusterWithUsersEquals(cluster, expectedHosts, 
defaultNamePredicate);
+    }
+    
+    private void 
assertByonClusterWithUsersEquals(FixedListMachineProvisioningLocation<? extends 
MachineLocation> cluster, Set<UserAndHostAndPort> expectedHosts, Predicate<? 
super String> expectedName) {
+        Set<UserAndHostAndPort> actualHosts = 
ImmutableSet.copyOf(Iterables.transform(cluster.getMachines(), new 
Function<MachineLocation, UserAndHostAndPort>() {
+            @Override public UserAndHostAndPort apply(MachineLocation input) {
+                SshMachineLocation machine = (SshMachineLocation) input;
+                return UserAndHostAndPort.fromParts(machine.getUser(), 
machine.getAddress().getHostName(), machine.getPort());
+            }}));
+        assertEquals(actualHosts, expectedHosts);
+        assertTrue(expectedName.apply(cluster.getDisplayName()), 
"name="+cluster.getDisplayName());
+    }
+
+    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 FixedListMachineProvisioningLocation<MachineLocation> 
resolve(String val) {
+        return (FixedListMachineProvisioningLocation<MachineLocation>) 
managementContext.getLocationRegistry().resolve(val);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private FixedListMachineProvisioningLocation<MachineLocation> 
resolve(String val, Map<?, ?> locationFlags) {
+        return (FixedListMachineProvisioningLocation<MachineLocation>) 
managementContext.getLocationRegistry().resolve(val, locationFlags);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationRebindTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationRebindTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationRebindTest.java
new file mode 100644
index 0000000..3068115
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationRebindTest.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.location.byon;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestUtils;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
+import org.apache.brooklyn.location.core.LocationConfigKeys;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.os.Os;
+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.Iterables;
+
+public class FixedListMachineProvisioningLocationRebindTest {
+
+    private FixedListMachineProvisioningLocation<SshMachineLocation> origLoc;
+    private ClassLoader classLoader = getClass().getClassLoader();
+    private ManagementContext origManagementContext;
+    private TestApplication origApp;
+    private TestApplication newApp;
+    private File mementoDir;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        mementoDir = Os.newTempDir(getClass());
+        origManagementContext = 
RebindTestUtils.newPersistingManagementContext(mementoDir, classLoader, 1);
+        
+        origLoc = new 
FixedListMachineProvisioningLocation.Builder(origManagementContext.getLocationManager())
+                .addAddresses("localhost", "127.0.0.1")
+                .user("myuser")
+                .keyFile("/path/to/myPrivateKeyFile")
+                .keyData("myKeyData")
+                .keyPassphrase("myKeyPassphrase")
+                .build();
+        origApp = ApplicationBuilder.newManagedApp(TestApplication.class, 
origManagementContext);
+        origApp.start(ImmutableList.of(origLoc));
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws Exception {
+        if (origManagementContext != null) 
Entities.destroyAll(origManagementContext);
+        if (newApp != null) Entities.destroyAll(newApp.getManagementContext());
+        if (mementoDir != null) RebindTestUtils.deleteMementoDir(mementoDir);
+    }
+    
+    @Test
+    public void testRebindPreservesConfig() throws Exception {
+        newApp = rebind();
+        FixedListMachineProvisioningLocation<SshMachineLocation> newLoc = 
(FixedListMachineProvisioningLocation<SshMachineLocation>) 
Iterables.get(newApp.getLocations(), 0);
+        
+        assertEquals(newLoc.getId(), origLoc.getId());
+        assertEquals(newLoc.getDisplayName(), origLoc.getDisplayName());
+        assertEquals(newLoc.getHostGeoInfo(), origLoc.getHostGeoInfo());
+        assertEquals(newLoc.getConfig(LocationConfigKeys.USER), 
origLoc.getConfig(LocationConfigKeys.USER));
+        
assertEquals(newLoc.getConfig(LocationConfigKeys.PRIVATE_KEY_PASSPHRASE), 
origLoc.getConfig(LocationConfigKeys.PRIVATE_KEY_PASSPHRASE));
+        assertEquals(newLoc.getConfig(LocationConfigKeys.PRIVATE_KEY_FILE), 
origLoc.getConfig(LocationConfigKeys.PRIVATE_KEY_FILE));
+        assertEquals(newLoc.getConfig(LocationConfigKeys.PRIVATE_KEY_DATA), 
origLoc.getConfig(LocationConfigKeys.PRIVATE_KEY_DATA));
+    }
+
+    @Test
+    public void testRebindParentRelationship() throws Exception {
+        newApp = rebind();
+        FixedListMachineProvisioningLocation<SshMachineLocation> newLoc = 
(FixedListMachineProvisioningLocation<SshMachineLocation>) 
Iterables.get(newApp.getLocations(), 0);
+        
+        assertLocationIdsEqual(newLoc.getChildren(), origLoc.getChildren());
+        assertEquals(Iterables.get(newLoc.getChildren(), 0).getParent(), 
newLoc);
+        assertEquals(Iterables.get(newLoc.getChildren(), 1).getParent(), 
newLoc);
+    }
+
+    @Test
+    public void testRebindPreservesInUseMachines() throws Exception {
+        SshMachineLocation inuseMachine = origLoc.obtain();
+        origApp.setAttribute(TestApplication.SERVICE_UP, true); // to force 
persist, and thus avoid race
+        
+        newApp = rebind();
+        FixedListMachineProvisioningLocation<SshMachineLocation> newLoc = 
(FixedListMachineProvisioningLocation<SshMachineLocation>) 
Iterables.get(newApp.getLocations(), 0);
+        
+        assertLocationIdsEqual(newLoc.getInUse(), origLoc.getInUse());
+        assertLocationIdsEqual(newLoc.getAvailable(), origLoc.getAvailable());
+    }
+
+    private TestApplication rebind() throws Exception {
+        RebindTestUtils.waitForPersisted(origApp);
+        return (TestApplication) RebindTestUtils.rebind(mementoDir, 
getClass().getClassLoader());
+    }
+    
+    private void assertLocationIdsEqual(Iterable<? extends Location> actual, 
Iterable<? extends Location> expected) {
+        Function<Location, String> locationIdFunction = new Function<Location, 
String>() {
+            @Override public String apply(@Nullable Location input) {
+                return (input != null) ? input.getId() : null;
+            }
+        };
+        Set<String> actualIds = MutableSet.copyOf(Iterables.transform(actual, 
locationIdFunction));
+        Set<String> expectedIds = 
MutableSet.copyOf(Iterables.transform(expected, locationIdFunction));
+        
+        assertEquals(actualIds, expectedIds);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationTest.java
new file mode 100644
index 0000000..f36ab1e
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocationTest.java
@@ -0,0 +1,578 @@
+/*
+ * 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.byon;
+
+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.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
+import org.apache.brooklyn.location.core.RecordingMachineLocationCustomizer;
+import 
org.apache.brooklyn.location.core.RecordingMachineLocationCustomizer.Call;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.stream.Streams;
+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;
+
+/**
+ * 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("[email protected]").
+                addAddress("192.168.0.{3,4}").
+                addAddresses("192.168.0.{6-8}").
+                addAddressMultipleTimes("192.168.0.{8,7}", 2).
+                addAddress("[email protected].{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("[email protected]").
+                addAddress("192.168.0.{3,4}").
+                addAddresses("192.168.0.{6-8}").
+                addAddressMultipleTimes("192.168.0.{8,7}", 2).
+                addAddress("[email protected].{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/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/byon/HostLocationResolverTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/byon/HostLocationResolverTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/byon/HostLocationResolverTest.java
new file mode 100644
index 0000000..24427f6
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/location/byon/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.byon;
+
+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.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+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/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolverTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolverTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolverTest.java
new file mode 100644
index 0000000..8fbca63
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolverTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.byon;
+
+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.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.byon.SingleMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class SingleMachineLocationResolverTest {
+
+    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("single");
+    }
+    
+    @Test
+    public void testThrowsOnInvalidTarget() throws Exception {
+        assertThrowsIllegalArgument("single()");
+        assertThrowsIllegalArgument("single(wrongprefix:(hosts=\"1.1.1.1\"))");
+        assertThrowsIllegalArgument("single(foo:bar)");
+    }
+
+    @Test
+    public void resolveHosts() {
+        resolve("single(target=localhost)");
+        resolve("single(target=byon(hosts=\"1.1.1.1\"))");
+
+        brooklynProperties.put("brooklyn.location.named.mynamed", 
"single(target=byon:(hosts=\"1.1.1.1\"))");
+        managementContext.clearLocationRegistry();
+        resolve("single(target=named:mynamed)");
+    }
+    
+    @Test
+    public void resolveWithOldColonFormat() {
+        resolve("single:(target=localhost)");
+    }
+    
+    @Test
+    public void testNamedByonLocation() throws Exception {
+        brooklynProperties.put("brooklyn.location.named.mynamed", 
"single(target=byon:(hosts=\"1.1.1.1\"))");
+        
+        SingleMachineProvisioningLocation<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", 
"single(target=byon:(hosts=\"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 SingleMachineProvisioningLocation<SshMachineLocation> 
resolve(String val) {
+        return (SingleMachineProvisioningLocation<SshMachineLocation>) 
managementContext.getLocationRegistry().resolve(val);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocationTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocationTest.java
new file mode 100644
index 0000000..27dee20
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocationTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.location.byon;
+
+import static org.testng.Assert.assertNotNull;
+
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.location.byon.SingleMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class SingleMachineProvisioningLocationTest {
+    
+    private static final Logger log = 
LoggerFactory.getLogger(SingleMachineProvisioningLocation.class);
+    
+    private LocalManagementContext managementContext;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        managementContext = LocalManagementContextForTests.newInstance();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (managementContext != null) managementContext.terminate();
+    }
+    
+    @SuppressWarnings("unchecked") 
+    @Test
+    public void testLocalhostSingle() throws Exception {
+        SingleMachineProvisioningLocation<SshMachineLocation> l = 
(SingleMachineProvisioningLocation<SshMachineLocation>) 
+            
managementContext.getLocationRegistry().resolve("single:(target='localhost')");
+        l.setManagementContext(managementContext);
+        
+        SshMachineLocation m1 = l.obtain();
+        
+        assertNotNull(m1);
+
+        log.info("GOT "+m1);
+        
+        l.release(m1);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/test/java/org/apache/brooklyn/location/core/AbstractLocationTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/core/AbstractLocationTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/core/AbstractLocationTest.java
new file mode 100644
index 0000000..eb9c906
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/location/core/AbstractLocationTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.core;
+
+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.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.location.core.AbstractLocation;
+import org.apache.brooklyn.location.core.internal.LocationInternal;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class AbstractLocationTest {
+
+    public static class ConcreteLocation extends AbstractLocation {
+        private static final long serialVersionUID = 3954199300889119970L;
+        @SetFromFlag(defaultVal="mydefault")
+        String myfield;
+
+        public ConcreteLocation() {
+            super();
+        }
+
+        public ConcreteLocation(Map<?,?> properties) {
+            super(properties);
+        }
+    }
+
+    private ManagementContext mgmt;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        mgmt = LocalManagementContextForTests.newInstance();
+    }
+    
+    @AfterMethod(alwaysRun = true)
+    public void tearDown(){
+        if (mgmt!=null) Entities.destroyAll(mgmt);
+    }
+
+    private ConcreteLocation createConcrete() {
+        return createConcrete(MutableMap.<String,Object>of());
+    }
+    private ConcreteLocation createConcrete(Map<String,?> flags) {
+        return createConcrete(null, flags);
+    }
+    @SuppressWarnings("deprecation")
+    private ConcreteLocation createConcrete(String id, Map<String,?> flags) {
+        return mgmt.getLocationManager().createLocation( 
LocationSpec.create(ConcreteLocation.class).id(id).configure(flags) );
+    }
+    
+    @Test
+    public void testEqualsUsesId() {
+        Location l1 = createConcrete("1", MutableMap.of("name", "bob"));
+        Location l1b = new ConcreteLocation(ImmutableMap.of("id", 1));
+        Location l2 = createConcrete("2", MutableMap.of("name", "frank"));
+        assertEquals(l1, l1b);
+        assertNotEquals(l1, l2);
+    }
+
+    @Test
+    public void nullNameAndParentLocationIsAcceptable() {
+        Location location = createConcrete(MutableMap.of("name", null, 
"parentLocation", null));
+        assertEquals(location.getDisplayName(), null);
+        assertEquals(location.getParent(), null);
+    }
+
+    @Test
+    public void testSettingParentLocation() {
+        Location location = createConcrete();
+        Location locationSub = createConcrete();
+        locationSub.setParent(location);
+        
+        assertEquals(ImmutableList.copyOf(location.getChildren()), 
ImmutableList.of(locationSub));
+        assertEquals(locationSub.getParent(), location);
+    }
+
+    @Test
+    public void testClearingParentLocation() {
+        Location location = createConcrete();
+        Location locationSub = createConcrete();
+        locationSub.setParent(location);
+        
+        locationSub.setParent(null);
+        assertEquals(ImmutableList.copyOf(location.getChildren()), 
Collections.emptyList());
+        assertEquals(locationSub.getParent(), null);
+    }
+    
+    @Test
+    public void testContainsLocation() {
+        Location location = createConcrete();
+        Location locationSub = createConcrete();
+        locationSub.setParent(location);
+        
+        assertTrue(location.containsLocation(location));
+        assertTrue(location.containsLocation(locationSub));
+        assertFalse(locationSub.containsLocation(location));
+    }
+
+
+    @Test
+    public void queryingNameReturnsNameGivenInConstructor() {
+        String name = "Outer Mongolia";
+        Location location = createConcrete(MutableMap.of("name", "Outer 
Mongolia"));
+        assertEquals(location.getDisplayName(), name);;
+    }
+
+    @Test
+    public void constructorParentLocationReturnsExpectedLocation() {
+        Location parent = createConcrete(MutableMap.of("name", "Middle 
Earth"));
+        Location child = createConcrete(MutableMap.of("name", "The Shire", 
"parentLocation", parent));
+        assertEquals(child.getParent(), parent);
+        assertEquals(ImmutableList.copyOf(parent.getChildren()), 
ImmutableList.of(child));
+    }
+
+    @Test
+    public void setParentLocationReturnsExpectedLocation() {
+        Location parent = createConcrete(MutableMap.of("name", "Middle 
Earth"));
+        Location child = createConcrete(MutableMap.of("name", "The Shire"));
+        child.setParent(parent);
+        assertEquals(child.getParent(), parent);
+        assertEquals(ImmutableList.copyOf(parent.getChildren()), 
ImmutableList.of(child));
+    }
+    
+    @Test
+    public void testAddChildToParentLocationReturnsExpectedLocation() {
+        ConcreteLocation parent = createConcrete();
+        Location child = createConcrete();
+        parent.addChild(child);
+        assertEquals(child.getParent(), parent);
+        assertEquals(ImmutableList.copyOf(parent.getChildren()), 
ImmutableList.of(child));
+    }
+
+    @Test
+    public void testFieldSetFromFlag() {
+        ConcreteLocation loc = createConcrete(MutableMap.of("myfield", 
"myval"));
+        assertEquals(loc.myfield, "myval");
+    }
+    
+    @Test
+    public void testFieldSetFromFlagUsesDefault() {
+        ConcreteLocation loc = createConcrete();
+        assertEquals(loc.myfield, "mydefault");
+    }
+
+    @Test
+    public void testLocationTags() throws Exception {
+        LocationInternal loc = 
mgmt.getLocationManager().createLocation(LocationSpec.create(ConcreteLocation.class).tag("x"));
+        assertEquals(loc.tags().getTags(), MutableSet.of("x"));
+    }
+
+}

Reply via email to