You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2020/03/12 13:13:25 UTC

[sling-scriptingbundle-maven-plugin] branch master updated: SLING-9201 - Files in nested resource tree folders should only be analysed in the context of the closest resource type

This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-scriptingbundle-maven-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new 29ad9df  SLING-9201 - Files in nested resource tree folders should only be analysed in the context of the closest resource type
29ad9df is described below

commit 29ad9dfd1b5bc53e4ef6f80974b6ad1bb8d4cfac
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Thu Mar 12 14:13:05 2020 +0100

    SLING-9201 - Files in nested resource tree folders should only be analysed in the context of the closest resource type
---
 .../maven/plugin/ResourceTypeFolderAnalyser.java   | 210 +++++++++------------
 .../maven/plugin/ResourceTypeFolderPredicate.java  |  19 +-
 .../sling/scriptingbundle/maven/plugin/Script.java |  94 +++++++++
 .../maven/plugin/MetadataMojoTest.java             |  13 +-
 .../scriptingbundle/maven/plugin/ScriptTest.java   |  94 +++++++++
 .../foo/depth1/depth2/depth3/depth3-selector.html  |   0
 .../apache/sling/foo/depth1/depth2/depth3/extends  |   1 +
 7 files changed, 311 insertions(+), 120 deletions(-)

diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderAnalyser.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderAnalyser.java
index 1e6645d..0ab251b 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderAnalyser.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderAnalyser.java
@@ -24,7 +24,6 @@ import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -74,33 +73,42 @@ class ResourceTypeFolderAnalyser {
         }
         if (resourceType != null) {
             String resourceTypeLabel = getResourceTypeLabel(resourceType);
-            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(scriptsDirectory.resolve(resourceTypeDirectory))) {
-                for (Path directoryEntry : directoryStream) {
-                    if (Files.isRegularFile(directoryEntry)) {
-                        Path file = directoryEntry.getFileName();
+            String finalResourceType = resourceType;
+            String finalVersion = version;
+            try (DirectoryStream<Path> resourceTypeDirectoryStream = Files.newDirectoryStream(scriptsDirectory.resolve(resourceTypeDirectory))) {
+                resourceTypeDirectoryStream.forEach(entry -> {
+                    if (Files.isRegularFile(entry)) {
+                        Path file = entry.getFileName();
                         if (file != null) {
                             if (MetadataMojo.EXTENDS_FILE.equals(file.toString())) {
-                                processExtendsFile(resourceType, version, directoryEntry, providedCapabilities, requiredCapabilities);
+                                processExtendsFile(finalResourceType, finalVersion, entry, providedCapabilities, requiredCapabilities);
                             } else if (MetadataMojo.REQUIRES_FILE.equals(file.toString())) {
-                                processRequiresFile(directoryEntry, requiredCapabilities);
+                                processRequiresFile(entry, requiredCapabilities);
                             } else {
-                                processScriptFile(resourceTypeDirectory, scriptEngineMappings, directoryEntry, resourceType, version,
+                                processScriptFile(resourceTypeDirectory, scriptEngineMappings, entry, finalResourceType, finalVersion,
                                         resourceTypeLabel, providedCapabilities);
                             }
                         }
-                    } else if (Files.isDirectory(directoryEntry) && !resourceTypeFolderPredicate.test(directoryEntry)) {
-                        try (Stream<Path> subtree = Files.walk(directoryEntry)) {
-                            Iterator<Path> subtreeIterator = subtree.iterator();
-                            while (subtreeIterator.hasNext()) {
-                                Path subtreeEntry = subtreeIterator.next();
-                                if (Files.isRegularFile(subtreeEntry)) {
-                                    processScriptFile(resourceTypeDirectory, scriptEngineMappings, subtreeEntry, resourceType, version,
-                                            resourceTypeLabel, providedCapabilities);
+                    } else if (Files.isDirectory(entry) && !resourceTypeFolderPredicate.test(entry)) {
+                        try (Stream<Path> selectorFilesStream = Files.walk(entry).filter(Files::isRegularFile).filter(file -> {
+                            Path fileParent = file.getParent();
+                            while (!resourceTypeDirectory.equals(fileParent)) {
+                                if (resourceTypeFolderPredicate.test(fileParent)) {
+                                    return false;
                                 }
+                                fileParent = fileParent.getParent();
                             }
+                            return true;
+                        })) {
+                            selectorFilesStream.forEach(
+                                    file -> processScriptFile(resourceTypeDirectory, scriptEngineMappings, file, finalResourceType,
+                                            finalVersion, resourceTypeLabel, providedCapabilities)
+                            );
+                        } catch (IOException e) {
+                            log.error(String.format("Unable to scan folder %s.", entry.toString()), e);
                         }
                     }
-                }
+                });
             } catch (IOException | IllegalArgumentException e) {
                 log.warn(String.format("Cannot analyse folder %s.", scriptsDirectory.resolve(resourceTypeDirectory).toString()), e);
             }
@@ -111,69 +119,68 @@ class ResourceTypeFolderAnalyser {
 
     private void processExtendsFile(@NotNull String resourceType, @Nullable String version, @NotNull Path extendsFile,
                                     @NotNull Set<ProvidedCapability> providedCapabilities,
-                                    @NotNull Set<RequiredCapability> requiredCapabilities)
-            throws IOException {
-        List<String> extendResources = Files.readAllLines(extendsFile, StandardCharsets.UTF_8);
-        if (extendResources.size() == 1) {
-            String extend = extendResources.get(0);
-            if (StringUtils.isNotEmpty(extend)) {
-                String[] extendParts = extend.split(";");
-                String extendedResourceType = extendParts[0];
-                String extendedResourceTypeVersion = extendParts.length > 1 ? extendParts[1] : null;
-                providedCapabilities.add(
-                        ProvidedCapability.builder()
-                                .withResourceType(resourceType)
-                                .withVersion(version)
-                                .withExtendsResourceType(extendedResourceType)
-                                .build());
-                RequiredCapability.Builder requiredBuilder =
-                        RequiredCapability.builder().withResourceType(extendedResourceType);
-                try {
-                    if (extendedResourceTypeVersion != null) {
-                        requiredBuilder.withVersionRange(
-                                VersionRange.valueOf(extendedResourceTypeVersion.substring(extendedResourceTypeVersion.indexOf('=') + 1)));
-                    }
-                } catch (IllegalArgumentException ignored) {
-                    log.warn(String.format("Invalid version range '%s' defined for extended resourceType '%s'" +
-                                    " in file %s.", extendedResourceTypeVersion, extendedResourceType,
-                            extendsFile.toString()));
+                                    @NotNull Set<RequiredCapability> requiredCapabilities) {
+        try {
+            List<String> extendResources = Files.readAllLines(extendsFile, StandardCharsets.UTF_8);
+            if (extendResources.size() == 1) {
+                String extend = extendResources.get(0);
+                if (StringUtils.isNotEmpty(extend)) {
+                    String[] extendParts = extend.split(";");
+                    String extendedResourceType = extendParts[0];
+                    String extendedResourceTypeVersion = extendParts.length > 1 ? extendParts[1] : null;
+                    providedCapabilities.add(
+                            ProvidedCapability.builder()
+                                    .withResourceType(resourceType)
+                                    .withVersion(version)
+                                    .withExtendsResourceType(extendedResourceType)
+                                    .build());
+                    RequiredCapability.Builder requiredBuilder =
+                            RequiredCapability.builder().withResourceType(extendedResourceType);
+                    extractVersionRange(extendsFile, requiredBuilder, extendedResourceTypeVersion);
+                    requiredCapabilities.add(requiredBuilder.build());
                 }
-                requiredCapabilities.add(requiredBuilder.build());
             }
+        } catch (IOException e) {
+            log.error(String.format("Unable to read file %s.", extendsFile.toString()), e);
         }
     }
 
     private void processRequiresFile(@NotNull Path requiresFile,
-                                     @NotNull Set<RequiredCapability> requiredCapabilities)
-            throws IOException {
-        List<String> requiredResourceTypes = Files.readAllLines(requiresFile, StandardCharsets.UTF_8);
-        for (String requiredResourceType : requiredResourceTypes) {
-            if (StringUtils.isNotEmpty(requiredResourceType)) {
-                String[] requireParts = requiredResourceType.split(";");
-                String resourceType = requireParts[0];
-                String version = requireParts.length > 1 ? requireParts[1] : null;
-                RequiredCapability.Builder requiredBuilder =
-                        RequiredCapability.builder().withResourceType(resourceType);
-                try {
-                    if (version != null) {
-                        requiredBuilder.withVersionRange(VersionRange.valueOf(version.substring(version.indexOf('=') + 1)));
-                    }
-                } catch (IllegalArgumentException ignored) {
-                    log.warn(String.format("Invalid version range '%s' defined for required resourceType '%s'" +
-                                    " in file %s.", version, resourceType,
-                            requiresFile.toString()));
+                                     @NotNull Set<RequiredCapability> requiredCapabilities) {
+        try {
+            List<String> requiredResourceTypes = Files.readAllLines(requiresFile, StandardCharsets.UTF_8);
+            for (String requiredResourceType : requiredResourceTypes) {
+                if (StringUtils.isNotEmpty(requiredResourceType)) {
+                    String[] requireParts = requiredResourceType.split(";");
+                    String resourceType = requireParts[0];
+                    String version = requireParts.length > 1 ? requireParts[1] : null;
+                    RequiredCapability.Builder requiredBuilder =
+                            RequiredCapability.builder().withResourceType(resourceType);
+                    extractVersionRange(requiresFile, requiredBuilder, version);
+                    requiredCapabilities.add(requiredBuilder.build());
                 }
-                requiredCapabilities.add(requiredBuilder.build());
             }
+        } catch (IOException e) {
+            log.error(String.format("Unable to read file %s.", requiresFile.toString()), e);
+        }
+    }
+
+    private void extractVersionRange(@NotNull Path requiresFile, @NotNull RequiredCapability.Builder requiredBuilder, String version) {
+        try {
+            if (version != null) {
+                requiredBuilder.withVersionRange(VersionRange.valueOf(version.substring(version.indexOf('=') + 1)));
+            }
+        } catch (IllegalArgumentException ignored) {
+            log.warn(String.format("Invalid version range format %s in file %s.", version, requiresFile.toString()));
         }
     }
 
     private void processScriptFile(@NotNull Path resourceTypeDirectory, @NotNull Map<String, String> scriptEngineMappings,
-                                   @NotNull Path script, @NotNull String resourceType, @Nullable String version,
+                                   @NotNull Path scriptPath, @NotNull String resourceType, @Nullable String version,
                                    @NotNull String resourceTypeLabel, @NotNull Set<ProvidedCapability> providedCapabilities) {
-        Path scriptFile = script.getFileName();
+        Path scriptFile = scriptPath.getFileName();
         if (scriptFile != null) {
-            Path relativeResourceTypeFolder = resourceTypeDirectory.relativize(script);
+            Path relativeResourceTypeFolder = resourceTypeDirectory.relativize(scriptPath);
             int pathSegments = relativeResourceTypeFolder.getNameCount();
             ArrayList<String> selectors = new ArrayList<>();
             if (pathSegments > 1) {
@@ -181,62 +188,33 @@ class ResourceTypeFolderAnalyser {
                     selectors.add(relativeResourceTypeFolder.getName(i).toString());
                 }
             }
-            String[] parts = scriptFile.toString().split("\\.");
-            if (parts.length < 2 || parts.length > 4) {
-                log.warn(String.format("Skipping script %s since it either does not target a script engine or it provides too many " +
-                        "selector parts in its name.", script.toString()));
-                return;
-            }
-            String name = parts[0];
-            String scriptEngineExtension = parts[parts.length - 1];
-            String scriptEngine = scriptEngineMappings.get(scriptEngineExtension);
-            if (StringUtils.isNotEmpty(scriptEngine)) {
-                String requestExtension = null;
-                String requestMethod = null;
-                if (parts.length == 2 && !resourceTypeLabel.equals(name)) {
-                    if (MetadataMojo.METHODS.contains(name)) {
-                        requestMethod = name;
-                    } else if (MimeTypeChecker.hasMimeType(name)) {
-                        requestExtension = name;
-                    } else {
-                        selectors.add(name);
+            String scriptName = scriptFile.toString();
+            Script script = Script.parseScript(scriptName);
+            if (script != null) {
+                String scriptEngine = scriptEngineMappings.get(script.getScriptExtension());
+                if (scriptEngine != null) {
+                    if (script.getName() != null && !resourceTypeLabel.equals(script.getName())) {
+                        selectors.add(script.getName());
                     }
+                    providedCapabilities.add(
+                            ProvidedCapability.builder()
+                                    .withResourceType(resourceType)
+                                    .withVersion(version)
+                                    .withSelectors(selectors)
+                                    .withRequestExtension(script.getRequestExtension())
+                                    .withRequestMethod(script.getRequestMethod())
+                                    .withScriptEngine(scriptEngine)
+                                    .build()
+                    );
+                } else {
+                    log.warn(String.format("Cannot find a script engine mapping for script %s.", scriptPath.toString()));
                 }
-                if (parts.length == 3) {
-                    String middle = parts[1];
-                    if (MetadataMojo.METHODS.contains(middle)) {
-                        requestMethod = middle;
-                    } else if (MimeTypeChecker.hasMimeType(middle)) {
-                        requestExtension = middle;
-                    }
-                    if (!resourceTypeLabel.equals(name)) {
-                        selectors.add(name);
-                    }
-                }
-                if (parts.length == 4){
-                    requestExtension = parts[1];
-                    requestMethod = parts[2];
-                    if (!resourceTypeLabel.equals(name)) {
-                        selectors.add(name);
-                    }
-                }
-                providedCapabilities.add(
-                        ProvidedCapability.builder()
-                                .withResourceType(resourceType)
-                                .withVersion(version)
-                                .withSelectors(selectors)
-                                .withRequestExtension(requestExtension)
-                                .withRequestMethod(requestMethod)
-                                .withScriptEngine(scriptEngine)
-                                .build()
-                );
             } else {
-                log.warn(String.format("Cannot find a script engine mapping for script %s.", script.toString()));
+                log.warn(String.format("File %s does not denote a script.", scriptPath.toString()));
             }
         } else {
-            log.warn(String.format("Skipping path %s since it has 0 elements.", script.toString()));
+            log.warn(String.format("Skipping path %s since it has 0 elements.", scriptPath.toString()));
         }
-
     }
 
     private String getResourceType(@NotNull Path resourceTypeFolder) {
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderPredicate.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderPredicate.java
index a656bd6..b96a56f 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderPredicate.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderPredicate.java
@@ -67,9 +67,22 @@ public class ResourceTypeFolderPredicate implements Predicate<Path> {
                     Path fileName = path.getFileName();
                     if (fileName != null) {
                         String childName = fileName.toString();
-                        String scriptName = childName.indexOf('.') != -1 ? childName.substring(0, childName.indexOf('.')) : null;
-                        if (resourceTypeLabel.equals(scriptName) || MetadataMojo.EXTENDS_FILE.equals(childName) ||
-                                MetadataMojo.METHODS.contains(scriptName)) {
+                        Script script = Script.parseScript(childName);
+                        if (
+                            MetadataMojo.EXTENDS_FILE.equals(childName) ||
+                            (
+                                script != null &&
+                                (
+                                    resourceTypeLabel.equals(script.getName()) ||
+                                    (
+                                        script.getName() == null &&
+                                        (
+                                            "html".equals(script.getRequestExtension()) || "GET".equals(script.getRequestMethod())
+                                        )
+                                    )
+                                )
+                            )
+                        ) {
                             return true;
                         }
                     }
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/Script.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/Script.java
new file mode 100644
index 0000000..cfd4b24
--- /dev/null
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/Script.java
@@ -0,0 +1,94 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.scriptingbundle.maven.plugin;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+class Script {
+
+    private final String name;
+    private final String requestExtension;
+    private final String requestMethod;
+    private final String scriptExtension;
+
+    private Script(@Nullable String name, @Nullable String requestExtension, @Nullable String requestMethod,
+                   @Nullable String scriptExtension) {
+        this.name = name;
+        this.requestExtension = requestExtension;
+        this.requestMethod = requestMethod;
+        this.scriptExtension = scriptExtension;
+    }
+
+    @Nullable
+    String getName() {
+        return name;
+    }
+
+    @Nullable
+    String getRequestExtension() {
+        return requestExtension;
+    }
+
+    @Nullable
+    String getRequestMethod() {
+        return requestMethod;
+    }
+
+    @Nullable
+    String getScriptExtension() {
+        return scriptExtension;
+    }
+
+    @Nullable
+    static Script parseScript(@NotNull String fileName) {
+        String[] parts = fileName.split("\\.");
+        if (parts.length < 2 || parts.length > 4) {
+            return null;
+        }
+        String name = parts[0];
+        String scriptExtension = parts[parts.length - 1];
+        String requestExtension = null;
+        String requestMethod = null;
+        if (parts.length == 2) {
+            if (MetadataMojo.METHODS.contains(name)) {
+                requestMethod = name;
+                name = null;
+            } else if (MimeTypeChecker.hasMimeType(name)) {
+                requestExtension = name;
+                name = null;
+            }
+        }
+        if (parts.length == 3) {
+            String middle = parts[1];
+            if (MetadataMojo.METHODS.contains(middle)) {
+                requestMethod = middle;
+            } else {
+                requestExtension = middle;
+            }
+        }
+        if (parts.length == 4) {
+            requestExtension = parts[1];
+            requestMethod = parts[2];
+        }
+        return new Script(name, requestExtension, requestMethod, scriptExtension);
+    }
+
+
+}
diff --git a/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojoTest.java b/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojoTest.java
index b8f9f97..7586ece 100644
--- a/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojoTest.java
+++ b/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojoTest.java
@@ -71,6 +71,11 @@ public class MetadataMojoTest {
                 ProvidedCapability.builder().withResourceType("org/apache/sling/foo").withScriptEngine("htl").withSelectors(Arrays.asList("depth1"
                         , "depth2", "100")).build(),
 
+                // org/apache/sling/foo/depth1/depth2/depth3
+                ProvidedCapability.builder().withResourceType("org/apache/sling/foo/depth1/depth2/depth3").withExtendsResourceType("org" +
+                        "/apache/sling/bar").build(),
+                ProvidedCapability.builder().withResourceType("org/apache/sling/foo/depth1/depth2/depth3").withScriptEngine("htl").withSelectors(Arrays.asList("depth3-selector")).build(),
+
                 // org.apache.sling.foobar/1.0.0
                 ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withScriptEngine("htl").withVersion("1.0.0").build(),
                 ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withVersion("1.0.0").withExtendsResourceType("org/apache/sling/bar").build(),
@@ -98,7 +103,6 @@ public class MetadataMojoTest {
                 ProvidedCapability.builder().withResourceType("sling").withScriptEngine("htl").build()
         ));
         Set<ProvidedCapability> provided = new HashSet<>(capabilities.getProvidedCapabilities());
-        assertEquals(pExpected.size(), provided.size());
         StringBuilder missingProvided = new StringBuilder();
         for (ProvidedCapability capability : pExpected) {
             boolean removed = provided.remove(capability);
@@ -109,6 +113,13 @@ public class MetadataMojoTest {
         if (missingProvided.length() > 0) {
             fail(missingProvided.toString());
         }
+        StringBuilder extraProvided = new StringBuilder();
+        for (ProvidedCapability capability : provided) {
+            extraProvided.append("Extra provided capability: ").append(capability.toString()).append(System.lineSeparator());
+        }
+        if (extraProvided.length() > 0) {
+            fail(extraProvided.toString());
+        }
 
         Set<RequiredCapability> rExpected = new HashSet<>(Arrays.asList(
                 RequiredCapability.builder().withResourceType("sling/default").withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).build(),
diff --git a/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptTest.java b/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptTest.java
new file mode 100644
index 0000000..6905554
--- /dev/null
+++ b/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptTest.java
@@ -0,0 +1,94 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.scriptingbundle.maven.plugin;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class ScriptTest {
+
+    @Test
+    public void parseTwoPartScriptName() {
+        Script script = Script.parseScript("test.html");
+        assertNotNull(script);
+        assertEquals("test", script.getName());
+        assertNull(script.getRequestExtension());
+        assertNull(script.getRequestMethod());
+        assertEquals("html", script.getScriptExtension());
+    }
+
+    @Test
+    public void parseTwoPartScriptRequestMethod() {
+        Script script = Script.parseScript("GET.html");
+        assertNotNull(script);
+        assertNull(script.getName());
+        assertNull(script.getRequestExtension());
+        assertEquals("GET", script.getRequestMethod());
+        assertEquals("html", script.getScriptExtension());
+    }
+
+    @Test
+    public void parseTwoPartScriptRequestExtension() {
+        Script script = Script.parseScript("html.html");
+        assertNotNull(script);
+        assertNull(script.getName());
+        assertEquals("html", script.getRequestExtension());
+        assertNull(script.getRequestMethod());
+        assertEquals("html", script.getScriptExtension());
+    }
+
+    @Test
+    public void testThreePartScriptNameRequestExtension() {
+        Script script = Script.parseScript("test.txt.html");
+        assertNotNull(script);
+        assertEquals("test", script.getName());
+        assertEquals("txt", script.getRequestExtension());
+        assertNull(script.getRequestMethod());
+        assertEquals("html", script.getScriptExtension());
+    }
+
+    @Test
+    public void testThreePartScriptNameRequestMethod() {
+        Script script = Script.parseScript("test.POST.html");
+        assertNotNull(script);
+        assertEquals("test", script.getName());
+        assertNull(script.getRequestExtension());
+        assertEquals("POST", script.getRequestMethod());
+        assertEquals("html", script.getScriptExtension());
+    }
+
+    @Test
+    public void testFourPartScript() {
+        Script script = Script.parseScript("test.txt.PUT.html");
+        assertNotNull(script);
+        assertEquals("test", script.getName());
+        assertEquals("txt", script.getRequestExtension());
+        assertEquals("PUT", script.getRequestMethod());
+        assertEquals("html", script.getScriptExtension());
+    }
+
+    @Test
+    public void testScriptWithNotEnoughOrTooManyParts() {
+        assertNull(Script.parseScript("extends"));
+        assertNull(Script.parseScript("test.1.txt.PUT.html"));
+    }
+}
diff --git a/src/test/resources/project-1/src/main/scripts/org/apache/sling/foo/depth1/depth2/depth3/depth3-selector.html b/src/test/resources/project-1/src/main/scripts/org/apache/sling/foo/depth1/depth2/depth3/depth3-selector.html
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/project-1/src/main/scripts/org/apache/sling/foo/depth1/depth2/depth3/extends b/src/test/resources/project-1/src/main/scripts/org/apache/sling/foo/depth1/depth2/depth3/extends
new file mode 100644
index 0000000..2d24f10
--- /dev/null
+++ b/src/test/resources/project-1/src/main/scripts/org/apache/sling/foo/depth1/depth2/depth3/extends
@@ -0,0 +1 @@
+org/apache/sling/bar