diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedCloud.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedCloud.java index 846e1fcb4..533763807 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedCloud.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedCloud.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.DeviceDetectionPipelineBuilder; import fiftyone.devicedetection.examples.shared.KeyHelper; import fiftyone.devicedetection.examples.shared.EvidenceHelper; @@ -53,6 +54,12 @@ */ @SuppressWarnings("DuplicatedCode") public class GettingStartedCloud { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-gettingstartedcloud.java"; + private static final Logger logger = LoggerFactory.getLogger(GettingStartedCloud.class); /** @@ -66,6 +73,8 @@ public static void main(String[] args) throws Exception { // prepare 'evidence' for use in pipeline (see below) List> evidence = EvidenceHelper.setUpEvidence(); run(resourceKey, evidence, System.out); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.CLOUD_REPOSITORIES); } /** diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedOnPrem.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedOnPrem.java index 6cb6e8433..067394b73 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedOnPrem.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/GettingStartedOnPrem.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.examples.shared.DataFileHelper; import fiftyone.devicedetection.examples.shared.EvidenceHelper; import fiftyone.devicedetection.hash.engine.onpremise.flowelements.DeviceDetectionHashEngine; @@ -58,6 +59,12 @@ * The concepts of "pipeline", "flow data", "evidence" and "results" are illustrated. */ public class GettingStartedOnPrem { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-gettingstartedonprem.java"; + private static final Logger logger = LoggerFactory.getLogger(GettingStartedOnPrem.class); /* In this example, by default, the 51degrees "Lite" file is used from the data repository in @@ -84,6 +91,8 @@ public static void main(String[] args) throws Exception { // prepare 'evidence' for use in pipeline (see below) List> evidence = EvidenceHelper.setUpEvidence(); run(dataFile, evidence, System.out); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.ON_PREMISE_REPOSITORIES); } /** diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MatchMetrics.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MatchMetrics.java index ddabcaed4..3bcd93807 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MatchMetrics.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MatchMetrics.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.DeviceDetectionPipelineBuilder; import fiftyone.devicedetection.examples.shared.DataFileHelper; import fiftyone.devicedetection.examples.shared.EvidenceHelper; @@ -61,6 +62,12 @@ * page. */ public class MatchMetrics { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-matchmetrics.java"; + private static final Logger logger = LoggerFactory.getLogger(MatchMetrics.class); /** @@ -76,6 +83,8 @@ public static void main(String[] args) throws Exception { String dataFilename = DataFileHelper.getDataFileLocation( args.length > 0 ? args[0] : LITE_HASH_DATA_FILE_PATH); run(dataFilename, EvidenceHelper.setUpEvidence(), false, System.out); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.ON_PREMISE_REPOSITORIES); } /** diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataCloud.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataCloud.java index 8ca360fab..377cf30cf 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataCloud.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataCloud.java @@ -21,6 +21,7 @@ * ********************************************************************* */ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.DeviceDetectionPipelineBuilder; import fiftyone.devicedetection.cloud.flowelements.DeviceDetectionCloudEngine; import fiftyone.devicedetection.examples.shared.KeyHelper; @@ -42,12 +43,20 @@ public class MetadataCloud { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-metadatacloud.java"; + private static final Logger logger = LoggerFactory.getLogger(GettingStartedOnPrem.class); public static void main(String[] args) throws Exception { configureLogback(getFilePath("logback.xml")); String resourceKey = args.length > 0 ? args[0]: null; run(resourceKey, System.out); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.CLOUD_REPOSITORIES); } diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataOnPrem.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataOnPrem.java index c85d852b8..a7dbdb5f1 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataOnPrem.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MetadataOnPrem.java @@ -21,6 +21,7 @@ * ********************************************************************* */ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.pipeline.engines.fiftyone.data.FiftyOneAspectPropertyMetaData; import fiftyone.devicedetection.examples.shared.DataFileHelper; import fiftyone.devicedetection.hash.engine.onpremise.flowelements.DeviceDetectionHashEngine; @@ -45,6 +46,12 @@ public class MetadataOnPrem { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-metadataonprem.java"; + private static final Logger logger = LoggerFactory.getLogger(GettingStartedOnPrem.class); /* In this example, by default, the 51degrees "Lite" file is used from the data repository in @@ -65,6 +72,8 @@ public static void main(String[] args) throws Exception { configureLogback(getFilePath("logback.xml")); String dataFile = args.length > 0 ? args[0] : LITE_V_4_1_HASH; run(dataFile, System.out); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.ON_PREMISE_REPOSITORIES); } diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MinimalExample.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MinimalExample.java index 8c5fb5206..71f37abee 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MinimalExample.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/MinimalExample.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.DeviceDetectionPipelineBuilder; import fiftyone.devicedetection.shared.DeviceData; import fiftyone.pipeline.core.data.FlowData; @@ -44,9 +45,17 @@ */ public class MinimalExample { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-minimalexample.java"; + public static void main(String[] args) throws Exception { String resource = args.length > 0 ? args[0] : null; run(resource); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.CLOUD_REPOSITORIES); } public static void run(String resource) throws Exception { diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/NativeModelCloud.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/NativeModelCloud.java index 3c6c724ac..6545522f3 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/NativeModelCloud.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/NativeModelCloud.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.cloud.data.MultiDeviceDataCloud; import fiftyone.devicedetection.cloud.flowelements.HardwareProfileCloudEngineBuilder; import fiftyone.devicedetection.shared.Constants; @@ -41,6 +42,12 @@ import static fiftyone.pipeline.util.FileFinder.getFilePath; public class NativeModelCloud { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-nativemodelcloud.java"; + // Example values to use when looking up device details from native model names. private static final String NATIVE_MODEL_1 = "SC-03L"; private static final String NATIVE_MODEL_2 = "iPhone11,8"; @@ -90,6 +97,10 @@ public static void run(String resourceKey, OutputStream os) throws Exception { // Repeat for an alternative native model name. analyseNativeModel(NATIVE_MODEL_2, pipeline, output); } + // The writer closes the stream it was given, so the section is + // written before it. + FindOutMore.write(output, EXAMPLE_TAG, + FindOutMore.CLOUD_REPOSITORIES); } } diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/OfflineProcessing.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/OfflineProcessing.java index fbcdee51b..173d74ba0 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/OfflineProcessing.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/OfflineProcessing.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.DeviceDetectionPipelineBuilder; import fiftyone.devicedetection.examples.shared.DataFileHelper; import fiftyone.devicedetection.hash.engine.onpremise.flowelements.DeviceDetectionHashEngine; @@ -67,6 +68,12 @@ * predictive power using Performance Profile, Graph and Difference and Drift settings. */ public class OfflineProcessing { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-offlineprocessing.java"; + static final Logger logger = LoggerFactory.getLogger(OfflineProcessing.class); // This 51degrees "Lite" file is used from the data repository in // cxx/data, and is downloaded if it is missing @@ -238,6 +245,10 @@ public static void run(String dataFile, InputStream is, OutputStream os) throws } // finish the last YAML document writer.write("...\n"); + // The section is written as comments, so the output stays a + // valid YAML stream. + FindOutMore.writeCommented(new PrintWriter(writer), "# ", + EXAMPLE_TAG, FindOutMore.ON_PREMISE_REPOSITORIES); writer.flush(); logger.info("Finished processing {} records", count); } diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/PerformanceBenchmark.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/PerformanceBenchmark.java index 163e9f8da..cda12774b 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/PerformanceBenchmark.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/PerformanceBenchmark.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.common.testhelpers.LogbackHelper; import fiftyone.devicedetection.DeviceDetectionOnPremisePipelineBuilder; import fiftyone.devicedetection.DeviceDetectionPipelineBuilder; @@ -58,6 +59,12 @@ * for more information about adjusting performance. */ public class PerformanceBenchmark { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-performancebenchmark.java"; + // the default number of threads if one is not provided. public static final int DEFAULT_NUMBER_OF_THREADS = 4; // the number of tests to execute. @@ -106,6 +113,8 @@ public static void main(String[] args) throws Exception { evidenceFilename, numberOfThreads, new PrintWriter(System.out,true)); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.ON_PREMISE_REPOSITORIES); } /** diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/ShareUsageExample.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/ShareUsageExample.java index 50d44c364..bd28e5cfd 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/ShareUsageExample.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/ShareUsageExample.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.examples.shared.DataFileHelper; import fiftyone.pipeline.core.data.FlowData; import fiftyone.pipeline.core.flowelements.Pipeline; @@ -74,6 +75,12 @@ * identifier before running this example.

*/ public class ShareUsageExample { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-shareusageexample.java"; + static final Logger logger = LoggerFactory.getLogger(ShareUsageExample.class); // Evidence file with 20,000 examples, from the data repository in @@ -104,6 +111,8 @@ public static void main(String[] args) throws Exception { File evidenceFile = DataFileHelper.findDataFile(HEADER_EVIDENCE_YML); run(Files.newInputStream(evidenceFile.toPath())); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.CLOUD_REPOSITORIES); } /** diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/TacCloud.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/TacCloud.java index ee5ee1e0b..c5a87bc5a 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/TacCloud.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/TacCloud.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.cloud.data.MultiDeviceDataCloud; import fiftyone.devicedetection.shared.Constants; import fiftyone.devicedetection.shared.DeviceData; @@ -56,6 +57,12 @@ * file tacCloud.xml. */ public class TacCloud { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-taccloud.java"; + // Example values to use when looking up device details from TACs. static final String TAC_1 = "35925406"; static final String TAC_2 = "86386802"; @@ -100,6 +107,10 @@ public static void run(String resourceKey, OutputStream os) throws Exception { analyseTac(TAC_1, pipeline, writer); analyseTac(TAC_2, pipeline, writer); } + // The writer closes the stream it was given, so the section is + // written before it. + FindOutMore.write(writer, EXAMPLE_TAG, + FindOutMore.CLOUD_REPOSITORIES); } } diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/UpdateDataFile.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/UpdateDataFile.java index ebffdef86..eae7ca9fd 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/UpdateDataFile.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/UpdateDataFile.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.devicedetection.DeviceDetectionPipelineBuilder; import fiftyone.devicedetection.examples.shared.DataFileHelper; import fiftyone.devicedetection.shared.testhelpers.KeyUtils; @@ -189,6 +190,12 @@ */ public class UpdateDataFile { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-updatedatafile.java"; + public static final String UPDATE_EXAMPLE_LICENSE_KEY_NAME = "LicenseKey"; public static final String DEFAULT_DATA_FILENAME = System.getProperty("user.dir") + File.separator + ENTERPRISE_HASH_DATA_FILE_NAME; @@ -209,6 +216,8 @@ public static void main(String[] args) throws Exception { String licenseKey = args.length > 0 ? args[0] : null; String dataFilename = args.length > 1 ? args[1] : null; run(dataFilename, licenseKey, true); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.ON_PREMISE_REPOSITORIES); } /** diff --git a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/comparison/Comparer.java b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/comparison/Comparer.java index ba231800a..19b60ceba 100644 --- a/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/comparison/Comparer.java +++ b/examples/device-detection/console/src/main/java/fiftyone/devicedetection/examples/console/comparison/Comparer.java @@ -22,6 +22,7 @@ package fiftyone.devicedetection.examples.console.comparison; +import fiftyone.devicedetection.examples.shared.FindOutMore; import fiftyone.common.testhelpers.LogbackHelper; import fiftyone.devicedetection.examples.console.comparison.Detection.BenchmarkResult; import fiftyone.devicedetection.examples.console.comparison.Detection.Request; @@ -50,6 +51,12 @@ * code has not been tested and it is left to those interested to uncomment and get working. */ public class Comparer { + /** + * This example's tag in the links of its "Find out more" section. + */ + private static final String EXAMPLE_TAG = + "console-src-main-java-fiftyone-devicedetection-examples-console-comparison-comparer.java"; + public static final int DEFAULT_NUMBER_OF_THREADS = 4; public static final int DEFAULT_NUMBER_OF_RESULTS = 2500; @SuppressWarnings("resource") @@ -69,6 +76,8 @@ public static void main(String[] args) throws Exception { DEFAULT_NUMBER_OF_RESULTS, new Reporting.Minimal(), new PrintWriter(System.out, true)); + FindOutMore.write(System.out, EXAMPLE_TAG, + FindOutMore.ON_PREMISE_REPOSITORIES); } /** diff --git a/examples/device-detection/console/src/test/java/fiftyone/devicedetection/examples/console/ExamplesWithoutSettingsTest.java b/examples/device-detection/console/src/test/java/fiftyone/devicedetection/examples/console/ExamplesWithoutSettingsTest.java index aea911082..00f21fca2 100644 --- a/examples/device-detection/console/src/test/java/fiftyone/devicedetection/examples/console/ExamplesWithoutSettingsTest.java +++ b/examples/device-detection/console/src/test/java/fiftyone/devicedetection/examples/console/ExamplesWithoutSettingsTest.java @@ -23,6 +23,7 @@ package fiftyone.devicedetection.examples.console; import fiftyone.devicedetection.examples.shared.DataFileHelper; +import fiftyone.devicedetection.examples.shared.FindOutMore; import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -75,6 +76,7 @@ private static String runWithoutSettings(Main main) throws Exception { } String output = buffer.toString("UTF-8"); out.println(output); + assertFindOutMore(output); return output; } @@ -83,6 +85,21 @@ private static void assertContains(String output, String expected) { output.contains(expected)); } + /** + * Checks that the output has the "Find out more" section after the last + * link to the contact page, and that the section closes with its rule. + * Log lines may still follow the section. + */ + private static void assertFindOutMore(String output) { + int heading = output.lastIndexOf(FindOutMore.HEADING); + assertTrue("The output should have the '" + FindOutMore.HEADING + + "' section after the link to the contact page", + heading > output.lastIndexOf(CONTACT_US)); + assertTrue("The '" + FindOutMore.HEADING + + "' section should end with its rule", + output.indexOf(FindOutMore.RULE, heading) > heading); + } + private static void assertOffer(String output) { assertContains(output, "This example used the free Lite data file."); assertContains(output, CONTACT_US); diff --git a/examples/device-detection/console/src/test/java/fiftyone/devicedetection/examples/console/FindOutMoreCoverageTest.java b/examples/device-detection/console/src/test/java/fiftyone/devicedetection/examples/console/FindOutMoreCoverageTest.java new file mode 100644 index 000000000..f090ac8fe --- /dev/null +++ b/examples/device-detection/console/src/test/java/fiftyone/devicedetection/examples/console/FindOutMoreCoverageTest.java @@ -0,0 +1,74 @@ +/* ********************************************************************* + * 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.console; + +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertTrue; + +/** + * Checks that every console example with a main method writes the "Find out + * more" section, including those that need a key or a paid data file and so + * do not run in ExamplesWithoutSettingsTest. + */ +public class FindOutMoreCoverageTest { + + @Test + public void everyExample_WritesFindOutMore() throws IOException { + Path sources = Paths.get(System.getProperty("basedir", "."), + "src", "main", "java"); + assertTrue(sources + " should hold the examples", + Files.isDirectory(sources)); + List files; + try (Stream walk = Files.walk(sources)) { + files = walk.filter(f -> f.toString().endsWith(".java")) + .collect(Collectors.toList()); + } + List examples = new ArrayList<>(); + List missing = new ArrayList<>(); + for (Path file : files) { + String text = new String(Files.readAllBytes(file), + StandardCharsets.UTF_8); + if (text.contains("static void main(")) { + examples.add(file.getFileName().toString()); + if (text.contains("FindOutMore.write") == false) { + missing.add(file.getFileName().toString()); + } + } + } + assertTrue("No examples were found in " + sources, + examples.isEmpty() == false); + assertTrue("These examples do not write the 'Find out more' " + + "section: " + missing, missing.isEmpty()); + } +} diff --git a/examples/device-detection/shared/src/main/java/fiftyone/devicedetection/examples/shared/FindOutMore.java b/examples/device-detection/shared/src/main/java/fiftyone/devicedetection/examples/shared/FindOutMore.java new file mode 100644 index 000000000..2b8f918f3 --- /dev/null +++ b/examples/device-detection/shared/src/main/java/fiftyone/devicedetection/examples/shared/FindOutMore.java @@ -0,0 +1,185 @@ +/* ********************************************************************* + * 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 java.io.OutputStream; +import java.io.PrintWriter; +import java.util.regex.Pattern; + +/** + * Writes the "Find out more" section that ends every example's output, set + * apart from the example's own output by a rule above and below. It gives + * pages on 51degrees.com, each tagged with the example that wrote it, then + * the repositories behind the example. + */ +public final class FindOutMore { + + /** + * The heading the section starts with. + */ + public static final String HEADING = "Find out more"; + + /** + * The rule above and below the section. + */ + public static final String RULE = + "----------------------------------------------------------------"; + + /** + * The repositories an on-premise example lists, being this SDK, the C + * engines it builds on and the free data files. + */ + public static final String[] ON_PREMISE_REPOSITORIES = { + "https://github.com/51Degrees/51degrees-java", + "https://github.com/51Degrees/51degrees-cxx", + "https://github.com/51Degrees/data" + }; + + /** + * The repositories a cloud example lists. + */ + public static final String[] CLOUD_REPOSITORIES = { + "https://github.com/51Degrees/51degrees-java" + }; + + /** + * Each page's label and its path on 51degrees.com. + */ + private static final String[][] PAGES = { + {"Device Detection", "device-detection"}, + {"Pricing", "pricing"}, + {"Documentation", "documentation/index.html"} + }; + + /** + * What a campaign tag value may hold. + */ + private static final Pattern SLUG = + Pattern.compile("[a-z0-9][a-z0-9._-]*"); + + private FindOutMore() { + } + + /** + * Writes the section. + * @param output where to write, flushed afterwards + * @param example the example's tag, a lower case slug such as + * "console-gettingstartedonprem.java" + * @param repositories the repositories to list, such as + * {@link #ON_PREMISE_REPOSITORIES} + */ + public static void write( + PrintWriter output, + String example, + String... repositories) { + output.println(); + writeLines(output, "", example, repositories); + } + + /** + * Writes the section with each line a comment, for output that has to + * stay valid in its own format, such as a YAML stream. + * @param output where to write, flushed afterwards + * @param comment what starts a comment line, such as "# " + * @param example the example's tag + * @param repositories the repositories to list + */ + public static void writeCommented( + PrintWriter output, + String comment, + String example, + String... repositories) { + writeLines(output, comment, example, repositories); + } + + private static void writeLines( + PrintWriter output, + String start, + String example, + String... repositories) { + output.println(start + RULE); + output.println(start + HEADING); + for (String[] page : PAGES) { + output.println(start + "\t" + page[0] + ": " + + link(page[1], example)); + } + for (String repository : repositories) { + output.println(start + "\t" + repository); + } + output.println(start + RULE); + output.flush(); + } + + /** + * Writes the section to a stream. See + * {@link #write(PrintWriter, String, String...)}. + */ + public static void write( + OutputStream output, + String example, + String... repositories) { + write(new PrintWriter(output), example, repositories); + } + + /** + * The section as HTML, for the end of a web example's page. + * @param example the example's tag + * @param repositories the repositories to list + * @return the markup + */ + public static String html(String example, String... repositories) { + StringBuilder html = new StringBuilder(); + html.append("
\n"); + html.append("

").append(HEADING).append("

\n"); + html.append(" \n
\n"); + return html.toString(); + } + + /** + * The address of a page on 51degrees.com, tagged with the example. + * @param page the path of the page + * @param example the example's tag + * @return the address + * @throws IllegalArgumentException if the tag is not a lower case slug + */ + public static String link(String page, String example) { + if (example == null || SLUG.matcher(example).matches() == false) { + throw new IllegalArgumentException( + "'" + example + "' is not a lower case slug."); + } + return "https://51degrees.com/" + + page + "?utm_source=code&utm_medium=example" + + "&utm_campaign=51degrees-java&utm_content=" + example + + "&utm_term=find-out-more"; + } +} diff --git a/examples/device-detection/shared/src/test/java/fiftyone/devicedetection/examples/shared/FindOutMoreTest.java b/examples/device-detection/shared/src/test/java/fiftyone/devicedetection/examples/shared/FindOutMoreTest.java new file mode 100644 index 000000000..8ff97350a --- /dev/null +++ b/examples/device-detection/shared/src/test/java/fiftyone/devicedetection/examples/shared/FindOutMoreTest.java @@ -0,0 +1,149 @@ +/* ********************************************************************* + * 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.io.PrintWriter; +import java.io.StringWriter; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +public class FindOutMoreTest { + + /** + * The campaign tags common-ci's scripts/utm-lint.ps1 requires of a link + * printed by an example. + */ + private static final Pattern TAGGED = Pattern.compile( + "https://51degrees\\.com/[^?\\s]+\\?utm_source=code" + + "&utm_medium=example&utm_campaign=51degrees-java" + + "&utm_content=[a-z0-9][a-z0-9._-]*" + + "&utm_term=[a-z0-9][a-z0-9._-]*"); + + private static String[] write(String example, String... repositories) { + StringWriter text = new StringWriter(); + FindOutMore.write(new PrintWriter(text), example, repositories); + return text.toString().split("\r?\n"); + } + + /** + * The section is a blank line, a rule, the heading, the three tagged + * pages, the repositories and a closing rule. + */ + @Test + public void write_LinksThenRepositories() { + String[] lines = write("console-example.java", + FindOutMore.ON_PREMISE_REPOSITORIES); + assertEquals(10, lines.length); + assertEquals("", lines[0]); + assertEquals(FindOutMore.RULE, lines[1]); + assertEquals(FindOutMore.HEADING, lines[2]); + assertTrue(lines[3].startsWith("\tDevice Detection: https://51degrees.com/device-detection?")); + assertTrue(lines[4].startsWith("\tPricing: https://51degrees.com/pricing?")); + assertTrue(lines[5].startsWith("\tDocumentation: https://51degrees.com/documentation/index.html?")); + for (int i = 3; i <= 5; i++) { + String link = lines[i].substring(lines[i].indexOf("https://")); + assertTrue(link, TAGGED.matcher(link).matches()); + assertTrue(link, link.contains("&utm_content=console-example.java&")); + } + assertEquals("\thttps://github.com/51Degrees/51degrees-java", lines[6]); + assertEquals("\thttps://github.com/51Degrees/51degrees-cxx", lines[7]); + assertEquals("\thttps://github.com/51Degrees/data", lines[8]); + assertEquals(FindOutMore.RULE, lines[9]); + } + + /** + * A cloud example lists this repository alone. + */ + @Test + public void write_Cloud_ThisRepository() { + String[] lines = write("console-example.java", + FindOutMore.CLOUD_REPOSITORIES); + assertEquals(8, lines.length); + assertEquals("\thttps://github.com/51Degrees/51degrees-java", lines[6]); + } + + /** + * Each line of the commented form starts with the comment marker, and + * there is no blank line, so a YAML stream stays valid. + */ + @Test + public void writeCommented_EveryLineAComment() { + StringWriter text = new StringWriter(); + FindOutMore.writeCommented(new PrintWriter(text), "# ", + "console-example.java", FindOutMore.CLOUD_REPOSITORIES); + String[] lines = text.toString().split("\\r?\\n"); + assertEquals(7, lines.length); + for (String line : lines) { + assertTrue(line, line.startsWith("# ")); + } + assertEquals("# " + FindOutMore.HEADING, lines[1]); + } + + /** + * The HTML form has the heading, each tagged page as a link with its + * ampersands escaped, and each repository as a link. + */ + @Test + public void html_LinksThenRepositories() { + String html = FindOutMore.html("web-example.java", + FindOutMore.ON_PREMISE_REPOSITORIES); + assertTrue(html, html.contains("

" + FindOutMore.HEADING + "

")); + java.util.regex.Matcher href = + Pattern.compile("href=\"([^\"]+)\"").matcher(html); + int pages = 0; + int repositories = 0; + while (href.find()) { + String link = href.group(1); + if (link.startsWith("https://51degrees.com/")) { + assertTrue(link, link.contains("&")); + assertTrue(link, + TAGGED.matcher(link.replace("&", "&")).matches()); + pages++; + } else { + assertTrue(link, link.startsWith("https://github.com/51Degrees/")); + repositories++; + } + } + assertEquals(3, pages); + assertEquals(3, repositories); + } + + /** + * A tag that is not a lower case slug would fail the campaign tag + * convention, so it is refused. + */ + @Test + public void link_NotASlug_Refused() { + assertThrows(IllegalArgumentException.class, + () -> FindOutMore.link("pricing", "Console-Example.java")); + assertThrows(IllegalArgumentException.class, + () -> FindOutMore.link("pricing", "console example")); + assertThrows(IllegalArgumentException.class, + () -> FindOutMore.link("pricing", null)); + } +} diff --git a/examples/device-detection/web/getting-started.cloud/src/main/webapp/WEB-INF/51Degrees-Cloud.xml b/examples/device-detection/web/getting-started.cloud/src/main/webapp/WEB-INF/51Degrees-Cloud.xml index ced491606..a7abb5715 100644 --- a/examples/device-detection/web/getting-started.cloud/src/main/webapp/WEB-INF/51Degrees-Cloud.xml +++ b/examples/device-detection/web/getting-started.cloud/src/main/webapp/WEB-INF/51Degrees-Cloud.xml @@ -25,7 +25,7 @@ - ${TestCloudEndpoint:-http://cloud.51degrees.com/api/v4} + ${TestCloudEndpoint:-https://cloud.51degrees.com/api/v4} - ${TestDataFile:-cxx/data/ip-intelligence/51Degrees-EnterpriseIpiV41.ipi} + ${TestDataFile:-cxx/data/ip-intelligence/51Degrees-LiteV41.ipi} LowMemory IPIntelligenceOnPremiseEngine diff --git a/examples/ip-intelligence/web/getting-started.onprem/src/main/webapp/WEB-INF/html/index.html b/examples/ip-intelligence/web/getting-started.onprem/src/main/webapp/WEB-INF/html/index.html index 2abdface2..afb91cab8 100644 --- a/examples/ip-intelligence/web/getting-started.onprem/src/main/webapp/WEB-INF/html/index.html +++ b/examples/ip-intelligence/web/getting-started.onprem/src/main/webapp/WEB-INF/html/index.html @@ -92,6 +92,7 @@

Location map

${CONTACT_MESSAGE} + ${FIND_OUT_MORE} diff --git a/examples/ip-intelligence/web/getting-started.onprem/src/test/java/fiftyone/ipintelligence/examples/web/GettingStartedWebOnPremTest.java b/examples/ip-intelligence/web/getting-started.onprem/src/test/java/fiftyone/ipintelligence/examples/web/GettingStartedWebOnPremTest.java index e1831b39b..f995a4ea6 100644 --- a/examples/ip-intelligence/web/getting-started.onprem/src/test/java/fiftyone/ipintelligence/examples/web/GettingStartedWebOnPremTest.java +++ b/examples/ip-intelligence/web/getting-started.onprem/src/test/java/fiftyone/ipintelligence/examples/web/GettingStartedWebOnPremTest.java @@ -22,6 +22,7 @@ package fiftyone.ipintelligence.examples.web; +import fiftyone.ipintelligence.examples.shared.FindOutMore; import fiftyone.ipintelligence.examples.shared.DataFileHelper; import org.eclipse.jetty.server.Server; import org.junit.AfterClass; @@ -96,6 +97,9 @@ public void testWebOnPrem() throws Exception { // Assert we got a successful response assertEquals("Expected HTTP 200 OK response", 200, code); assertTrue("Response should not be empty", responseBody.length() > 0); + assertTrue("The page should end with the '" + FindOutMore.HEADING + + "' section", responseBody.contains( + "

" + FindOutMore.HEADING + "

")); } @AfterClass diff --git a/examples/ip-intelligence/web/getting-started.onprem/src/test/java/fiftyone/ipintelligence/examples/web/OptionsFileTest.java b/examples/ip-intelligence/web/getting-started.onprem/src/test/java/fiftyone/ipintelligence/examples/web/OptionsFileTest.java new file mode 100644 index 000000000..047f34bdc --- /dev/null +++ b/examples/ip-intelligence/web/getting-started.onprem/src/test/java/fiftyone/ipintelligence/examples/web/OptionsFileTest.java @@ -0,0 +1,61 @@ +/* ********************************************************************* + * 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.ipintelligence.examples.web; + +import fiftyone.ipintelligence.examples.shared.DataFileHelper; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Checks the options file this web example runs with. + */ +public class OptionsFileTest { + + /** + * With no data file set, the example falls back to the free Lite file, + * which a fresh clone downloads, and not to the Enterprise file, which a + * fresh clone never has. + */ + @Test + public void defaultDataFile_FreeLiteFile() throws IOException { + String xml = new String(Files.readAllBytes(Paths.get( + System.getProperty("basedir", "."), "src", "main", "webapp", + "WEB-INF", "51Degrees-OnPrem.xml")), StandardCharsets.UTF_8); + Matcher dataFile = Pattern.compile( + "\\$\\{TestDataFile:-([^}]*)\\}") + .matcher(xml); + assertTrue("51Degrees-OnPrem.xml should give a default IP Intelligence " + + "data file", dataFile.find()); + assertEquals("cxx/data/" + DataFileHelper.LITE_DATA_FILE_REL_PATH, + dataFile.group(1)); + } +}