You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2013/08/13 03:29:26 UTC
git commit: TAP5-1007: When Tapestry is loading templates or other
files on case-insensitive OSs (Windows) it should trigger an error if the
file name case is incorrect (which will result in a runtime failure on
case-sensitive OSs, such as Linux)
Updated Branches:
refs/heads/master bb255c0a1 -> e16e5fb50
TAP5-1007: When Tapestry is loading templates or other files on case-insensitive OSs (Windows) it should trigger an error if the file name case is incorrect (which will result in a runtime failure on case-sensitive OSs, such as Linux)
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/e16e5fb5
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/e16e5fb5
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/e16e5fb5
Branch: refs/heads/master
Commit: e16e5fb50555545e36960767568ac9520322fc9b
Parents: bb255c0
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Mon Aug 12 18:29:19 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Mon Aug 12 18:29:19 2013 -0700
----------------------------------------------------------------------
.../internal/services/AssetSourceImpl.java | 121 +++++++++++--------
.../internal/services/ContextResource.java | 11 +-
.../ContextResourceSymbolProviderTest.java | 10 +-
.../internal/services/AssetSourceImplTest.java | 20 +--
.../internal/services/ContextResourceTest.java | 12 +-
.../ioc/internal/util/AbstractResource.java | 73 +++++++++++
.../ioc/internal/util/ClasspathResource.java | 7 +-
.../ioc/specs/ClasspathResourceSpec.groovy | 14 +++
8 files changed, 192 insertions(+), 76 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
index ac83ddb..9bcc065 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
@@ -19,6 +19,8 @@ import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.internal.AssetConstants;
import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
+import org.apache.tapestry5.ioc.Invokable;
+import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.annotations.PostInjection;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
@@ -54,13 +56,16 @@ public class AssetSourceImpl extends LockSupport implements AssetSource
private final AtomicBoolean firstWarning = new AtomicBoolean(true);
+ private final OperationTracker tracker;
+
public AssetSourceImpl(ThreadLocale threadLocale,
- Map<String, AssetFactory> configuration, SymbolSource symbolSource, Logger logger)
+ Map<String, AssetFactory> configuration, SymbolSource symbolSource, Logger logger, OperationTracker tracker)
{
this.threadLocale = threadLocale;
this.symbolSource = symbolSource;
this.logger = logger;
+ this.tracker = tracker;
Map<Class, AssetFactory> byResourceClass = CollectionFactory.newMap();
@@ -80,7 +85,8 @@ public class AssetSourceImpl extends LockSupport implements AssetSource
}
@PostInjection
- public void clearCacheWhenResourcesChange(ResourceChangeTracker tracker) {
+ public void clearCacheWhenResourcesChange(ResourceChangeTracker tracker)
+ {
tracker.clearOnInvalidation(cache);
}
@@ -114,79 +120,88 @@ public class AssetSourceImpl extends LockSupport implements AssetSource
return getUnlocalizedAsset(symbolSource.expandSymbols(path));
}
- public Asset getComponentAsset(ComponentResources resources, String path)
+ public Asset getComponentAsset(final ComponentResources resources, final String path)
{
- assert resources != null;
+ assert resources != null;
assert InternalUtils.isNonBlank(path);
- // First, expand symbols:
+ return tracker.invoke(String.format("Resolving '%s' for component %s", path, resources.getCompleteId()
+ ),
+ new Invokable<Asset>()
+ {
+ public Asset invoke()
+ {
+ // First, expand symbols:
- String expanded = symbolSource.expandSymbols(path);
+ String expanded = symbolSource.expandSymbols(path);
- int dotx = expanded.indexOf(':');
+ int dotx = expanded.indexOf(':');
- // We special case the hell out of 'classpath:' so that we can provide warnings today (5.4) and
- // blow up in a useful fashion tomorrow (5.5).
+ // We special case the hell out of 'classpath:' so that we can provide warnings today (5.4) and
+ // blow up in a useful fashion tomorrow (5.5).
- if (dotx > 0 && !expanded.substring(0, dotx).equalsIgnoreCase(AssetConstants.CLASSPATH))
- {
- return getAssetInLocale(resources.getBaseResource(), expanded, resources.getLocale());
- }
+ if (dotx > 0 && !expanded.substring(0, dotx).equalsIgnoreCase(AssetConstants.CLASSPATH))
+ {
+ return getAssetInLocale(resources.getBaseResource(), expanded, resources.getLocale());
+ }
- // No prefix, so implicitly classpath:, or explicitly classpath:
+ // No prefix, so implicitly classpath:, or explicitly classpath:
- String restOfPath = expanded.substring(dotx + 1);
+ String restOfPath = expanded.substring(dotx + 1);
- // This is tricky, because a relative path (including "../") is ok in 5.3, since its just somewhere
- // else on the classpath (though you can "stray" out of the "safe" zone). In 5.4, under /META-INF/assets/
- // it's possible to "stray" out beyond the safe zone more easily, into parts of the classpath that can't be
- // represented in the URL.
+ // This is tricky, because a relative path (including "../") is ok in 5.3, since its just somewhere
+ // else on the classpath (though you can "stray" out of the "safe" zone). In 5.4, under /META-INF/assets/
+ // it's possible to "stray" out beyond the safe zone more easily, into parts of the classpath that can't be
+ // represented in the URL.
+ // Ends with trailing slash:
+ String metaRoot = "META-INF/assets/" + toPathPrefix(resources.getComponentModel().getLibraryName());
- // Ends with trailing slash:
- String metaRoot = "META-INF/assets/" + toPathPrefix(resources.getComponentModel().getLibraryName());
+ String metaPath = metaRoot + (restOfPath.startsWith("/")
+ ? restOfPath.substring(1)
+ : restOfPath);
- String metaPath = metaRoot + (restOfPath.startsWith("/")
- ? restOfPath.substring(1)
- : restOfPath);
+ // Based on the path, metaResource is where it should exist in a 5.4 and beyond world ... unless the expanded
+ // path was a bit too full of ../ sequences, in which case the expanded path is not valid and we adjust the
+ // error we write.
- // Based on the path, metaResource is where it should exist in a 5.4 and beyond world ... unless the expanded
- // path was a bit too full of ../ sequences, in which case the expanded path is not valid and we adjust the
- // error we write.
+ Resource metaResource = findLocalizedResource(null, metaPath, resources.getLocale());
- Resource metaResource = findLocalizedResource(null, metaPath, resources.getLocale());
+ Asset result = getComponentAsset(resources, expanded, metaResource);
- Asset result = getComponentAsset(resources, expanded, metaResource);
+ // This is the best way to tell if the result is an asset for a Classpath resource.
- // This is the best way to tell if the result is an asset for a Classpath resource.
+ Resource resultResource = result.getResource();
- Resource resultResource = result.getResource();
+ if (!resultResource.equals(metaResource))
+ {
+ if (firstWarning.getAndSet(false))
+ {
+ logger.error("Packaging of classpath assets has changed in release 5.4; " +
+ "Assets should no longer be on the main classpath, " +
+ "but should be moved to 'META-INF/assets/' or a sub-folder. Future releases of Tapestry may " +
+ "no longer support assets on the main classpath.");
+ }
- if (!resultResource.equals(metaResource))
- {
- if (firstWarning.getAndSet(false))
- {
- logger.error("Packaging of classpath assets has changed in release 5.4; " +
- "Assets should no longer be on the main classpath, " +
- "but should be moved to 'META-INF/assets/' or a sub-folder. Future releases of Tapestry may " +
- "no longer support assets on the main classpath.");
- }
+ if (metaResource.getFolder().startsWith(metaRoot))
+ {
+ logger.error(String.format("Classpath asset '/%s' should be moved to folder '/%s/'.",
+ resultResource.getPath(),
+ metaResource.getFolder()));
+ } else
+ {
+ logger.error(String.format("Classpath asset '/%s' should be moved under folder '/%s', and the relative path adjusted.",
+ resultResource.getPath(),
+ metaRoot));
+ }
+ }
- if (metaResource.getFolder().startsWith(metaRoot))
- {
- logger.error(String.format("Classpath asset '/%s' should be moved to folder '/%s/'.",
- resultResource.getPath(),
- metaResource.getFolder()));
- } else
- {
- logger.error(String.format("Classpath asset '/%s' should be moved under folder '/%s', and the relative path adjusted.",
- resultResource.getPath(),
- metaRoot));
- }
- }
+ return result;
+ }
+ }
- return result;
+ );
}
private Asset getComponentAsset(ComponentResources resources, String expandedPath, Resource metaResource)
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
index f5337b2..26f22a4 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008, 2010, 2012 The Apache Software Foundation
+// Copyright 2006-2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -63,6 +63,7 @@ public class ContextResource extends AbstractResource
try
{
acquireReadLock();
+
if (!urlResolved)
{
resolveURL();
@@ -104,7 +105,11 @@ public class ContextResource extends AbstractResource
try
{
url = file.toURI().toURL();
+
+ validateURL(url);
+
urlResolved = true;
+
return;
} catch (MalformedURLException ex)
{
@@ -116,8 +121,12 @@ public class ContextResource extends AbstractResource
// URL we get ... but reloading won't work.
url = context.getResource(contextPath);
+
+ validateURL(url);
+
urlResolved = true;
+
} finally
{
downgradeWriteLockToReadLock();
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-core/src/test/java/org/apache/tapestry5/internal/ContextResourceSymbolProviderTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/ContextResourceSymbolProviderTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/ContextResourceSymbolProviderTest.java
index 483658e..b9a772b 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/ContextResourceSymbolProviderTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/ContextResourceSymbolProviderTest.java
@@ -1,4 +1,4 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009-2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -25,8 +25,6 @@ public class ContextResourceSymbolProviderTest extends InternalBaseTestCase
{
private static final String CONTENT = "homer=simpson\r\nmonty=burns";
- private static final String PATH = "bar/foo.properties";
-
@Test
public void access() throws Exception
{
@@ -36,11 +34,11 @@ public class ContextResourceSymbolProviderTest extends InternalBaseTestCase
Context context = mockContext();
- expect(context.getRealFile("/" + PATH)).andReturn(f);
+ expect(context.getRealFile("/bar/" + f.getName())).andReturn(f);
replay();
- ContextResourceSymbolProvider provider = new ContextResourceSymbolProvider(context, PATH);
+ ContextResourceSymbolProvider provider = new ContextResourceSymbolProvider(context, "bar/" + f.getName());
/* test general access */
assertEquals(provider.valueForSymbol("homer"), "simpson");
@@ -64,7 +62,5 @@ public class ContextResourceSymbolProviderTest extends InternalBaseTestCase
fos.write(CONTENT.getBytes());
fos.close();
-
- fos = null;
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetSourceImplTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetSourceImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetSourceImplTest.java
index 904217e..bf57ab9 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetSourceImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetSourceImplTest.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2010, 2012 The Apache Software Foundation
+// Copyright 2006-2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -16,7 +16,9 @@ package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.internal.QuietOperationTracker;
import org.apache.tapestry5.ioc.internal.util.ClasspathResource;
import org.apache.tapestry5.ioc.services.SymbolSource;
import org.apache.tapestry5.ioc.services.ThreadLocale;
@@ -35,6 +37,8 @@ public class AssetSourceImplTest extends InternalBaseTestCase
private final Resource rootResource = new ClasspathResource("/");
+ private final OperationTracker tracker = new QuietOperationTracker();
+
@Test
public void relative_asset()
{
@@ -52,7 +56,7 @@ public class AssetSourceImplTest extends InternalBaseTestCase
replay();
- AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null);
+ AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null, tracker);
// First try creates it:
@@ -82,7 +86,7 @@ public class AssetSourceImplTest extends InternalBaseTestCase
replay();
- AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null);
+ AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null, tracker);
// First try creates it:
@@ -112,7 +116,7 @@ public class AssetSourceImplTest extends InternalBaseTestCase
replay();
- AssetSource source = new AssetSourceImpl(null, configuration, symbolSource, null);
+ AssetSource source = new AssetSourceImpl(null, configuration, symbolSource, null, tracker);
// First try creates it:
@@ -142,7 +146,7 @@ public class AssetSourceImplTest extends InternalBaseTestCase
replay();
- AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null);
+ AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null, tracker);
assertSame(source.getClasspathAsset("org/apache/tapestry5/internal/services/SimpleComponent.properties"), asset);
@@ -167,7 +171,7 @@ public class AssetSourceImplTest extends InternalBaseTestCase
replay();
- AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null);
+ AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null, tracker);
assertSame(source.getAsset(baseResource,
"classpath:org/apache/tapestry5/internal/services/SimpleComponent.properties", Locale.UK), asset);
@@ -189,7 +193,7 @@ public class AssetSourceImplTest extends InternalBaseTestCase
replay();
- AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null);
+ AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null, tracker);
try
{
@@ -214,7 +218,7 @@ public class AssetSourceImplTest extends InternalBaseTestCase
replay();
- AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null);
+ AssetSource source = new AssetSourceImpl(threadLocale, configuration, null, null, tracker);
try
{
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextResourceTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextResourceTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextResourceTest.java
index 08a032b..0823c24 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextResourceTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextResourceTest.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006-2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -27,18 +27,18 @@ public class ContextResourceTest extends InternalBaseTestCase
@Test
public void get_url_no_real_file() throws Exception
{
- String path = "/foo/Bar.txt";
+ String path = "/foo/ContextResourceTest.class";
URL url = getClass().getResource("ContextResourceTest.class");
Context context = mockContext();
expect(context.getRealFile(path)).andReturn(null);
- expect(context.getResource("/foo/Bar.txt")).andReturn(url);
+ expect(context.getResource("/foo/ContextResourceTest.class")).andReturn(url);
replay();
- Resource r = new ContextResource(context, "foo/Bar.txt");
+ Resource r = new ContextResource(context, "foo/ContextResourceTest.class");
assertSame(r.toURL(), url);
@@ -50,7 +50,7 @@ public class ContextResourceTest extends InternalBaseTestCase
{
File f = File.createTempFile("Bar", ".txt");
- String path = "/foo/Bar.txt";
+ String path = "/foo/" + f.getName();
Context context = mockContext();
@@ -58,7 +58,7 @@ public class ContextResourceTest extends InternalBaseTestCase
replay();
- Resource r = new ContextResource(context, "foo/Bar.txt");
+ Resource r = new ContextResource(context, "foo/" + f.getName());
assertEquals(r.toURL(), f.toURL());
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
index feed8cf..23773cd 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
@@ -18,8 +18,10 @@ import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
import java.io.BufferedInputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Locale;
@@ -69,6 +71,11 @@ public abstract class AbstractResource extends LockSupport implements Resource
public final String getFile()
{
+ return extractFile(path);
+ }
+
+ private static String extractFile(String path)
+ {
int slashx = path.lastIndexOf('/');
return path.substring(slashx + 1);
@@ -264,7 +271,9 @@ public abstract class AbstractResource extends LockSupport implements Resource
URL url = toURL();
if (url == null)
+ {
return null;
+ }
return new BufferedInputStream(url.openStream());
}
@@ -273,4 +282,68 @@ public abstract class AbstractResource extends LockSupport implements Resource
* Factory method provided by subclasses.
*/
protected abstract Resource newResource(String path);
+
+ /**
+ * Validates that the URL is correct; at this time, a correct URL is one of:
+ * <ul><li>null</li>
+ * <li>a non-file: URL</li>
+ * <li>a file: URL where the case of the file matches the corresponding path element</li>
+ * </ul>
+ * See <a href="https://issues.apache.org/jira/browse/TAP5-1007">TAP5-1007</a>
+ *
+ * @param url
+ * to validate
+ * @since 5.4
+ */
+ protected void validateURL(URL url)
+ {
+ if (url == null)
+ {
+ return;
+ }
+
+ // Don't have to be concerned with the ClasspathURLConverter since this is intended as a
+ // runtime check during development; it's about ensuring that what works in development on
+ // a case-insensitive file system will work in production on the classpath (or other case sensitive
+ // file system).
+
+ if (!url.getProtocol().equals("file"))
+ {
+ return;
+ }
+
+ File file = toFile(url);
+
+ String expectedFileName = null;
+
+ try
+ {
+ expectedFileName = extractFile(file.getCanonicalPath());
+ } catch (IOException e)
+ {
+ return;
+ }
+
+ String actualFileName = getFile();
+
+ if (actualFileName.equals(expectedFileName))
+ {
+ return;
+ }
+
+ throw new IllegalStateException(String.format("Resource %s does not match the case of the actual file name, '%s'.",
+ this, expectedFileName));
+
+ }
+
+ private File toFile(URL url)
+ {
+ try
+ {
+ return new File(url.toURI());
+ } catch (URISyntaxException ex)
+ {
+ return new File(url.getPath());
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
index a7e613d..84e44f6 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2012 The Apache Software Foundation
+// Copyright 2006-2013 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -77,12 +77,17 @@ public final class ClasspathResource extends AbstractResource
if (!urlResolved)
{
url = classLoader.getResource(getPath());
+
+ validateURL(url);
+
urlResolved = true;
}
} finally
{
downgradeWriteLockToReadLock();
}
+
+
}
@Override
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e16e5fb5/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSpec.groovy
index 2442bf3..a99336d 100644
--- a/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSpec.groovy
@@ -24,6 +24,20 @@ class ClasspathResourceSpec extends Specification {
content(r) == RESOURCE_TXT_CONTENT
}
+ def "case-mismatch on file name is detected"() {
+ def r = new ClasspathResource("$FOLDER/Resource.Txt")
+
+ when:
+
+ r.toURL()
+
+ then:
+
+ IllegalStateException e = thrown()
+
+ e.message == "Resource classpath:org/apache/tapestry5/ioc/internal/util/Resource.Txt does not match the case of the actual file name, 'resource.txt'."
+ }
+
def "expand path from root resource yields same URL as full path"() {
def r = new ClasspathResource("").forFile(PATH)