Skip to content

Commit 295eef7

Browse files
BenWhiteheadchingor13
authored andcommitted
Create new google-cloud-conformance-tests module (#5540)
* Create new google-cloud-conformance-tests module A new module where common code related to conformance tests can be shared. * Includes a rudimentary classpath scanner that can be used to locate test definitions. (The firestore test suite currently have 224 test definitions.) * The classpath scanner includes a suite of tests to ensure it can accurately find resources in jar files and directories. * Tests have been ran against the following JVMs on Linux x64 and verified to pass. ``` openjdk version "1.7.0_75" OpenJDK Runtime Environment (build 1.7.0_75-b13) OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode) java version "1.8.0_201" Java(TM) SE Runtime Environment (build 1.8.0_201-b09) Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode) java version "11.0.2" 2019-01-15 LTS Java(TM) SE Runtime Environment 18.9 (build 11.0.2+9-LTS) Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.2+9-LTS, mixed mode) openjdk version "12" 2019-03-19 OpenJDK Runtime Environment (build 12+33) OpenJDK 64-Bit Server VM (build 12+33, mixed mode, sharing) ``` * Review cleanup * format
1 parent 46c39ff commit 295eef7

File tree

7 files changed

+464
-0
lines changed

7 files changed

+464
-0
lines changed

google-cloud-clients/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,12 @@
420420
<artifactId>checker-compat-qual</artifactId>
421421
<version>2.5.5</version>
422422
</dependency>
423+
<dependency>
424+
<groupId>com.google.cloud</groupId>
425+
<artifactId>google-cloud-conformance-tests</artifactId>
426+
<version>0.1.0-SNAPSHOT</version>
427+
<scope>test</scope>
428+
</dependency>
423429
</dependencies>
424430
</dependencyManagement>
425431
<modules>
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<artifactId>google-cloud-conformance-tests</artifactId>
7+
<version>0.1.0-SNAPSHOT</version>
8+
<packaging>jar</packaging>
9+
10+
<parent>
11+
<groupId>com.google.cloud</groupId>
12+
<artifactId>google-cloud-testing</artifactId>
13+
<version>0.98.1-alpha-SNAPSHOT</version><!-- {x-version-update:google-cloud-testing:current} -->
14+
</parent>
15+
16+
<properties>
17+
<protobuf.version>3.7.1</protobuf.version>
18+
</properties>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>junit</groupId>
23+
<artifactId>junit</artifactId>
24+
<scope>test</scope>
25+
</dependency>
26+
<dependency>
27+
<groupId>com.google.truth</groupId>
28+
<artifactId>truth</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>com.google.api.grpc</groupId>
32+
<artifactId>proto-google-cloud-firestore-v1</artifactId>
33+
</dependency>
34+
</dependencies>
35+
36+
<build>
37+
<extensions>
38+
<extension>
39+
<groupId>kr.motd.maven</groupId>
40+
<artifactId>os-maven-plugin</artifactId>
41+
<version>1.6.2</version>
42+
</extension>
43+
</extensions>
44+
<plugins>
45+
<plugin>
46+
<groupId>org.codehaus.mojo</groupId>
47+
<artifactId>build-helper-maven-plugin</artifactId>
48+
<version>3.0.0</version>
49+
<executions>
50+
<execution>
51+
<id>add-main-resource</id>
52+
<phase>generate-resources</phase>
53+
<goals>
54+
<goal>add-resource</goal>
55+
</goals>
56+
<configuration>
57+
<resources>
58+
<resource>
59+
<directory>src/main/proto</directory>
60+
</resource>
61+
</resources>
62+
</configuration>
63+
</execution>
64+
</executions>
65+
</plugin>
66+
</plugins>
67+
</build>
68+
69+
<profiles>
70+
<profile>
71+
<id>gen-conformance-protos</id>
72+
<build>
73+
<plugins>
74+
<plugin>
75+
<groupId>org.xolstice.maven.plugins</groupId>
76+
<artifactId>protobuf-maven-plugin</artifactId>
77+
<version>0.6.1</version>
78+
<executions>
79+
<execution>
80+
<id>generate-main</id>
81+
<goals>
82+
<goal>compile</goal>
83+
</goals>
84+
<phase>generate-sources</phase>
85+
</execution>
86+
<execution>
87+
<id>generate-test</id>
88+
<goals>
89+
<goal>test-compile</goal>
90+
</goals>
91+
<phase>generate-test-sources</phase>
92+
</execution>
93+
</executions>
94+
<configuration>
95+
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
96+
</configuration>
97+
</plugin>
98+
<plugin>
99+
<groupId>com.coveo</groupId>
100+
<artifactId>fmt-maven-plugin</artifactId>
101+
<version>2.9</version>
102+
<executions>
103+
<execution>
104+
<id>format-main</id>
105+
<goals>
106+
<goal>format</goal>
107+
</goals>
108+
<phase>process-sources</phase>
109+
</execution>
110+
<execution>
111+
<id>format-test</id>
112+
<goals>
113+
<goal>format</goal>
114+
</goals>
115+
<phase>process-test-sources</phase>
116+
</execution>
117+
</executions>
118+
<configuration>
119+
<additionalSourceDirectories>
120+
<directory>target/generated-sources/protobuf/java</directory>
121+
</additionalSourceDirectories>
122+
</configuration>
123+
</plugin>
124+
</plugins>
125+
</build>
126+
</profile>
127+
</profiles>
128+
129+
</project>
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
* Copyright 2019 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.conformance;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.net.URISyntaxException;
22+
import java.net.URL;
23+
import java.nio.file.DirectoryStream;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.nio.file.Paths;
27+
import java.util.ArrayList;
28+
import java.util.Enumeration;
29+
import java.util.List;
30+
import java.util.jar.JarEntry;
31+
import java.util.jar.JarFile;
32+
33+
public final class ConformanceTestLocator {
34+
35+
private ConformanceTestLocator() {}
36+
37+
/**
38+
* Given the provided {@link MatchPattern matchPattern} list resources on the classpath starting
39+
* from {@link MatchPattern#getBaseResourcePath() MatchPattern.getBaseResourcePath} and for each
40+
* resources resource test that it is a file and that matches according to {@link
41+
* MatchPattern#matches(String) MatchPattern.matches(String)}.
42+
*
43+
* <p>Resolution of resources paths will be against the current threads context class loader
44+
* ({@link Thread#currentThread()}.{@link Thread#getContextClassLoader() getContextClassLoader()}
45+
*
46+
* @param matchPattern The {@link MatchPattern} to match against
47+
* @return The list of all resources on the classpath that match the specified {@code
48+
* matchPattern}
49+
* @throws IOException If there is an error attempting to read a classpath resource (listing the
50+
* contents of a directory or jar).
51+
* @throws URISyntaxException If there is an error translating a classpath URL into a filesystem
52+
* URI.
53+
*/
54+
public static List<String> findAllResourcePaths(final MatchPattern matchPattern)
55+
throws IOException, URISyntaxException {
56+
return findAllResourcePaths(matchPattern, Thread.currentThread().getContextClassLoader());
57+
}
58+
59+
/**
60+
* Given the provided {@link MatchPattern matchPattern} list resources on the classpath starting
61+
* from {@link MatchPattern#getBaseResourcePath() MatchPattern.getBaseResourcePath} and for each
62+
* resources resource test that it is a file and that matches according to {@link
63+
* MatchPattern#matches(String) MatchPattern.matches(String)}.
64+
*
65+
* <p>Resolution of resources paths will be against the parameter {@code classLoader}
66+
*
67+
* @param matchPattern The {@link MatchPattern} to match against
68+
* @param classLoader The classLoader to scan for resources
69+
* @return The list of all resources on the classpath that match the specified {@code
70+
* matchPattern}
71+
* @throws IOException If there is an error attempting to read a classpath resource (listing the
72+
* contents of a directory or jar).
73+
* @throws URISyntaxException If there is an error translating a classpath URL into a filesystem
74+
* URI.
75+
*/
76+
public static List<String> findAllResourcePaths(
77+
final MatchPattern matchPattern, ClassLoader classLoader)
78+
throws IOException, URISyntaxException {
79+
final List<String> resourcePaths = new ArrayList<>();
80+
final Enumeration<URL> pkgDir = classLoader.getResources(matchPattern.getBaseResourcePath());
81+
while (pkgDir.hasMoreElements()) {
82+
URL url = pkgDir.nextElement();
83+
if (url != null) {
84+
final String scheme = url.getProtocol();
85+
switch (scheme) {
86+
case "file":
87+
final List<String> cf = handleFileScheme(url, matchPattern);
88+
resourcePaths.addAll(cf);
89+
break;
90+
case "jar":
91+
final List<String> cj = handleJarScheme(url, matchPattern);
92+
resourcePaths.addAll(cj);
93+
break;
94+
default:
95+
throw new IllegalStateException("Unable to scan scheme '" + scheme + "'");
96+
}
97+
}
98+
}
99+
100+
return resourcePaths;
101+
}
102+
103+
private static List<String> handleFileScheme(final URL url, MatchPattern mp)
104+
throws IOException, URISyntaxException {
105+
final Path path = Paths.get(url.toURI());
106+
return handleFileScheme(mp, path);
107+
}
108+
109+
private static List<String> handleFileScheme(final MatchPattern mp, final Path path)
110+
throws IOException {
111+
final List<String> resourcePaths = new ArrayList<>();
112+
try (final DirectoryStream<Path> paths = Files.newDirectoryStream(path)) {
113+
for (Path p : paths) {
114+
if (Files.isDirectory(p)) {
115+
resourcePaths.addAll(handleFileScheme(mp, p));
116+
} else {
117+
final String filePath = normalizePath(p);
118+
final String resourcePath =
119+
filePath.substring(filePath.indexOf(mp.getBaseResourcePath()));
120+
if (mp.matches(resourcePath)) {
121+
resourcePaths.add(resourcePath);
122+
}
123+
}
124+
}
125+
}
126+
return resourcePaths;
127+
}
128+
129+
private static List<String> handleJarScheme(final URL url, MatchPattern mp) throws IOException {
130+
final String urlPath = url.getPath();
131+
final String jarPath = urlPath.substring(5, urlPath.indexOf("!"));
132+
133+
final List<String> resourcePaths = new ArrayList<>();
134+
final JarFile jarFile = new JarFile(jarPath);
135+
final Enumeration<JarEntry> jarEntries = jarFile.entries();
136+
while (jarEntries.hasMoreElements()) {
137+
JarEntry je = jarEntries.nextElement();
138+
if (!je.isDirectory() && mp.matches(je.getName())) {
139+
resourcePaths.add(je.getName());
140+
}
141+
}
142+
return resourcePaths;
143+
}
144+
145+
private static String normalizePath(Path p) {
146+
final String s = p.normalize().toString();
147+
if (File.separatorChar == '\\') {
148+
return s.replace('\\', '/');
149+
} else {
150+
return s;
151+
}
152+
}
153+
154+
/**
155+
* Factory method to create a relatively simple {@link MatchPattern}.
156+
*
157+
* @param baseResourcePath The non-null base path to start scanning for resources on the classpath
158+
* @param suffix The non-null suffix to match found elements against for inclusion
159+
* @return A new {@link MatchPattern} where classpath scanning will start from {@code
160+
* baseResourcePath} walking the classpath and matching to elements that end in {@code
161+
* suffix}. Suffix matching is a simple match (non-regex).
162+
*/
163+
public static MatchPattern newMatchPattern(final String baseResourcePath, final String suffix) {
164+
if (baseResourcePath == null) {
165+
throw new IllegalArgumentException("baseResourcePath must be non-null");
166+
}
167+
if (suffix == null || suffix.isEmpty()) {
168+
throw new IllegalArgumentException("suffix must be non-null and non-empty");
169+
}
170+
// when listing the resources from the classpath, a leading slash will result in resources
171+
// not being found. If the baseResourcePath passed in here has a leading slash, detect and
172+
// remove it.
173+
int begin = 0;
174+
if (baseResourcePath.startsWith("/")) {
175+
begin = 1;
176+
}
177+
return new SimpleMatchPattern(baseResourcePath.substring(begin), suffix);
178+
}
179+
180+
public interface MatchPattern {
181+
String getBaseResourcePath();
182+
183+
boolean matches(String resourcePath);
184+
}
185+
186+
private static final class SimpleMatchPattern implements MatchPattern {
187+
private final String baseResourcePath;
188+
private final String suffix;
189+
190+
SimpleMatchPattern(final String baseResourcePath, final String suffix) {
191+
// parameter construction validation is performed in the factory method #newMatchPattern
192+
this.baseResourcePath = baseResourcePath;
193+
this.suffix = suffix;
194+
}
195+
196+
@Override
197+
public String getBaseResourcePath() {
198+
return baseResourcePath;
199+
}
200+
201+
@Override
202+
public boolean matches(final String resourcePath) {
203+
return resourcePath.startsWith(baseResourcePath) && resourcePath.endsWith(suffix);
204+
}
205+
}
206+
}

0 commit comments

Comments
 (0)