Skip to content

Commit d12e32a

Browse files
committed
tests + extract code base from URLs
1 parent d9f715d commit d12e32a

File tree

6 files changed

+187
-18
lines changed

6 files changed

+187
-18
lines changed

test/framework/src/main/java/org/elasticsearch/bootstrap/TestBuildInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111

1212
import java.util.List;
1313

14-
record TestBuildInfo(String componentName, List<TestBuildInfoLocations> locations) {}
14+
record TestBuildInfo(String componentName, List<TestBuildInfoLocation> locations) {}

test/framework/src/main/java/org/elasticsearch/bootstrap/TestBuildInfoLocations.java renamed to test/framework/src/main/java/org/elasticsearch/bootstrap/TestBuildInfoLocation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99

1010
package org.elasticsearch.bootstrap;
1111

12-
record TestBuildInfoLocations(String className, String moduleName) {}
12+
record TestBuildInfoLocation(String className, String moduleName) {}

test/framework/src/main/java/org/elasticsearch/bootstrap/TestBuildInfoParser.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.bootstrap;
1111

12+
import org.elasticsearch.core.SuppressForbidden;
1213
import org.elasticsearch.xcontent.ObjectParser;
1314
import org.elasticsearch.xcontent.ParseField;
1415
import org.elasticsearch.xcontent.XContentFactory;
@@ -17,6 +18,7 @@
1718
import org.elasticsearch.xcontent.XContentType;
1819

1920
import java.io.IOException;
21+
import java.io.InputStream;
2022
import java.net.URL;
2123
import java.util.ArrayList;
2224
import java.util.List;
@@ -43,6 +45,7 @@ private static class Location {
4345
public void moduleName(final String moduleName) {
4446
this.moduleName = moduleName;
4547
}
48+
4649
public void className(final String className) {
4750
this.className = className;
4851
}
@@ -61,7 +64,7 @@ public void locations(final List<Location> locations) {
6164
}
6265

6366
TestBuildInfo build() {
64-
return new TestBuildInfo(name, locations.stream().map(l -> new TestBuildInfoLocations(l.className, l.moduleName)).toList());
67+
return new TestBuildInfo(name, locations.stream().map(l -> new TestBuildInfoLocation(l.className, l.moduleName)).toList());
6568
}
6669
}
6770

@@ -70,18 +73,22 @@ static TestBuildInfo fromXContent(final XContentParser parser) throws IOExceptio
7073
}
7174

