-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInterfaceImplementorTest.java
More file actions
200 lines (176 loc) · 7.53 KB
/
InterfaceImplementorTest.java
File metadata and controls
200 lines (176 loc) · 7.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package info.kgeorgiy.java.advanced.implementor;
import info.kgeorgiy.java.advanced.base.BaseTest;
import info.kgeorgiy.java.advanced.implementor.examples.basic.InterfaceWithDefaultMethod;
import info.kgeorgiy.java.advanced.implementor.examples.basic.InterfaceWithStaticMethod;
import info.kgeorgiy.java.advanced.implementor.examples.full.InterfaceWithoutMethods;
import info.kgeorgiy.java.advanced.implementor.standard.basic.Accessible;
import info.kgeorgiy.java.advanced.implementor.standard.basic.Descriptor;
import info.kgeorgiy.java.advanced.implementor.standard.basic.Logger;
import info.kgeorgiy.java.advanced.implementor.standard.basic.RandomAccess;
import info.kgeorgiy.java.advanced.implementor.standard.full.*;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestWatcher;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class InterfaceImplementorTest extends BaseTest {
private String methodName;
@Rule
public TestWatcher watcher = watcher(description -> methodName = description.getMethodName());
@Test
public void test01_constructor() {
assertConstructor(Impler.class);
}
protected void assertConstructor(final Class<?>... ifaces) {
final Class<?> token = loadClass();
for (final Class<?> iface : ifaces) {
Assert.assertTrue(token.getName() + " should implement " + iface.getName() + " interface", iface.isAssignableFrom(token));
}
checkConstructor("public default constructor", token);
}
@Test
public void test02_methodlessInterfaces() throws IOException {
test(false, RandomAccess.class, InterfaceWithoutMethods.class);
}
@Test
public void test03_standardInterfaces() throws IOException {
test(false, Accessible.class, AccessibleAction.class, SDeprecated.class);
}
@Test
public void test04_extendedInterfaces() throws IOException {
test(false, Descriptor.class, CachedRowSet.class, DataInput.class, DataOutput.class, Logger.class);
}
@Test
public void test05_standardNonInterfaces() throws IOException {
test(true, void.class, String[].class, int[].class, String.class, boolean.class);
}
@Test
public void test06_java8Interfaces() throws IOException {
test(false, InterfaceWithStaticMethod.class, InterfaceWithDefaultMethod.class);
}
protected void test(final boolean shouldFail, final Class<?>... classes) throws IOException {
final Path root = getRoot();
try {
implement(shouldFail, root, classes);
if (!shouldFail) {
compile(root, classes);
check(root, classes);
}
} finally {
clean(root);
}
}
private Path getRoot() {
return Paths.get(methodName);
}
protected static URLClassLoader getClassLoader(final Path root) {
try {
return new URLClassLoader(new URL[]{root.toUri().toURL()});
} catch (final MalformedURLException e) {
throw new AssertionError(e);
}
}
private void compile(final Path root, final Class<?>... classes) {
final List<String> files = new ArrayList<>();
for (final Class<?> token : classes) {
files.add(getFile(root, token).toString());
}
compileFiles(root, files);
}
private void compileFiles(final Path root, final List<String> files) {
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
Assert.assertNotNull("Could not find java compiler, include tools.jar to classpath", compiler);
final List<String> args = new ArrayList<>();
args.addAll(files);
args.add("-cp");
args.add(root + File.pathSeparator + System.getProperty("java.class.path"));
final int exitCode = compiler.run(null, null, null, args.toArray(new String[args.size()]));
Assert.assertEquals("Compiler exit code", 0, exitCode);
}
private void clean(final Path root) throws IOException {
if (!Files.exists(root)) {
return;
}
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
private void implement(final boolean shouldFail, final Path root, final Class<?>... classes) {
Impler implementor;
try {
implementor = createCUT();
} catch (final Exception e) {
e.printStackTrace();
Assert.fail("Instantiation error");
implementor = null;
}
for (final Class<?> clazz : classes) {
try {
implement(root, implementor, clazz);
Assert.assertTrue("You may not implement " + clazz, !shouldFail);
} catch (final ImplerException e) {
if (shouldFail) {
return;
}
throw new AssertionError("Error implementing " + clazz, e);
} catch (final Throwable e) {
throw new AssertionError("Error implementing " + clazz, e);
}
final Path file = getFile(root, clazz);
Assert.assertTrue("Error implementing clazz: File '" + file + "' not found", Files.exists(file));
}
}
protected void implement(final Path root, final Impler implementor, final Class<?> clazz) throws ImplerException {
implementor.implement(clazz, root);
}
private Path getFile(final Path root, final Class<?> clazz) {
final String path = clazz.getCanonicalName().replace(".", "/") + "Impl.java";
return root.resolve(path).toAbsolutePath();
}
private void check(final Path root, final Class<?>... classes) {
final URLClassLoader loader = getClassLoader(root);
for (final Class<?> token : classes) {
check(loader, token);
}
}
protected static void check(final URLClassLoader loader, final Class<?> token) {
final String name = token.getCanonicalName() + "Impl";
try {
System.err.println("Loading class " + name);
final Class<?> impl = loader.loadClass(name);
if (token.isInterface()) {
Assert.assertTrue(name + " should implement " + token, Arrays.asList(impl.getInterfaces()).contains(token)) ;
} else {
Assert.assertEquals(name + " should extend " + token, token, impl.getSuperclass()) ;
}
Assert.assertFalse(name + " should not be abstract", Modifier.isAbstract(impl.getModifiers()));
Assert.assertFalse(name + " should not be interface", Modifier.isInterface(impl.getModifiers()));
} catch (final ClassNotFoundException e) {
throw new AssertionError("Error loading class " + name, e);
}
}
}