diff options
author | Eric Anderson <ejona@google.com> | 2017-11-21 13:20:54 -0800 |
---|---|---|
committer | Eric Anderson <ejona@google.com> | 2017-11-22 17:57:46 -0800 |
commit | 2bde25d2d9de8922d3f2e05fb7bd1d9f693f5881 (patch) | |
tree | 5e5943ab1472059e56546643194db5a75f05706c /context | |
parent | 6c6538648ba518eef3230e8e2ab6f39746a8b76b (diff) | |
download | grpc-grpc-java-2bde25d2d9de8922d3f2e05fb7bd1d9f693f5881.tar.gz |
testing: Remove DeadlineSubject
The class is still used internally, so we move it to context's tests for
it to be reused. To avoid a circular dependency with context's tests
depending on core's tests, StaticTestingClassLoader was also moved to
context's tests.
This is driven by a need to modernize DeadlineSubject for newer versions
of Truth, but the newer versions of Truth update Guava. To avoid leaking
the Guava update to all users of grpc-testing, we're removing the
Subject. In our internal tests we can update the Truth dependency with
less issue.
Diffstat (limited to 'context')
-rw-r--r-- | context/build.gradle | 3 | ||||
-rw-r--r-- | context/src/test/java/io/grpc/StaticTestingClassLoader.java | 76 | ||||
-rw-r--r-- | context/src/test/java/io/grpc/testing/DeadlineSubject.java | 129 |
3 files changed, 206 insertions, 2 deletions
diff --git a/context/build.gradle b/context/build.gradle index 67c5e1d32..e13dfe46b 100644 --- a/context/build.gradle +++ b/context/build.gradle @@ -1,7 +1,6 @@ description = 'gRPC: Context' dependencies { - testCompile project(':grpc-testing') - testCompile project(':grpc-core').sourceSets.test.output + testCompile libraries.jsr305 signature "org.codehaus.mojo.signature:java16:1.1@signature" } diff --git a/context/src/test/java/io/grpc/StaticTestingClassLoader.java b/context/src/test/java/io/grpc/StaticTestingClassLoader.java new file mode 100644 index 000000000..590a7c0da --- /dev/null +++ b/context/src/test/java/io/grpc/StaticTestingClassLoader.java @@ -0,0 +1,76 @@ +/* + * Copyright 2017, gRPC Authors All rights reserved. + * + * Licensed 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 io.grpc; + +import com.google.common.base.Preconditions; +import com.google.common.io.ByteStreams; +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Pattern; + +/** + * A class loader that can be used to repeatedly trigger static initialization of a class. A new + * instance is required per test. + */ +public final class StaticTestingClassLoader extends ClassLoader { + private final Pattern classesToDefine; + + public StaticTestingClassLoader(ClassLoader parent, Pattern classesToDefine) { + super(parent); + this.classesToDefine = Preconditions.checkNotNull(classesToDefine, "classesToDefine"); + } + + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + if (!classesToDefine.matcher(name).matches()) { + throw new ClassNotFoundException(name); + } + InputStream is = getResourceAsStream(name.replace('.', '/') + ".class"); + if (is == null) { + throw new ClassNotFoundException(name); + } + byte[] b; + try { + b = ByteStreams.toByteArray(is); + } catch (IOException ex) { + throw new ClassNotFoundException(name, ex); + } + return defineClass(name, b, 0, b.length); + } + + @Override + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + // Reverse normal loading order; check this class loader before its parent + synchronized (getClassLoadingLock(name)) { + Class<?> klass = findLoadedClass(name); + if (klass == null) { + try { + klass = findClass(name); + } catch (ClassNotFoundException e) { + // This ClassLoader doesn't know a class with that name; that's part of normal operation + } + } + if (klass == null) { + klass = super.loadClass(name, false); + } + if (resolve) { + resolveClass(klass); + } + return klass; + } + } +} diff --git a/context/src/test/java/io/grpc/testing/DeadlineSubject.java b/context/src/test/java/io/grpc/testing/DeadlineSubject.java new file mode 100644 index 000000000..3d425a9dc --- /dev/null +++ b/context/src/test/java/io/grpc/testing/DeadlineSubject.java @@ -0,0 +1,129 @@ +/* + * Copyright 2016, gRPC Authors All rights reserved. + * + * Licensed 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 io.grpc.testing; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.common.truth.ComparableSubject; +import com.google.common.truth.FailureStrategy; +import com.google.common.truth.SubjectFactory; +import io.grpc.Deadline; +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +/** + * Propositions for {@link Deadline} subjects. + */ +public final class DeadlineSubject extends ComparableSubject<DeadlineSubject, Deadline> { + private static final SubjectFactory<DeadlineSubject, Deadline> deadlineFactory = + new DeadlineSubjectFactory(); + + public static SubjectFactory<DeadlineSubject, Deadline> deadline() { + return deadlineFactory; + } + + private DeadlineSubject(FailureStrategy failureStrategy, Deadline subject) { + super(failureStrategy, subject); + } + + /** + * Prepares for a check that the subject is deadline within the given tolerance of an + * expected value that will be provided in the next call in the fluent chain. + */ + @CheckReturnValue + public TolerantDeadlineComparison isWithin(final long delta, final TimeUnit timeUnit) { + return new TolerantDeadlineComparison() { + @Override + public void of(Deadline expected) { + Deadline actual = getSubject(); + checkNotNull(actual, "actual value cannot be null. expected=%s", expected); + + // This is probably overkill, but easier than thinking about overflow. + BigInteger actualTimeRemaining = BigInteger.valueOf(actual.timeRemaining(NANOSECONDS)); + BigInteger expectedTimeRemaining = BigInteger.valueOf(expected.timeRemaining(NANOSECONDS)); + BigInteger deltaNanos = BigInteger.valueOf(timeUnit.toNanos(delta)); + if (actualTimeRemaining.subtract(expectedTimeRemaining).abs().compareTo(deltaNanos) > 0) { + failWithRawMessage( + "%s and <%s> should have been within <%sns> of each other", + getDisplaySubject(), + expected, + deltaNanos); + } + } + }; + } + + // TODO(carl-mastrangelo): Add a isNotWithin method once there is need for one. Currently there + // is no such method since there is no code that uses it, and would lower our coverage numbers. + + /** + * A partially specified proposition about an approximate relationship to a {@code deadline} + * subject using a tolerance. + */ + public abstract static class TolerantDeadlineComparison { + + private TolerantDeadlineComparison() {} + + /** + * Fails if the subject was expected to be within the tolerance of the given value but was not + * <i>or</i> if it was expected <i>not</i> to be within the tolerance but was. The expectation, + * subject, and tolerance are all specified earlier in the fluent call chain. + */ + public abstract void of(Deadline expectedDeadline); + + /** + * Do not call this method. + * + * @throws UnsupportedOperationException always + * @deprecated {@link Object#equals(Object)} is not supported on TolerantDeadlineComparison + * If you meant to compare deadlines, use {@link #of(Deadline)} instead. + */ + // Deprecation used to signal visual warning in IDE for the unaware users. + // This method is created as a precaution and won't be removed as part of deprecation policy. + @Deprecated + @Override + public boolean equals(@Nullable Object o) { + throw new UnsupportedOperationException( + "If you meant to compare deadlines, use .of(Deadline) instead."); + } + + /** + * Do not call this method. + * + * @throws UnsupportedOperationException always + * @deprecated {@link Object#hashCode()} is not supported on TolerantDeadlineComparison + */ + // Deprecation used to signal visual warning in IDE for the unaware users. + // This method is created as a precaution and won't be removed as part of deprecation policy. + @Deprecated + @Override + public int hashCode() { + throw new UnsupportedOperationException("Subject.hashCode() is not supported."); + } + } + + private static final class DeadlineSubjectFactory + extends SubjectFactory<DeadlineSubject, Deadline> { + @Override + public DeadlineSubject getSubject(FailureStrategy fs, Deadline that) { + return new DeadlineSubject(fs, that); + } + } +} |