1
2
3
4 package net.sourceforge.pmd.processor;
5
6 import java.io.ByteArrayInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.List;
12 import java.util.concurrent.atomic.AtomicInteger;
13
14 import org.junit.Assert;
15 import org.junit.Test;
16
17 import net.sourceforge.pmd.PMDConfiguration;
18 import net.sourceforge.pmd.ReportListener;
19 import net.sourceforge.pmd.RuleContext;
20 import net.sourceforge.pmd.RuleSetFactory;
21 import net.sourceforge.pmd.RuleViolation;
22 import net.sourceforge.pmd.lang.ast.Node;
23 import net.sourceforge.pmd.lang.rule.AbstractRule;
24 import net.sourceforge.pmd.renderers.Renderer;
25 import net.sourceforge.pmd.stat.Metric;
26 import net.sourceforge.pmd.util.datasource.DataSource;
27
28 public class MultiThreadProcessorTest {
29
30 @Test
31 public void testRulesThreadSafety() {
32 PMDConfiguration configuration = new PMDConfiguration();
33 configuration.setRuleSets("rulesets/MultiThreadProcessorTest/basic.xml");
34 configuration.setThreads(2);
35 List<DataSource> files = new ArrayList<DataSource>();
36 files.add(new StringDataSource("file1-violation.dummy", "ABC"));
37 files.add(new StringDataSource("file2-foo.dummy", "DEF"));
38
39 SimpleReportListener reportListener = new SimpleReportListener();
40 RuleContext ctx = new RuleContext();
41 ctx.getReport().addListener(reportListener);
42
43 MultiThreadProcessor processor = new MultiThreadProcessor(configuration);
44 RuleSetFactory ruleSetFactory = new RuleSetFactory();
45 processor.processFiles(ruleSetFactory, files, ctx, Collections.<Renderer>emptyList());
46
47
48 Assert.assertEquals("Test rule has not been executed", 2, NotThreadSafeRule.count.get());
49
50 Assert.assertEquals("Missing violation", 1, reportListener.violations.get());
51 }
52
53 private static class StringDataSource implements DataSource {
54 private final String data;
55 private final String name;
56 public StringDataSource(String name, String data) {
57 this.name = name;
58 this.data = data;
59 }
60 @Override
61 public InputStream getInputStream() throws IOException {
62 return new ByteArrayInputStream(data.getBytes("UTF-8"));
63 }
64 @Override
65 public String getNiceFileName(boolean shortNames, String inputFileName) {
66 return name;
67 }
68 }
69
70 public static class NotThreadSafeRule extends AbstractRule {
71 public static AtomicInteger count = new AtomicInteger(0);
72 private boolean hasViolation;
73 @Override
74 public void apply(List<? extends Node> nodes, RuleContext ctx) {
75 count.incrementAndGet();
76
77 if (ctx.getSourceCodeFilename().contains("violation")) {
78 hasViolation = true;
79 } else {
80 letTheOtherThreadRun(10);
81 hasViolation = false;
82 }
83
84 letTheOtherThreadRun(100);
85 if (hasViolation) {
86 addViolation(ctx, nodes.get(0));
87 }
88 }
89 private void letTheOtherThreadRun(int millis) {
90 try {
91 Thread.yield();
92 Thread.sleep(millis);
93 } catch (InterruptedException e) {
94
95 }
96 }
97 }
98
99 private static class SimpleReportListener implements ReportListener {
100 public AtomicInteger violations = new AtomicInteger(0);
101 @Override
102 public void ruleViolationAdded(RuleViolation ruleViolation) {
103 violations.incrementAndGet();
104 }
105 @Override
106 public void metricAdded(Metric metric) {
107 }
108 }
109 }