1 package org.apache.maven.plugins.shade.resource.rule;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import static java.lang.annotation.ElementType.METHOD;
23 import static java.lang.annotation.RetentionPolicy.RUNTIME;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.Target;
34 import java.nio.charset.StandardCharsets;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.Map;
38 import java.util.jar.JarEntry;
39 import java.util.jar.JarInputStream;
40 import java.util.jar.JarOutputStream;
41
42 import org.apache.maven.plugins.shade.relocation.Relocator;
43 import org.apache.maven.plugins.shade.resource.ResourceTransformer;
44 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
45 import org.codehaus.plexus.component.configurator.converters.ConfigurationConverter;
46 import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup;
47 import org.codehaus.plexus.component.configurator.converters.lookup.DefaultConverterLookup;
48 import org.codehaus.plexus.component.configurator.expression.DefaultExpressionEvaluator;
49 import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
50 import org.codehaus.plexus.configuration.PlexusConfiguration;
51 import org.junit.rules.TestRule;
52 import org.junit.runner.Description;
53 import org.junit.runners.model.Statement;
54
55 public class TransformerTesterRule implements TestRule
56 {
57 @Override
58 public Statement apply( final Statement base, final Description description )
59 {
60 return new Statement()
61 {
62 @Override
63 public void evaluate() throws Throwable
64 {
65 final TransformerTest spec = description.getAnnotation( TransformerTest.class );
66 if ( spec == null )
67 {
68 base.evaluate();
69 return;
70 }
71
72 final Map<String, String> jar;
73 try
74 {
75 final ResourceTransformer transformer = createTransformer(spec);
76 visit(spec, transformer);
77 jar = captureOutput(transformer);
78 }
79 catch ( final Exception ex )
80 {
81 if ( Exception.class.isAssignableFrom( spec.expectedException() ) )
82 {
83 assertTrue(
84 ex.getClass().getName(),
85 spec.expectedException().isAssignableFrom( ex.getClass() ) );
86 return;
87 }
88 else
89 {
90 throw ex;
91 }
92 }
93 asserts(spec, jar);
94 }
95 };
96 }
97
98 private void asserts( final TransformerTest spec, final Map<String, String> jar)
99 {
100 if ( spec.strictMatch() && jar.size() != spec.expected().length )
101 {
102 fail( "Strict match test failed: " + jar );
103 }
104 for ( final Resource expected : spec.expected() )
105 {
106 final String content = jar.get( expected.path() );
107 assertNotNull( expected.path(), content );
108 assertTrue(
109 expected.path() + ", expected=" + expected.content() + ", actual=" + content,
110 content.replace( System.lineSeparator(), "\n" ) .matches( expected.content() ) );
111 }
112 }
113
114 private Map<String, String> captureOutput(final ResourceTransformer transformer ) throws IOException
115 {
116 final ByteArrayOutputStream out = new ByteArrayOutputStream();
117 try ( final JarOutputStream jar = new JarOutputStream( out ) )
118 {
119 transformer.modifyOutputStream( jar );
120 }
121
122 final Map<String, String> created = new HashMap<>();
123 try ( final JarInputStream jar = new JarInputStream( new ByteArrayInputStream( out.toByteArray() ) ) )
124 {
125 JarEntry entry;
126 while ( ( entry = jar.getNextJarEntry() ) != null )
127 {
128 created.put( entry.getName(), read( jar ) );
129 }
130 }
131 return created;
132 }
133
134 private void visit( final TransformerTest spec, final ResourceTransformer transformer ) throws IOException
135 {
136 for ( final Resource resource : spec.visited() )
137 {
138 if ( transformer.canTransformResource( resource.path() ))
139 {
140 transformer.processResource(
141 resource.path(),
142 new ByteArrayInputStream( resource.content().getBytes(StandardCharsets.UTF_8) ),
143 Collections.<Relocator>emptyList() );
144 }
145 }
146 }
147
148 private String read(final JarInputStream jar) throws IOException
149 {
150 final StringBuilder builder = new StringBuilder();
151 final byte[] buffer = new byte[512];
152 int read;
153 while ( (read = jar.read(buffer) ) >= 0 )
154 {
155 builder.append( new String( buffer, 0, read ) );
156 }
157 return builder.toString();
158 }
159
160 private ResourceTransformer createTransformer(final TransformerTest spec)
161 {
162 final ConverterLookup lookup = new DefaultConverterLookup();
163 try
164 {
165 final ConfigurationConverter converter = lookup.lookupConverterForType( spec.transformer() );
166 final PlexusConfiguration configuration = new DefaultPlexusConfiguration( "configuration" );
167 for ( final Property property : spec.configuration() )
168 {
169 configuration.addChild( property.name(), property.value() );
170 }
171 return ResourceTransformer.class.cast(
172 converter.fromConfiguration( lookup, configuration, spec.transformer(), spec.transformer(),
173 Thread.currentThread().getContextClassLoader(),
174 new DefaultExpressionEvaluator() ) );
175 }
176 catch (final ComponentConfigurationException e)
177 {
178 throw new IllegalStateException(e);
179 }
180 }
181
182
183
184
185 @Target(METHOD)
186 @Retention(RUNTIME)
187 public @interface TransformerTest
188 {
189
190
191
192 Resource[] visited();
193
194
195
196
197 Resource[] expected();
198
199
200
201
202 boolean strictMatch() default true;
203
204
205
206
207 Class<?> transformer();
208
209
210
211
212 Property[] configuration();
213
214
215
216
217 Class<?> expectedException() default Object.class;
218 }
219
220 @Target(METHOD)
221 @Retention(RUNTIME)
222 public @interface Property
223 {
224 String name();
225 String value();
226 }
227
228 @Target(METHOD)
229 @Retention(RUNTIME)
230 public @interface Resource
231 {
232 String path();
233 String content();
234 }
235 }