Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,14 @@

import fiftyone.devicedetection.examples.shared.EvidenceHelper;
import fiftyone.devicedetection.examples.shared.KeyHelper;

import fiftyone.devicedetection.shared.testhelpers.KeyUtils;
import org.junit.Test;

import static org.junit.Assume.assumeFalse;

public class GettingStartedCloudTest {
@Test
public void gettingStartedCloudTest() throws Exception {
String resourceKey = KeyHelper.getOrSetTestResourceKey(false);
assumeFalse("Skipping test, no resource key found",
KeyUtils.isInvalidKey(resourceKey));
String resourceKey = KeyHelper.requireTestResourceKey(
"whether a device is mobile, and its platform and browser",
"device");

GettingStartedCloud.run(resourceKey,
EvidenceHelper.setUpEvidence(), System.out);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,13 @@
package fiftyone.devicedetection.examples.console;

import fiftyone.devicedetection.examples.shared.KeyHelper;
import fiftyone.devicedetection.shared.testhelpers.KeyUtils;
import org.junit.Test;

import static org.junit.Assume.assumeFalse;

public class MetadataCloudTest {
@Test
public void gettingMetaDataCloudTest() throws Exception {
String resourceKey = KeyHelper.getOrSetTestResourceKey(false);
assumeFalse("Skipping test, no resource key found",
KeyUtils.isInvalidKey(resourceKey));
String resourceKey = KeyHelper.requireTestResourceKey(
"the device detection properties it offers", "device");
MetadataCloud.run(resourceKey, System.out);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,13 @@
package fiftyone.devicedetection.examples.console;

import fiftyone.devicedetection.examples.shared.KeyHelper;
import fiftyone.devicedetection.shared.testhelpers.KeyUtils;
import org.junit.Test;

import static org.junit.Assume.assumeFalse;

public class MinimalExampleTest {
@Test
public void minimalExampleTest() throws Exception {
String resourceKey = KeyHelper.getOrSetTestResourceKey(false);
assumeFalse("Skipping test, no resource key found",
KeyUtils.isInvalidKey(resourceKey));
String resourceKey = KeyHelper.requireTestResourceKey(
"whether a device is mobile", "device");
MinimalExample.run(resourceKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@
package fiftyone.devicedetection.examples.console;

import fiftyone.devicedetection.examples.shared.KeyHelper;
import fiftyone.devicedetection.shared.testhelpers.KeyUtils;
import org.junit.Test;

import static org.junit.Assume.assumeFalse;
import java.util.Collections;

public class NativeModelCloudTest {
@Test
public void run() throws Exception {
String resourceKey =
KeyUtils.getNamedKey(NativeModelCloud.NATIVE_MODEL_EXAMPLE_RESOURCE_KEY_NAME);
assumeFalse("Skipping test, no resource key found",
KeyUtils.isInvalidKey(resourceKey));
String resourceKey = KeyHelper.requireResourceKey(
Collections.singletonList(
NativeModelCloud.NATIVE_MODEL_EXAMPLE_RESOURCE_KEY_NAME),
"the devices that match a native model name", "hardware");
NativeModelCloud.run(resourceKey, System.out);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@
package fiftyone.devicedetection.examples.console;

import fiftyone.devicedetection.examples.shared.KeyHelper;
import fiftyone.devicedetection.shared.testhelpers.KeyUtils;
import org.junit.Test;

import static org.junit.Assume.assumeFalse;
import java.util.Collections;

public class TacCloudTest {

@Test
public void run() throws Exception {
String resourceKey = KeyUtils.getNamedKey(TacCloud.TAC_EXAMPLE_RESOURCE_KEY_NAME);
assumeFalse("Skipping test, no resource key found",
KeyUtils.isInvalidKey(resourceKey));
String resourceKey = KeyHelper.requireResourceKey(
Collections.singletonList(TacCloud.TAC_EXAMPLE_RESOURCE_KEY_NAME),
"the devices that match a TAC", "hardware");
TacCloud.run(resourceKey, System.out);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,127 @@
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

public class KeyHelper {
public static final String TEST_RESOURCE_KEY = "TestResourceKey";
static Logger logger = LoggerFactory.getLogger(KeyHelper.class);

/**
* The names every cloud test reads a resource key from first, before
* the names its own example reads.
*/
public static final List<String> SHARED_RESOURCE_KEY_NAMES =
Collections.unmodifiableList(Arrays.asList(
"_51DEGREES_RESOURCE_KEY",
"_51DEGREES_RESOURCE_KEY_BESPOKE"));

static final String CREATE_KEY_URL =
"https://configure.51degrees.com/?utm_source=code&utm_medium=example&utm_campaign=51degrees-java&utm_content=shared-src-main-java-fiftyone-devicedetection-examples-shared-keyhelper.java&utm_term=no-resource-key";

/**
* The resource key for a test that calls the live cloud service, from
* {@link #SHARED_RESOURCE_KEY_NAMES} and then {@link #TEST_RESOURCE_KEY}.
* Fails the test when there is none, because a cloud test that passes
* without a key has called nothing.
* @param asksFor what the test asks the service for
* @param products the products the key has to carry
* @return the resource key
*/
public static String requireTestResourceKey(String asksFor,
String... products) {
return requireResourceKey(Collections.singletonList(TEST_RESOURCE_KEY),
asksFor, products);
}

/**
* The resource key for a test that calls the live cloud service, from
* {@link #SHARED_RESOURCE_KEY_NAMES} and then the given names.
* @param exampleNames the names the example reads, in the order it
* reads them
* @param asksFor what the test asks the service for
* @param products the products the key has to carry
* @return the resource key
*/
public static String requireResourceKey(List<String> exampleNames,
String asksFor,
String... products) {
return requireResourceKey(KeyHelper::readKey, exampleNames, asksFor,
products);
}

static String requireResourceKey(Function<String, String> read,
List<String> exampleNames,
String asksFor,
String... products) {
List<String> names = new ArrayList<>(SHARED_RESOURCE_KEY_NAMES);
names.addAll(exampleNames);
for (String name : names) {
String value = read.apply(name);
if (KeyUtils.isInvalidKey(value) == false) {
return value.trim();
}
}
throw new AssertionError(noResourceKey(names, asksFor, products));
}

static String noResourceKey(List<String> names, String asksFor,
String... products) {
return "This test calls the live 51Degrees cloud service, so it " +
"needs a resource key and there is nothing to fall back on. " +
"None of " + String.join(", ", names) + " is set, as an " +
"environment variable or as a system property, tried in that " +
"order. The key has to carry the " + productList(products) +
", because the test asks the service for " + asksFor + ". To " +
"see the products a key carries, open " +
"https://cloud.51degrees.com/api/v4/accessibleproperties?resource=<key>. " +
"Create a key at " + CREATE_KEY_URL + ".";
}

/**
* The message for a value the cloud answered without.
* @param thing the property or element that has no value
* @param reason the reason the cloud gave, or null
* @param products the products a key needs for the value
* @return the message
*/
public static String answeredWithout(String thing, String reason,
String... products) {
return "The cloud answered without " + thing + " (" +
(reason == null || reason.trim().isEmpty() ?
"no reason given" : reason.trim()) +
"), which is what a key that does not carry the " +
productList(products) + " gets.";
}

private static String productList(String... products) {
StringBuilder list = new StringBuilder();
for (int i = 0; i < products.length; i++) {
if (i > 0) {
list.append(i == products.length - 1 ? " and " : ", ");
}
list.append(products[i]);
}
return list + (products.length == 1 ? " product" : " products");
}

/**
* A key read as an environment variable, as its upper case name, and
* then as a system property, skipping any that is not a key, such as
* the empty string the example poms pass for a key that is not set.
*/
private static String readKey(String name) {
for (String value : new String[]{
System.getenv(name),
System.getenv(name.toUpperCase()),
System.getProperty(name)}) {
if (KeyUtils.isInvalidKey(value) == false) {
return value;
}
}
return null;
}

/**
* Obtain a resource key either from environment variable or from a property.
*/
Expand Down Expand Up @@ -79,8 +194,11 @@ public static String getOrSetResourceKey(String value, String variableName,
}
}

// capture the passed parameter for next time called
System.setProperty(variableName, value);
// Keep a valid key for next time. System.setProperty throws on null,
// and an invalid key is not worth keeping.
if (KeyUtils.isInvalidKey(value) == false) {
System.setProperty(variableName, value);
}
return value;
}
public static String getOrSetSuperResourceKey(String value, String variablename) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2026 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */

package fiftyone.devicedetection.examples.shared;

import org.junit.Test;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;

public class KeyHelperTest {
/**
* Values of the form KeyUtils accepts as a key. Neither is a real key.
*/
private static final String KEY_A = "AQAAAAAAAAAAAAAAAAAAAAAA";
private static final String KEY_B = "AgAAAAAAAAAAAAAAAAAAAAAA";

/**
* With no key anywhere, the lookup logs that there is none and stores
* nothing, rather than passing null to System.setProperty.
*/
@Test
public void getOrSetTestResourceKey_NoKey_StoresNothing() {
String name = KeyHelper.TEST_RESOURCE_KEY;
// A test can clear a system property but not an environment
// variable.
assumeTrue(name + " is set in the environment",
System.getenv(name) == null &&
System.getenv(name.toUpperCase()) == null);
String saved = System.clearProperty(name);
try {
assertNull(KeyHelper.getOrSetTestResourceKey(false));
assertNull(System.getProperty(name));
} finally {
if (saved != null) {
System.setProperty(name, saved);
}
}
}

@Test
public void requireResourceKey_NoKey_FailsNamingEveryName() {
AssertionError error = assertThrows(AssertionError.class, () ->
KeyHelper.requireResourceKey(name -> null,
Collections.singletonList(KeyHelper.TEST_RESOURCE_KEY),
"whether a device is mobile", "device"));
assertEquals("This test calls the live 51Degrees cloud service, so " +
"it needs a resource key and there is nothing to fall back " +
"on. None of _51DEGREES_RESOURCE_KEY, " +
"_51DEGREES_RESOURCE_KEY_BESPOKE, TestResourceKey is set, as " +
"an environment variable or as a system property, tried in " +
"that order. The key has to carry the device product, " +
"because the test asks the service for whether a device is " +
"mobile. To see the products a key carries, open " +
"https://cloud.51degrees.com/api/v4/accessibleproperties?resource=<key>. " +
"Create a key at " + KeyHelper.CREATE_KEY_URL + ".",
error.getMessage());
}

@Test
public void requireResourceKey_TwoProducts_NamesBoth() {
AssertionError error = assertThrows(AssertionError.class, () ->
KeyHelper.requireResourceKey(name -> null,
Collections.singletonList("SuperResourceKey"),
"the devices that match a TAC", "hardware", "device"));
assertTrue(error.getMessage(), error.getMessage().contains(
"None of _51DEGREES_RESOURCE_KEY, " +
"_51DEGREES_RESOURCE_KEY_BESPOKE, SuperResourceKey is set"));
assertTrue(error.getMessage(), error.getMessage().contains(
"carry the hardware and device products, because"));
}

/**
* A name holding something that is not a key, such as the empty
* string the example poms pass for a key that is not set, is passed
* over.
*/
@Test
public void requireResourceKey_FirstValidNameWins() {
Map<String, String> values = new HashMap<>();
values.put("_51DEGREES_RESOURCE_KEY", "");
values.put("_51DEGREES_RESOURCE_KEY_BESPOKE", KEY_A);
values.put(KeyHelper.TEST_RESOURCE_KEY, KEY_B);
assertEquals(KEY_A, KeyHelper.requireResourceKey(values::get,
Collections.singletonList(KeyHelper.TEST_RESOURCE_KEY),
"whether a device is mobile", "device"));
}

@Test
public void requireResourceKey_ExampleNameAfterSharedNames() {
Map<String, String> values = new HashMap<>();
values.put(KeyHelper.TEST_RESOURCE_KEY, KEY_B);
assertEquals(KEY_B, KeyHelper.requireResourceKey(values::get,
Collections.singletonList(KeyHelper.TEST_RESOURCE_KEY),
"whether a device is mobile", "device"));
}

@Test
public void answeredWithout_NoReason_SaysSo() {
assertEquals("The cloud answered without BrowserName (no reason " +
"given), which is what a key that does not carry the device " +
"product gets.",
KeyHelper.answeredWithout("BrowserName", null, "device"));
assertEquals("The cloud answered without BrowserName (BrowserName " +
"is a paid feature), which is what a key that does not carry " +
"the device product gets.",
KeyHelper.answeredWithout("BrowserName",
"BrowserName is a paid feature", "device"));
}
}
Loading
Loading