| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The old implementation is still avaiable under a flag, but we'll
remove it in due course.
Design goal:
- Push as much code in src/interactive as possible to enable reuse
outside of the REPL
- Don't entangle the REPL completion with JLine. The enclosed test
case drives the REPL and autocompletion programatically.
- Don't hard code UI choices, like how to render symbols or
how to filter candidates.
When completion is requested, we wrap the entered code into the
same "interpreter wrapper" synthetic code as is done for regular
execution. We then start a throwaway instance of the presentation
compiler, which takes this as its one and only source file, and
has a classpath formed from the REPL's classpath and the REPL's
output directory (by default, this is in memory).
We can then typecheck the tree, and find the position in the synthetic
source corresponding to the cursor location. This is enough to use
the new completion APIs in the presentation compiler to prepare
a list of candidates.
We go to extra lengths to allow completion of partially typed
identifiers that appear to be keywords, e.g `global.def` should offer
`definitions`.
Two secret handshakes are included; move the the end of the line,
type `// print<TAB>` and you'll see the post-typer tree.
`// typeAt 4 6<TAB>` shows the type of the range position within
the buffer.
The enclosed unit test exercises most of the new functionality.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The presentation compiler currently demands that all interaction
is performed by asynchronous submission of work items, which are
queued and executed on the presentation compiler thread.
This is fairly inconvenient if you are a known-single-threaded client
that is trying to use the compiler from your own thread.
This commit adds an option to disable "assertCorrectThread" to better
support this use case.
|
| |
| |
| |
| |
| |
| |
| |
| | |
When invoking a higher-order method and the value passed for the
SAM type is either a function literal or a parameter of the callsite
method, inline the higher-order method into the callee.
This is a first version, the heuristics will be refined further.
|
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Before this change, with `-Yopt-warnings:at-inline-failed` or with
the default options, the inliner would emit inline warnings for failed
attempts of callsites that are not annotated @inline.
A test for this change follows in a later commit, after enabling the
new inliner heuristic for higher-order methods.
|
| | |
|
| |
| |
| |
| |
| |
| | |
By default, `-Xxml:-coalescing` as of 2.12.
This commit corrects the default for future versions.
|
|\ \
| | |
| | | |
SI-9396 Runner computes path only once
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Change the classpath URL list in the runner settings to a lazy val.
Also clean up PathResolver's use of settings.classpath so
that the default is defined in one place, namely in settings,
where it can also be overridden.
The previous definition in both places was the same, namely,
`sys.env.getOrElse("CLASSPATH", ".")`, but the history of the
code path is fraught.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Remove some unnecessary flags files
- neg/t4425.flags
- run/blame_eye_triple_eee-double.flags
- run/blame_eye_triple_eee-float.flags
Force tests that use -optimize to GenASM
- neg/sealed-final-neg.flags
- pos/inline-access-levels.flags
- pos/inliner2.flags
- pos/sealed-final.flags
- pos/t3420.flags
- pos/t8410.flags
- run/constant-optimization.flags
- run/dead-code-elimination.flags
- run/elidable-opt.flags
- run/finalvar.flags
- run/icode-reader-dead-code.scala
- run/optimizer-array-load.flags
- run/synchronized.flags
- run/t3509.flags
- run/t3569.flags
- run/t4285.flags
- run/t4935.flags
- run/t5789.scala
- run/t6188.flags
- run/t7459b-optimize.flags
- run/t7582.flags
- run/t7582b.flags
- run/t8601.flags
- run/t8601b.flags
- run/t8601c.flags
- run/t8601d.flags
- run/t8601e.flags
- run/t9003.flags
Move some tests to the new optimizer
- run/classfile-format-51.scala
- run/classfile-format-52.scala
- run/run-bug4840.flags
- run/t2106.flags
- run/t6102.flags
|
|/ / |
|
|\ \
| | |
| | | |
SI-9377 ScalaVersion init no longer fails if versionless
|
| | |
| | |
| | |
| | | |
Simplify and sweeten the message.
|
| | |
| | |
| | |
| | |
| | |
| | | |
If the version string was empty, ScalaVersion would
indignantly refuse to initialize. Now it takes a missing
property as "none".
|
|/ /
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Add a setting to take a custom Reporter.
Example of reporter that discounts deprecations for purposes of
(not) failing the build:
```
import scala.tools.nsc.Settings
import scala.tools.nsc.reporters.ConsoleReporter
import scala.reflect.internal.util._
class MyReporter(ss: Settings) extends ConsoleReporter(ss) {
var deprecationCount = 0
override def warning(pos: Position, msg: String): Unit = {
if (msg contains "is deprecated") deprecationCount += 1
super.warning(pos, msg)
}
override def hasWarnings: Boolean = count(WARNING) - deprecationCount > 0
override def reset() = { deprecationCount = 0 ; super.reset() }
}
```
Invoked as:
```
$ scalac -toolcp . -Xreporter myrep.MyReporter -Xfatal-warnings -deprecation test.scala
test.scala:8: warning: class C in package tester is deprecated: Don't use me
Console println s"${new C}"
^
one warning found
```
where the reporter class is in the current directory, placed on the tool class path.
Also flush on early-reported errors.
|
|\| |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
When an indylambda closure is allocated and invoked within the same
method, rewrite the invocation to the implementation method.
This works for any indylambda / SAM type, not only Scala functions.
However, the Scala compiler (under -Xexperimental) currently desugars
function literals for non-FunctionN types to an anonymous class during
typer.
No testing yet, waiting for FunctionN to become SAMs first.
The feature requires scala-java8-compat to be on the classpath and a
number of compiler flags:
-Ydelambdafy:method -Ybackend:GenBCode -Yopt:closure-elimination -target:jvm-1.8
➜ scala git:(opt/closureInlining) ant -Dscala-java8-compat.package=1 -Dlocker.skip=1
➜ scala git:(opt/closureInlining) cd sandbox
➜ sandbox git:(opt/closureInlining) cat Fun.java
public interface Fun<T> {
T apply(T x);
}
➜ sandbox git:(opt/closureInlining) javac Fun.java
➜ sandbox git:(opt/closureInlining) cat Test.scala
class C {
val z = "too"
def f = {
val kap = "me! me!"
val f: Tuple2[String, String] => String = (o => z + kap + o.toString)
f(("a", "b"))
}
def g = {
val f: Int => String = x => x.toString
f(10)
}
def h = {
val f: Fun[Int] = x => x + 100 // Java SAM, requires -Xexperimental, will create an anonymous class in typer
f(10)
}
def i = {
val l = 10l
val f: (Long, String) => String = (x, s) => s + l + z + x
f(20l, "n")
}
def j = {
val f: Int => Int = x => x + 101 // specialized
f(33)
}
}
➜ sandbox git:(opt/closureInlining) ../build/quick/bin/scalac -target:jvm-1.8 -Yopt:closure-elimination -Ydelambdafy:method -Ybackend:GenBCode -Xexperimental -cp ../build/quick/scala-java8-compat:. Test.scala
➜ sandbox git:(opt/closureInlining) asm -a C.class
➜ sandbox git:(opt/closureInlining) cat C.asm
[...]
public g()Ljava/lang/String;
L0
INVOKEDYNAMIC apply()Lscala/compat/java8/JFunction1; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)Ljava/lang/Object;,
// handle kind 0x6 : INVOKESTATIC
C.C$$$anonfun$2$adapted(Ljava/lang/Object;)Ljava/lang/String;,
(Ljava/lang/Object;)Ljava/lang/String;,
3,
1,
Lscala/Serializable;.class,
0
]
CHECKCAST scala/Function1
L1
ASTORE 1
L2
ALOAD 1
BIPUSH 10
INVOKESTATIC scala/runtime/BoxesRunTime.boxToInteger (I)Ljava/lang/Integer;
ASTORE 2
POP
ALOAD 2
INVOKESTATIC C.C$$$anonfun$2$adapted (Ljava/lang/Object;)Ljava/lang/String;
CHECKCAST java/lang/String
L3
ARETURN
[...]
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Switch the defaults of `-Ydelambdafy` and `-Ybackend`.
Rewrite t6288b-jump-position test - no more icode
Don't crash GenBCode beyond JVM code size limits
A similar patch is in GenASM, see 3fa2c97
Fix check files for GenBCode / delambdafy:method defaults
Force copy propagation test to ASM, see SI-9364
Force inline-ex-handlers test to GenASM, see SI-9364
Move t6613 test to pending - still broken in GenBCode
Adding a `flags` file with `-Ybackend:GenASM` doesn't seem to have
the desired effect.
SI-6613 is re-opened.
Force a few tests to GenASM, see SI-9364
|
|\| |
|
| | |
|
| |\
| | |
| | | |
Nullness Analysis for GenBCode
|
| | | |
|
| |/ |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
- Require Java 8 in ant build
- use -source 1.8 and -target 1.8 for javac
- Default scalac's -target to `jvm-1.8`, ignore and deprecate attempts
to use `jvm-1.{6.7}`
- Remove fragile javap-app test. The feature itself is slated for removal.
- Remove obsolete Java6 checkfile
- Adapt DCE tests
- Remove deprecated/redundant -target:jvm-1.6 from flags where the
intent was to trigger generation of stack map frames.
- Remove tests with -target:jvm-1.5 that tested without stack
map frames
- Ignore OpenJDK JVM warnings (via test/[files|scaladoc]/filters).
|
|\| |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Previously, the flag caused any elidable to be elided.
This commit simply sets -Xelide-below to ASSERTION + 1.
The flag is useful because there's no mnemonic for specifying
the magic constant as an option argument. `-Xelide-below ASSERTION`
means asserts are enabled.
|
|\| |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Preserve current behavior (no PCData nodes, only Text)
for 2.11.
The coalescing flag is on if enabled or if source level
is not 2.12 and no flag was supplied.
The subtle change is that adjacent Text nodes are thereby
coalesced. This happens in the presence of CData sections,
but this is at the discretion of the parser anyway.
Also, no PCData nodes are emitted under coalescing, even
if there were no sibling text nodes. That is the correct
behavior: the general idea is that coalescing mode says,
I only want to deal with text.
|
| |
| |
| |
| | |
Verbose option help and test tweak.
|
| |
| |
| |
| |
| |
| |
| |
| |
| | |
As long as Scala does XML literals, there is probably parsing
behavior worth configuring.
Therefore, the umbrella option is promoted to `-Xxml`.
It was tempting to make it `-XML`, but we resisted.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
XML Parser uses `scala.xml.PCData`.
A compiler flag `-Yxml:coalescing`, analogous to
`DocumentBuilderFactory.setCoalescing`, turns `PCData`
nodes into `Text` nodes and coalesces sibling text nodes.
This change also fixes parse errors such as rejecting a
sequence of CDATA sections.
A sequence of "top level" nodes are not coalesced.
```
scala> <a><b/>start<![CDATA[hi & bye]]><c/>world<d/>stuff<![CDATA[red & black]]></a>
res0: scala.xml.Elem = <a><b/>start<![CDATA[hi & bye]]><c/>world<d/>stuff<![CDATA[red & black]]></a>
scala> :replay -Yxml:coalescing
Replaying: <a><b/>start<![CDATA[hi & bye]]><c/>world<d/>stuff<![CDATA[red & black]]></a>
res0: scala.xml.Elem = <a><b/>starthi & bye<c/>world<d/>stuffred & black</a>
```
|
|\| |
|
| |
| |
| |
| |
| |
| | |
This behavior is confusing and also problematic for writing partest
tests: CI passes -optimize, which negates the -Ybackend:GenBCode entry
in a flags file.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Many backend components don't have access to the compiler instance
holding the settings.
Before this change, individual settings required in these parts of the
backend were made available as members of trait BTypes, implemented
in the subclass BTypesFromSymbols (which has access to global).
This change exposes a single value of type ScalaSettings in the super
trait BTypes, which gives access to all compiler settings.
|
| |
| |
| |
| |
| | |
Introduces a stress-test mode "everything" in which the inliner tries
to inline every calliste that can be statically resolved.
|
|\| |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Issue precise warnings when the inliner fails to inline or analyze a
callsite. Inline failures may have various causes, for example because
some class cannot be found on the classpath when building the call
graph. So we need to store problems that happen early in the optimizer
(when building the necessary data structures, call graph, ClassBTypes)
to be able to report them later in case the inliner accesses the
related data.
We use Either to store these warning messages. The commit introduces
an implicit class `RightBiasedEither` to make Either easier to use for
error propagation. This would be subsumed by a biased either in the
standard library (or could use a Validation).
The `info` of each ClassBType is now an Either. There are two cases
where the info is not available:
- The type info should be parsed from a classfile, but the class
cannot be found on the classpath
- SI-9111, the type of a Java source originating class symbol cannot
be completed
This means that the operations on ClassBType that query the info now
return an Either, too.
Each Callsite in the call graph now stores the source position of the
call instruction. Since the call graph is built after code generation,
we build a map from invocation nodes to positions during code gen and
query it when building the call graph.
The new inliner can report a large number of precise warnings when a
callsite cannot be inlined, or if the inlining metadata cannot be
computed precisely, for example due to a missing classfile.
The new -Yopt-warnings multi-choice option allows configuring inliner
warnings.
By default (no option provided), a one-line summary is issued in case
there were callsites annotated @inline that could not be inlined.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Building the call graph and running the inliner require all code to
be reachable (in terms of ASM's data flow analysis / interpreter).
After removing unreachable code, empty exception handlers need to be
removed for correctness (see comment in LocalOpt.methodOptimizations).
Removing a live exception handler renders its handler unreachable and
therefore requires running another round of removing unreachable code.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Inlining decisions will be taken by analyzing the ASM bytecode. This
commit adds tools to build a call graph representation that can be
used for these decisions.
The call graph is currently built by considering method descriptors of
callsite instructions. It will become more precise by using data flow
analyses.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The goal of this commit is to allow the new inliner (in GenBCode,
coming soon) to inline methods generated by the GenASM backend of
2.11.6.
The ScalaInlineInfo attribute is added to every classfile generated
by GenASM. It contains metadata about the class and its methods that
will be used by the new inliner.
Storing this metadata to the classfile prevents the need to look up a
class symbol for a certain class file name, a process that is known to
be brittle due to name mangling. Also, some symbols are not exactly
the same when originating in a class being compiled or an unpickled
one. For example, method symbols for mixed-in members are only added
to classes being compiled.
The classfile attribute is relatively small, because all strings it
references (class internal names, method names, method descriptors)
would exist anyway in the constant pool. It just adds a few references
and bits for each method in the classfile.
Jar sizes before:
480142 scala-actors.jar
15531408 scala-compiler.jar
5543249 scala-library.jar
4663078 scala-reflect.jar
785953 scalap.jar
After:
490491 scala-actors.jar (102.1%)
15865500 scala-compiler.jar (102.1%)
5722504 scala-library.jar (103.2%)
4788370 scala-reflect.jar (102.7%)
805890 scalap.jar (102.5%)
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
An -Xlint:stars-align warning for the case of patterns
with at least one "fixed" component and a varargs component.
Warn if the fixed patterns don't exactly align with the fixed
value components, such that a sequence wildcard aligns exactly
with the varargs component (either a T* parameter in a case class
or a Seq[T] in an extractor result).
This addresses the case of the xml.Elem extractor, which does
not correspond to the Elem class constructor. One can be fooled
into supplying an extra field for extraction.
Vanilla extractors of type `Option[Seq[_]]` are unaffected by
this flag. It's OK to ask for `case X(a, b, c)` in the expectation
that three results are forthcoming. There is no semantic confusion
over where the varargs begin.
|
|/
|
|
|
|
|
|
|
|
|
|
|
|
| |
Probably it was unintended to accept "2.." and "2.-11.7".
This commit makes it a bit more regular and also accepts arbitrary
text after the dash in the build string, including newlines.
Since the number parts must be numbers, accept only digits.
That also disallows "2.+11.7", which could be misconstrued as
some sort of range.
As before, the special build string prefixes "rc" and "m" are
case-insensitive, but "FINAL" is not.
|
|\
| |
| | |
Fix many typos in docs and comments
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
This commit corrects many typos found in scaladocs, comments and
documentation. It should reduce a bit number of PRs which fix one
typo.
There are no changes in the 'real' code except one corrected name of
a JUnit test method and some error messages in exceptions. In the case
of typos in other method or field names etc., I just skipped them.
Obviously this commit doesn't fix all existing typos. I just generated
in IntelliJ the list of potential typos and looked through it quickly.
|
|/
|
|
|
|
| |
This was disabled by mistake. Settings are still a challenge.
This fixes the bcode-delambdafy-method build
(https://scala-webapps.epfl.ch/jenkins/view/2.11.x/job/scala-nightly-genbcode-2.11.x/)
|
|
|
|
|
|
|
|
|
|
|
| |
This commit addresses code review comments.
The flat classpath is no longer the default classpath representation.
It was the default one just for the test purposes. For now it's not
desirable to make it permanently the default representation.
ZipAndJarFileLookupFactory is marked as sealed - it should help to
limit the ways of creating flat classpath instances for zips and jars.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The goal of these changes is to add possibility to:
- compare an efficiency and a content of both cp implementations
(ClassPathImplComparator)
- examine the memory consumption by creating a lot of globals using
a specified classpath (ClassPathMemoryConsumptionTester) - it can be
considered as e.g. some approximation of ScalaPresentationCompilers
in Scala IDE when working with many projects
ClassPathMemoryConsumptionTester is placed in main (I mean not test)
sources so thanks to that it has properly, out of the box configured
boot classpath etc. and it's easy to use it, e.g.:
scala scala.tools.nsc.ClassPathMemoryConsumptionTester
-YclasspathImpl:<implementation_to_test> -cp <some_cp>
-sourcepath <some_sp> -requiredInstances 50 SomeFileToCompile.scala
At the end it waits for the "exit" command so there can be used some
profiler like JProfiler to look how the given implementation behaves.
Also flat classpath implementation is set as a default one to test it
on Jenkins. This particular change must be reverted when all tests
will pass because for now it's not desirable to make it permanently
the default representation.
|
|
|
|
|
|
| |
There's added -YdisableFlatCpCaching option to ScalaSettings which
allows user to disable caching of flat representation of classpath
elements.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit integrates with the compiler the whole flat classpath
representation build next to the recursive one as an alternative.
From now flat classpath really works and can be turned on. There's
added flag -YclasspathImpl with two options: recursive (the default
one) and flat.
It was needed to make the dynamic dispatch to the particular
classpath representation according to the chosen type of a classpath
representation.
There's added PathResolverFactory which is used instead of a concrete
implementation of a path resolver. It turned out that only a small
subset of path resolvers methods is used outside this class in Scala
sources. Therefore, PathResolverFactory returns an instance of a base
interface PathResolverResult providing only these used methods.
PathResolverFactory in combination with matches in some other places
ensures that in all places using classpath we create/get the proper
representation.
Also the classPath method in Global is modified to use the dynamic
dispatch. This is very important change as a return type changed to
the base ClassFileLookup providing subset of old ClassPath public
methods. It can be problematic if someone was using in his project
the explicit ClassPath type or public methods which are not provided
via ClassFileLookup. I tested flat classpath with sbt and Scala IDE
and there were no problems. Also was looking at sources of some other
projects like e.g. Scala plugin for IntelliJ and there shouldn't be
problems, I think, but it would be better to check these changes
using the community build.
Scalap's Main.scala is changed to be able to use both implementations
and also to use flags related to the classpath implementation.
The classpath invalidation is modified to work properly with the old
(recursive) classpath representation after changes made in a Global.
In the case of the attempt to use the invalidation for the flat cp it
just throws exception with a message that the flat one currently
doesn't support the invalidation. And also that's why the partest's
test for the invalidation has been changed to use (always) the old
implementation. There's added an adequate comment with TODO to this
file.
There's added partest test generating various dependencies
(directories, zips and jars with sources and class files) and testing
whether the compilation and further running an application works
correctly, when there are these various types of entries specified as
-classpath and -sourcepath. It should be a good approximation of real
use cases.
|
|\
| |
| | |
FSC server port selected by argument
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Option "port" limits compile server lookup and start to given port.
Normally fsc will start a compile server in a random port if no server
is yet running. This can be problematic with firewalls and/or remote
compile servers. Option "port" should not be confused with option
"server" which looks for a compile server in given host and port and
fails if such server is not found.
Automatic tests for command line user interface do not exist at all.
Thus, adding a test for one new option would require designing a whole
new testing method.
|