7275
// TODO: possibly move it to whoever is calling/using this
73-
static List<TestBuildInfo> parseAllPluginTestBuildInfo(final XContentParser parser) throws IOException {
76+
// TODO: server test build info
77+
static List<TestBuildInfo> parseAllPluginTestBuildInfo() throws IOException {
7478
var xContent = XContentFactory.xContent(XContentType.JSON);
7579
List<TestBuildInfo> pluginsTestBuildInfos = new ArrayList<>();
7680
var resources = TestBuildInfoParser.class.getClassLoader().getResources("/META-INF/es-plugins/");
7781
URL resource;
7882
while ((resource = resources.nextElement()) != null) {
79-
try (var stream = resource.openStream()) {
80-
pluginsTestBuildInfos.add(fromXContent(xContent.createParser(XContentParserConfiguration.EMPTY, stream)));
83+
try (var stream = getStream(resource); var parser = xContent.createParser(XContentParserConfiguration.EMPTY, stream)) {
84+
pluginsTestBuildInfos.add(fromXContent(parser));
8185
}
8286
}
8387
return pluginsTestBuildInfos;
8488
}
8589

86-
// TODO: server test build info
90+
@SuppressForbidden(reason = "URLs from class loader")
91+
private static InputStream getStream(URL resource) throws IOException {
92+
return resource.openStream();
93+
}
8794
}

test/framework/src/main/java/org/elasticsearch/bootstrap/TestScopeResolver.java

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99

1010
package org.elasticsearch.bootstrap;
1111

12+
import org.elasticsearch.core.SuppressForbidden;
1213
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
1314

15+
import java.net.MalformedURLException;
16+
import java.net.URL;
1417
import java.util.HashMap;
1518
import java.util.List;
1619
import java.util.Map;
@@ -19,34 +22,70 @@
1922
record TestScopeResolver(Map<String, PolicyManager.PolicyScope> scopeMap) {
2023
PolicyManager.PolicyScope getScope(Class<?> callerClass) {
2124
var callerCodeSource = callerClass.getProtectionDomain().getCodeSource().getLocation().toString();
22-
return scopeMap.getOrDefault(callerCodeSource, PolicyManager.PolicyScope.unknown(null));
25+
return scopeMap.getOrDefault(callerCodeSource, PolicyManager.PolicyScope.unknown(callerCodeSource));
2326
}
2427

25-
Function<Class<?>, PolicyManager.PolicyScope> createScopeResolver(TestBuildInfo serverBuildInfo, List<TestBuildInfo> pluginsBuildInfo) {
28+
static Function<Class<?>, PolicyManager.PolicyScope> createScopeResolver(
29+
TestBuildInfo serverBuildInfo,
30+
List<TestBuildInfo> pluginsBuildInfo
31+
) {
2632

2733
Map<String, PolicyManager.PolicyScope> scopeMap = new HashMap<>();
28-
for (var pluginBuildInfo: pluginsBuildInfo) {
34+
for (var pluginBuildInfo : pluginsBuildInfo) {
2935
for (var location : pluginBuildInfo.locations()) {
30-
var codeSource = this.getClass().getClassLoader().getResource(location.className());
36+
var codeSource = TestScopeResolver.class.getClassLoader().getResource(location.className());
3137
if (codeSource == null) {
3238
throw new IllegalArgumentException("Cannot locate class [" + location.className() + "]");
3339
}
34-
scopeMap.put(
35-
codeSource.toString(),
36-
PolicyManager.PolicyScope.plugin(pluginBuildInfo.componentName(), location.moduleName())
37-
);
40+
try {
41+
scopeMap.put(
42+
getCodeSource(codeSource, location.className()),
43+
PolicyManager.PolicyScope.plugin(pluginBuildInfo.componentName(), location.moduleName())
44+
);
45+
} catch (MalformedURLException e) {
46+
throw new IllegalArgumentException("Cannot locate class [" + location.className() + "]", e);
47+
}
3848
}
3949
}
4050

4151
for (var location : serverBuildInfo.locations()) {
42-
var codeSource = this.getClass().getClassLoader().getResource(location.className());
43-
if (codeSource == null) {
52+
var classUrl = TestScopeResolver.class.getClassLoader().getResource(location.className());
53+
if (classUrl == null) {
4454
throw new IllegalArgumentException("Cannot locate class [" + location.className() + "]");
4555
}
46-
scopeMap.put(codeSource.toString(), PolicyManager.PolicyScope.server(location.moduleName()));
56+
try {
57+
scopeMap.put(getCodeSource(classUrl, location.className()), PolicyManager.PolicyScope.server(location.moduleName()));
58+
} catch (MalformedURLException e) {
59+
throw new IllegalArgumentException("Cannot locate class [" + location.className() + "]", e);
60+
}
4761
}
4862

4963
var testScopeResolver = new TestScopeResolver(scopeMap);
5064
return testScopeResolver::getScope;
5165
}
66+
67+
private static String getCodeSource(URL classUrl, String className) throws MalformedURLException {
68+
if (isJarUrl(classUrl)) {
69+
return extractJarFileUrl(classUrl).toString();
70+
}
71+
var s = classUrl.toString();
72+
return s.substring(0, s.indexOf(className));
73+
}
74+
75+
private static boolean isJarUrl(URL url) {
76+
return "jar".equals(url.getProtocol());
77+
}
78+
79+
@SuppressWarnings("deprecation")
80+
@SuppressForbidden(reason = "need file spec in string form to extract the inner URL form the JAR URL")
81+
private static URL extractJarFileUrl(URL jarUrl) throws MalformedURLException {
82+
String spec = jarUrl.getFile();
83+
int separator = spec.indexOf("!/");
84+
85+
if (separator == -1) {
86+
throw new MalformedURLException();
87+
}
88+
89+
return new URL(spec.substring(0, separator));
90+
}
5291
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.bootstrap;
11+
12+
import org.elasticsearch.test.ESTestCase;
13+
import org.elasticsearch.xcontent.XContentFactory;
14+
import org.elasticsearch.xcontent.XContentParserConfiguration;
15+
import org.elasticsearch.xcontent.XContentType;
16+
17+
import java.io.IOException;
18+
19+
import static org.elasticsearch.test.LambdaMatchers.transformedItemsMatch;
20+
import static org.hamcrest.Matchers.contains;
21+
import static org.hamcrest.Matchers.is;
22+
23+
public class TestBuildInfoParserTests extends ESTestCase {
24+
public void testSimpleParsing() throws IOException {
25+
26+
var input = """
27+
{
28+
"name": "lang-painless",
29+
"locations": [
30+
{
31+
"class": "Location.class",
32+
"module": "org.elasticsearch.painless"
33+
},
34+
{
35+
"class": "org/objectweb/asm/AnnotationVisitor.class",
36+
"module": "org.objectweb.asm"
37+
},
38+
{
39+
"class": "org/antlr/v4/runtime/ANTLRErrorListener.class",
40+
"module": "org.antlr.antlr4.runtime"
41+
},
42+
{
43+
"class": "org/objectweb/asm/commons/AdviceAdapter.class",
44+
"module": "org.objectweb.asm.commons"
45+
}
46+
]
47+
}
48+
""";
49+
50+
try (var parser = XContentFactory.xContent(XContentType.JSON).createParser(XContentParserConfiguration.EMPTY, input)) {
51+
var testInfo = TestBuildInfoParser.fromXContent(parser);
52+
assertThat(testInfo.componentName(), is("lang-painless"));
53+
assertThat(
54+
testInfo.locations(),
55+
transformedItemsMatch(
56+
TestBuildInfoLocation::moduleName,
57+
contains("org.elasticsearch.painless", "org.objectweb.asm", "org.antlr.antlr4.runtime", "org.objectweb.asm.commons")
58+
)
59+
);
60+
61+
assertThat(
62+
testInfo.locations(),
63+
transformedItemsMatch(
64+
TestBuildInfoLocation::className,
65+
contains(
66+
"Location.class",
67+
"org/objectweb/asm/AnnotationVisitor.class",
68+
"org/antlr/v4/runtime/ANTLRErrorListener.class",
69+
"org/objectweb/asm/commons/AdviceAdapter.class"
70+
)
71+
)
72+
);
73+
}
74+
}
75+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.bootstrap;
11+
12+
import org.elasticsearch.plugins.Plugin;
13+
import org.elasticsearch.test.ESTestCase;
14+
15+
import java.util.List;
16+
17+
import static org.hamcrest.Matchers.is;
18+
19+
public class TestScopeResolverTests extends ESTestCase {
20+
21+
public void testScopeResolverServerClass() {
22+
var testBuildInfo = new TestBuildInfo(
23+
"server",
24+
List.of(new TestBuildInfoLocation("org/elasticsearch/Build.class", "org.elasticsearch.server"))
25+
);
26+
var resolver = TestScopeResolver.createScopeResolver(testBuildInfo, List.of());
27+
28+
var scope = resolver.apply(Plugin.class);
29+
assertThat(scope.componentName(), is("(server)"));
30+
assertThat(scope.moduleName(), is("org.elasticsearch.server"));
31+
}
32+
33+
public void testScopeResolverInternalClass() {
34+
var testBuildInfo = new TestBuildInfo(
35+
"server",
36+
List.of(new TestBuildInfoLocation("org/elasticsearch/Build.class", "org.elasticsearch.server"))
37+
);
38+
var testOwnBuildInfo = new TestBuildInfo(
39+
"test-component",
40+
List.of(new TestBuildInfoLocation("org/elasticsearch/bootstrap/TestBuildInfoParserTests.class", "test-module-name"))
41+
);
42+
var resolver = TestScopeResolver.createScopeResolver(testBuildInfo, List.of(testOwnBuildInfo));
43+
44+
var scope = resolver.apply(this.getClass());
45+
assertThat(scope.componentName(), is("test-component"));
46+
assertThat(scope.moduleName(), is("test-module-name"));
47+
}
48+
}

0 commit comments

Comments
 (0)