View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.maven;
6   
7   import com.qulice.spi.Environment;
8   import com.qulice.spi.ResourceValidator;
9   import com.qulice.spi.Validator;
10  import com.qulice.spi.Violation;
11  import java.io.File;
12  import java.util.Collection;
13  import java.util.Collections;
14  import java.util.concurrent.CountDownLatch;
15  import java.util.concurrent.TimeoutException;
16  import java.util.concurrent.atomic.AtomicInteger;
17  import org.apache.maven.monitor.logging.DefaultLog;
18  import org.apache.maven.plugin.MojoFailureException;
19  import org.apache.maven.plugin.logging.Log;
20  import org.apache.maven.project.MavenProject;
21  import org.codehaus.plexus.context.Context;
22  import org.codehaus.plexus.context.DefaultContext;
23  import org.codehaus.plexus.logging.AbstractLogger;
24  import org.codehaus.plexus.logging.Logger;
25  import org.junit.jupiter.api.Assertions;
26  import org.junit.jupiter.api.Test;
27  
28  /**
29   * Test case for {@link CheckMojo} class.
30   * @since 0.3
31   */
32  final class CheckMojoTest {
33  
34      /**
35       * CheckMojo can skip execution if "skip" flag is set.
36       * @throws Exception If something wrong happens inside
37       */
38      @Test
39      void skipsExecutionOnSkipFlag() throws Exception {
40          final CheckMojo mojo = new CheckMojo();
41          final Logger logger = new FakeLogger();
42          final Log log = new DefaultLog(logger);
43          mojo.setLog(log);
44          mojo.setSkip(true);
45          mojo.execute();
46          Assertions.assertEquals("[INFO] Execution skipped", logger.toString());
47      }
48  
49      /**
50       * CheckMojo can set timeout to "forever".
51       */
52      @Test
53      void setsTimeoutToForever() {
54          final CheckMojo mojo = new CheckMojo();
55          mojo.setTimeout("forever");
56          final var validator = new BlockedValidator();
57          final ValidatorsProvider provider = new ValidatorsProviderMocker()
58              .withExternalResource(validator)
59              .mock();
60          mojo.setValidatorsProvider(provider);
61          final MavenProject project = new MavenProject();
62          mojo.setProject(project);
63          mojo.setLog(new DefaultLog(new FakeLogger()));
64          new Thread(
65              () -> {
66                  try {
67                      mojo.execute();
68                  } catch (final MojoFailureException exception) {
69                      throw new IllegalStateException(exception);
70                  }
71              }
72          ).start();
73          validator.await();
74          Assertions.assertEquals(
75              1,
76              validator.count(),
77              "Without the 'await' statement above, this test would run forever"
78          );
79      }
80  
81      /**
82       * CheckMojo can set timeout to "1s".
83       */
84      @Test
85      void setsTimeoutToOneSecond() {
86          final CheckMojo mojo = new CheckMojo();
87          mojo.setTimeout("1s");
88          final ValidatorsProvider provider = new ValidatorsProviderMocker()
89              .withExternalResource(new BlockedValidator())
90              .mock();
91          mojo.setValidatorsProvider(provider);
92          final MavenProject project = new MavenProject();
93          mojo.setProject(project);
94          mojo.setLog(new DefaultLog(new FakeLogger()));
95          Assertions.assertSame(
96              TimeoutException.class,
97              Assertions.assertThrows(
98                  IllegalStateException.class,
99                  mojo::execute,
100                 "Should throw IllegalStateException because of timeout"
101             ).getCause().getClass(),
102             "The cause is expected to be timeout"
103         );
104     }
105 
106     /**
107      * CheckMojo can validate a project using all provided validators.
108      * @throws Exception If something wrong happens inside
109      */
110     @Test
111     void validatesUsingAllProvidedValidators() throws Exception {
112         final CheckMojo mojo = new CheckMojo();
113         final FakeValidator external = new FakeValidator("somename");
114         final FakeResourceValidator rexternal = new FakeResourceValidator(
115             "other"
116         );
117         final FakeMavenValidator internal = new FakeMavenValidator();
118         final ValidatorsProvider provider = new ValidatorsProviderMocker()
119             .withInternal(internal)
120             .withExternal(external)
121             .withExternalResource(rexternal)
122             .mock();
123         mojo.setValidatorsProvider(provider);
124         final MavenProject project = new MavenProject();
125         mojo.setProject(project);
126         mojo.setLog(new DefaultLog(new FakeLogger()));
127         final Context context = new DefaultContext();
128         mojo.contextualize(context);
129         mojo.execute();
130         Assertions.assertEquals(1, internal.count());
131         Assertions.assertEquals(1, external.count());
132         Assertions.assertEquals(1, rexternal.count());
133     }
134 
135     /**
136      * FakeLogger.
137      * A logger that logs in a buffer.
138      *
139      * @since 0.24.1
140      */
141     @SuppressWarnings("PMD.AvoidStringBufferField")
142     private static final class FakeLogger extends AbstractLogger {
143         /**
144          * Log level tags.
145          */
146         private static final String[] TAGS = {
147             "[DEBUG] ",
148             "[INFO] ",
149             "[WARNING] ",
150             "[ERROR] ",
151             "[FATAL ERROR] ",
152         };
153 
154         /**
155          * Logged messages.
156          */
157         private final StringBuilder messages;
158 
159         FakeLogger() {
160             this(1, "fakelogger");
161         }
162 
163         FakeLogger(final int threshold, final String name) {
164             super(threshold, name);
165             this.messages = new StringBuilder();
166         }
167 
168         @Override
169         public void debug(final String message, final Throwable throwable) {
170             if (this.isDebugEnabled()) {
171                 this.messages.append(FakeLogger.TAGS[0].concat(message));
172                 if (throwable != null) {
173                     throwable.printStackTrace(System.out);
174                 }
175             }
176         }
177 
178         @Override
179         public void info(final String message, final Throwable throwable) {
180             if (this.isInfoEnabled()) {
181                 this.messages.append(FakeLogger.TAGS[1].concat(message));
182                 if (throwable != null) {
183                     throwable.printStackTrace(System.out);
184                 }
185             }
186         }
187 
188         @Override
189         public void warn(final String message, final Throwable throwable) {
190             if (this.isWarnEnabled()) {
191                 this.messages.append(FakeLogger.TAGS[2].concat(message));
192                 if (throwable != null) {
193                     throwable.printStackTrace(System.out);
194                 }
195             }
196         }
197 
198         @Override
199         public void error(final String message, final Throwable throwable) {
200             if (this.isErrorEnabled()) {
201                 this.messages.append(FakeLogger.TAGS[3].concat(message));
202                 if (throwable != null) {
203                     throwable.printStackTrace(System.out);
204                 }
205             }
206         }
207 
208         @Override
209         public void fatalError(
210             final String message,
211             final Throwable throwable
212         ) {
213             if (this.isFatalErrorEnabled()) {
214                 this.messages.append(FakeLogger.TAGS[4].concat(message));
215                 if (throwable != null) {
216                     throwable.printStackTrace(System.out);
217                 }
218             }
219         }
220 
221         @Override
222         public Logger getChildLogger(final String name) {
223             return this;
224         }
225 
226         @Override
227         public String toString() {
228             return this.messages.toString();
229         }
230     }
231 
232     /**
233      * FakeValidator
234      * A mock to a Validator.
235      *
236      * @since 0.24.1
237      */
238     private static final class FakeValidator implements Validator {
239         /**
240          * Validator name.
241          */
242         private final String label;
243 
244         /**
245          * Method calls counter.
246          */
247         private final AtomicInteger cnt;
248 
249         FakeValidator(final String name) {
250             this.label = name;
251             this.cnt = new AtomicInteger(0);
252         }
253 
254         @Override
255         public void validate(final Environment env) {
256             this.cnt.incrementAndGet();
257         }
258 
259         @Override
260         public String name() {
261             return this.label;
262         }
263 
264         public int count() {
265             return this.cnt.get();
266         }
267     }
268 
269     /**
270      * BlockedValidator
271      * A mock to a Validator that blocks forever.
272      *
273      * @since 0.24.1
274      */
275     private static final class BlockedValidator implements ResourceValidator {
276 
277         /**
278          * Method calls counter.
279          */
280         private final AtomicInteger cnt;
281 
282         /**
283          * Latch to signal when validation starts.
284          */
285         private final CountDownLatch latch;
286 
287         BlockedValidator() {
288             this.cnt = new AtomicInteger(0);
289             this.latch = new CountDownLatch(1);
290         }
291 
292         @Override
293         public Collection<Violation> validate(final Collection<File> ignore) {
294             this.cnt.incrementAndGet();
295             this.latch.countDown();
296             try {
297                 Thread.sleep(Long.MAX_VALUE);
298             } catch (final InterruptedException ex) {
299                 Thread.currentThread().interrupt();
300             }
301             return Collections.emptyList();
302         }
303 
304         @Override
305         public String name() {
306             return "blocked forever";
307         }
308 
309         public int count() {
310             return this.cnt.get();
311         }
312 
313         public void await() {
314             try {
315                 this.latch.await();
316             } catch (final InterruptedException ex) {
317                 Thread.currentThread().interrupt();
318             }
319         }
320     }
321 
322     /**
323      * FakeResourceValidator.
324      * A mock to a ResourceValidator.
325      *
326      * @since 0.24.1
327      */
328     private static final class FakeResourceValidator
329         implements ResourceValidator {
330         /**
331          * Resource validator name.
332          */
333         private final String label;
334 
335         /**
336          * Method calls counter.
337          */
338         private final AtomicInteger cnt;
339 
340         FakeResourceValidator(final String name) {
341             this.label = name;
342             this.cnt = new AtomicInteger(0);
343         }
344 
345         @Override
346         public Collection<Violation> validate(final Collection<File> files) {
347             this.cnt.incrementAndGet();
348             return Collections.emptyList();
349         }
350 
351         @Override
352         public String name() {
353             return this.label;
354         }
355 
356         public int count() {
357             return this.cnt.get();
358         }
359     }
360 
361     /**
362      * FakeMavenValidator.
363      *
364      * A mock to a MavenValidator.
365      *
366      * @since 0.24.1
367      */
368     private static final class FakeMavenValidator implements MavenValidator {
369         /**
370          * Method calls counter.
371          */
372         private final AtomicInteger cnt;
373 
374         FakeMavenValidator() {
375             this.cnt = new AtomicInteger(0);
376         }
377 
378         @Override
379         public void validate(final MavenEnvironment env) {
380             this.cnt.incrementAndGet();
381         }
382 
383         public int count() {
384             return this.cnt.get();
385         }
386     }
387 }