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
|
// SPDX-License-Identifier: Apache-2.0
package firrtl.jqf
import collection.JavaConverters._
import java.io.{File, FileNotFoundException, IOException, PrintWriter}
import java.net.MalformedURLException
import edu.berkeley.cs.jqf.fuzz.junit.GuidedFuzzing
import edu.berkeley.cs.jqf.fuzz.repro.ReproGuidance
import edu.berkeley.cs.jqf.instrument.InstrumentingClassLoader
case class JQFReproOptions(
classpath: Seq[String] = null,
testClassName: String = null,
testMethod: String = null,
input: File = null,
logCoverage: Option[File] = None,
excludes: Seq[String] = Seq.empty,
includes: Seq[String] = Seq.empty,
printArgs: Boolean = false
)
object JQFRepro {
final def main(args: Array[String]): Unit = {
val parser = new scopt.OptionParser[JQFReproOptions]("JQF-Repro") {
opt[String]("classpath")
.required()
.unbounded()
.action((x, c) => c.copy(classpath = x.split(":")))
.text("the classpath to instrument and load the test class from")
opt[String]("testClassName")
.required()
.unbounded()
.action((x, c) => c.copy(testClassName = x))
.text("the full class path of the test class")
opt[String]("testMethod")
.required()
.unbounded()
.action((x, c) => c.copy(testMethod = x))
.text("the method of the test class to run")
opt[File]("input")
.required()
.unbounded()
.action((x, c) => c.copy(input = x))
.text("input file or directory to reproduce test case(s)")
opt[File]("logCoverage")
.unbounded()
.action((x, c) => c.copy(logCoverage = Some(x)))
.text("output file to dump coverage info")
opt[Seq[String]]("excludes")
.unbounded()
.action((x, c) => c.copy(excludes = x))
.text("comma-separated list of FQN prefixes to exclude from coverage instrumentation")
opt[Seq[String]]("includes")
.unbounded()
.action((x, c) => c.copy(includes = x))
.text("comma-separated list of FQN prefixes to forcibly include, even if they match an exclude")
opt[Unit]("printArgs")
.unbounded()
.action((_, c) => c.copy(printArgs = true))
.text("whether to print the args to each test case")
}
parser.parse(args, JQFReproOptions()) match {
case Some(opts) => execute(opts)
case _ => System.exit(1)
}
}
def execute(opts: JQFReproOptions): Unit = {
// Configure classes to instrument
if (opts.excludes.nonEmpty) {
System.setProperty("janala.excludes", opts.excludes.mkString(","))
}
if (opts.includes.nonEmpty) {
System.setProperty("janala.includes", opts.includes.mkString(","))
}
val loader = try {
new InstrumentingClassLoader(
opts.classpath.toArray,
getClass().getClassLoader())
} catch {
case e: MalformedURLException =>
throw new JQFException("Could not get project classpath", e)
}
// If a coverage dump file was provided, enable logging via system property
if (opts.logCoverage.isDefined) {
System.setProperty("jqf.repro.logUniqueBranches", "true")
}
// If args should be printed, set system property
if (opts.printArgs) {
System.setProperty("jqf.repro.printArgs", "true")
}
if (!opts.input.exists() || !opts.input.canRead()) {
throw new JQFException("Cannot find or open file " + opts.input)
}
val (guidance, result) = try {
val guidance = new ReproGuidance(opts.input, null)
val result = GuidedFuzzing.run(opts.testClassName, opts.testMethod, loader, guidance, System.out)
guidance -> result
} catch {
case e: ClassNotFoundException => throw new JQFException("Could not load test class", e);
case e: IllegalArgumentException => throw new JQFException("Bad request", e);
case e: FileNotFoundException => throw new JQFException("File not found", e);
case e: RuntimeException => throw new JQFException("Internal error", e);
}
// If a coverage dump file was provided, then dump coverage
if (opts.logCoverage.isDefined) {
val coverageSet = guidance.getBranchesCovered()
assert(coverageSet != null) // Should not happen if we set the system property above
val sortedCoverage = coverageSet.asScala.toSeq.sorted
try {
val covOut = new PrintWriter(opts.logCoverage.get)
sortedCoverage.foreach(covOut.println(_))
} catch {
case e: IOException =>
throw new JQFException("Could not dump coverage info.", e)
}
}
if (!result.wasSuccessful()) {
throw new JQFException("Test case produces a failure.")
}
}
}
|