Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Temporarily insource Scalacheck #5244

Merged
merged 2 commits into from Jun 28, 2016

Conversation

retronym
Copy link
Member

This is a temporary measure until we release Scala
2.12.0. It means we are able to release milestones,
and RCs of Scala without needing a public release of
Scalacheck. While we've never had to wait very long
for these in the past (Thanks, Rickard!) we'd like
to spare the maintainer some work betwen now and 2.12.0.

After we release Scala 2.12.0, we'll revert to a binary
dependency on the standard Scalacheck.

I've had to remove scalacheck as a SBT test framework
in our build. We don't use it directly as such (instead,
it is used indirectly through partest --scalacheck),
and it's test discovery (which we expect to return nothing)
fails after re-STARR-ing due to an unsolved problem with
SBT's testLoader including either STARR or sbt-launch.jar
on the classpath used to discover and spawn tests.

For the record, I tried the following to no avail:

// Two modifications are needed from the stock SBT configuration in order to exclude STARR
// from the classloader that performs test discovery.
//   - We make `isManagedVersion` hold by providing an explicit Scala version, in order to go into the desired
//     branch in `createTestLoader`
//   - We remove STARR from the classloader of the scala instance
def fixTestLoader = testLoader := {
  val s = scalaInstance.value
  val scalaInstance1 =
    new ScalaInstance(s.version, appConfiguration.value.provider.scalaProvider.loader(), s.libraryJar, s.compilerJar, s.extraJars, Some(s.actualVersion))
  assert(scalaInstance1.isManagedVersion)
  TestFramework.createTestLoader(Attributed.data(fullClasspath.value), scalaInstance1, IO.createUniqueDirectory(taskTemporaryDirectory.value))
}

@scala-jenkins scala-jenkins added this to the 2.12.0-RC1 milestone Jun 23, 2016
@@ -550,6 +549,7 @@ lazy val junit = project.in(file("test") / "junit")
fork in Test := true,
libraryDependencies ++= Seq(junitDep, junitIntefaceDep, jolDep),
testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"),
testFrameworks -= new TestFramework("org.scalacheck.ScalaCheckFramework"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-=! 🎉

@retronym
Copy link
Member Author

retronym commented Jun 24, 2016

Locally, I'm seeing a problen in nan-ordering-test, which amounts to this unexplained variation

Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_91).
Type in expressions for evaluation. Or try :help.

scala> def test(f1: Float, f2: Float) = (implicitly[Numeric[Float]].lt(f1, f2), f1 < f2)
test: (f1: Float, f2: Float)(Boolean, Boolean)

scala> test(Float.NaN, 0f)
res0: (Boolean, Boolean) = (false,false)

vs

Welcome to Scala 2.12.0-20160624-065100-be7ba72 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_91).

scala> def test(f1: Float, f2: Float) = (implicitly[Numeric[Float]].lt(f1, f2), f1 < f2)
test: (f1: Float, f2: Float)(Boolean, Boolean)

scala> test(Float.NaN, 0f)
res1: (Boolean, Boolean) = (true,false)

2.12.0-M4 has yet another idea:

scala> test(Float.NaN, 0f)
res2: (Boolean, Boolean) = (true,true)

@adriaanm
Copy link
Contributor

Are we compiling scalacheck with the right compiler?

@retronym
Copy link
Member Author

It is compiled with STARR in the SBT build. Not 100% sure about the Ant build.

@retronym
Copy link
Member Author

Test failure here:

! Parallel collections.ParVector[Int].padTos must be equal: Falsified after 1 passed tests.
> Labels of failing property: 
smaller
> ARG_0: (List(),ParVector(0),0)
> ARG_0_ORIGINAL: (WrappedArray(0),ParVector(0),1)

@adriaanm
Copy link
Contributor

I think this mix of compilers is the problem. It seems to me the proposal to go with a private published scalacheck from the bootstrap build for one commit is the easier solution.

@adriaanm
Copy link
Contributor

adriaanm commented Jun 24, 2016

It is compiled with STARR in the SBT build. Not 100% sure about the Ant build.

So could the test failures be explained by the fact that scalacheck is compiled with STARR, while the tests are compiled with quick? The failures could be a result of different super call semantics, right?

@retronym
Copy link
Member Author

This PR is not based on the trait-statics PR. I don't think there is a material difference between STARR and quick in super calls. But anything is possible...

@adriaanm
Copy link
Contributor

Right, but we have changed other things about the encoding, like #5085

@retronym
Copy link
Member Author

I believe that the scalacheck tests might been neutered since the change to remove unnecessary forwarders. They seem to rely on mixin in a main method from trait Props into each object Test. We had a similar problem with JUnit test cases which we added a special case to the compiler to support.

@retronym
Copy link
Member Author

#5207 is probably related, too.

@sjrd
Copy link
Member

sjrd commented Jun 24, 2016

We had a similar problem with JUnit test cases which we added a special case to the compiler to support.

By "support" you mean display a warning? It's a good step, but I wouldn't call it "support".

We do provide actual support in a compiler plugin, though: https://github.com/scala-js/scala-2.12-junit-mixin-plugin
Still needs documentation, but basically it plugs in after mixin to add the necessary bridges for JUnit 4. And it's totally platform-agnostic, so it also works in Scala.js projects.

@retronym
Copy link
Member Author

RIght, I think the NaN problem makes sense: the implementation in Ordering[Float] will be incorrect until we recompile it with a new STARR that includes @lrytz fix for the NaN regression. But we compile the nan-ordering test case itself with quick. We could move that test to disabled and make a ticket to re-enable it after M5.

I just rebased this on top the latest 2.12.x, which includes the switch to SBT based PR validation. That change does not skip locker, so at least the NaN problem should be gone.

@retronym
Copy link
Member Author

@sjrd We may yet need to add the forwarders back in as we're facing a non-neglible performance hit without them. 😦

@retronym
Copy link
Member Author

The reported failure in the padTo scalacheck is misleading. Scalacheck by default attempt to shrink the falsification. However, in that test, this independentally shrinks the collections in the random (Seq[T], Seq[T], Int) datapoint, which breaks the assumption that the sequences contain the same elements (as constructed in collectionPairsWithLengths.

We could avoid the problem by using forAllNoShrink, rather than forall, or instead performing the collection copy (fromSeq) within the test itself.

To troubleshoot further, I'm using forAllNoShrink locally. Maybe the underlying problem is random test data is too large in some cases and is hitting an OOME.

@retronym
Copy link
Member Author

retronym commented Jun 27, 2016

The root problem (that is being masked by the incorrect shrinking) is that this throws an unsupported operation error in 2.11 and 2.12

collection.parallel.immutable.ParVector(-3,-2,-1).padTo(4, 1)
java.lang.UnsupportedOperationException
    at scala.collection.parallel.immutable.Repetition.seq(package.scala:23)
    at scala.collection.parallel.immutable.Repetition.seq(package.scala:19)
    at scala.collection.parallel.ParSeqLike$class.patch(ParSeqLike.scala:222)
    at scala.collection.parallel.immutable.ParVector.patch(ParVector.scala:38)
    at scala.collection.parallel.ParSeqLike$class.padTo(ParSeqLike.scala:256)
    at scala.collection.parallel.immutable.ParVector.padTo(ParVector.scala:38

I'm not sure why that wasn't being caught by the Scalacheck tests before.

@adriaanm
Copy link
Contributor

Wow this just gets weirder and weirder. Maybe we've never been testing what we thought we were testing? 😳 Sounds like I should've been more keen on insourcing when you suggested it.

@retronym
Copy link
Member Author

One confounding factor is that I failed to insource exactly the same version of Scalacheck as we'd been using before. I'm going to rebase to include a commit to upgrade first and see what that tells me. The actual fix for the padTo test is easy enough, btw. But hopefully the step by step approach will help solve our epistemological crisis.

This is a temporary measure until we release Scala
2.12.0. It means we are able to release milestones,
and RCs of Scala without needing a public release of
Scalacheck. While we've never had to wait very long
for these in the past (Thanks, Rickard!) we'd like
to spare the maintainer some work betwen now and 2.12.0.

After we release Scala 2.12.0, we'll revert to a binary
dependency on the standard Scalacheck.

I have replaced the scala-parser-combinator based
command line option parsing with a quick and dirty
version.

I've had to remove scalacheck as a SBT test framework
in our build. We don't use it directly as such (instead,
it is used indirectly through `partest --scalacheck`),
and it's test discovery (which we expect to return nothing)
fails after re-STARR-ing due to an unsolved problem with
SBT's testLoader including either STARR or sbt-launch.jar
on the classpath used to discover and spawn tests.

For the record, I tried the following to no avail:

```
// Two modifications are needed from the stock SBT configuration in order to exclude STARR
// from the classloader that performs test discovery.
//   - We make `isManagedVersion` hold by providing an explicit Scala version, in order to go into the desired
//     branch in `createTestLoader`
//   - We remove STARR from the classloader of the scala instance
def fixTestLoader = testLoader := {
  val s = scalaInstance.value
  val scalaInstance1 =
    new ScalaInstance(s.version, appConfiguration.value.provider.scalaProvider.loader(), s.libraryJar, s.compilerJar, s.extraJars, Some(s.actualVersion))
  assert(scalaInstance1.isManagedVersion)
  TestFramework.createTestLoader(Attributed.data(fullClasspath.value), scalaInstance1, IO.createUniqueDirectory(taskTemporaryDirectory.value))
}

```

f
@retronym
Copy link
Member Author

Okay, the last piece of the puzzle was the fact that I had stubbed out the Scalacheck option parser and just used the defaults, which meant our custom option of -minSuccessfulTests 5 was no longer added, and a more exhaustive search for the bugs found the "corner" case of a collection with 1-256 elements.

Details of my fix for this in the new commit comment

@retronym
Copy link
Member Author

@som-snytt DYR whether your addition of -minSuccessfulTests 5 in bf44669#diff-5d9130d7e2cd71725aeac13615844703R3 was replicating existing behaviour or was a change?

@som-snytt
Copy link
Contributor

som-snytt commented Jun 27, 2016

@retronym I haven't clicked that diff yet, but it would have to be "replicate existing".

Edit: Yes, the commented-out main shows the original test configuration.

@adriaanm
Copy link
Contributor

I'm running the ant build locally to verify it passes.

println(cdoub)
property("padTos must be equal") = forAllNoShrink(collectionPairsWithLengths) { case (s, coll, len) =>
// assert(s.size == coll.size, (s.size, coll.size))
println("=" + coll.size + "/" + coll.getClass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ant this gets to the console -- intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, that's stray debugging. Removed.

This was throwing a UnsupportedOperationError for small operations.

The parallel collections test suite sets `-minSuccessfulTests 5` in
test/files/scalacheck/parallel-collections/pc.scala, which is far
lower thatn the default of 100, and means that we are less likely
to falsify properties.

This parameter seems to have been added in scala#2476, assuming I'm reading
it correctly. Not sure of the motiviation, perhaps just to make the
slowest part of the scalacheck test suite run faster?

I haven't changed the paramater now, but instead have included a one
element collection in generator.

I also found that when the test failed, Scalacheck would try to minimize
the example, but did so assuming that the elements of the tuple of
test data could be independentally shrunk. This breaks the invariant
that the two collections contain equal elements, and led to spurious
error reports. I have disabled shrinking in all tests tests affected
by this.
@adriaanm
Copy link
Contributor

LGTM!

@adriaanm adriaanm merged commit 7a7fdac into scala:2.12.x Jun 28, 2016
@milessabin
Copy link
Contributor

milessabin commented Jul 20, 2016

I'm seeing a problem using a distribution built with dist/mkPack in conjunction with scalaHome in SBT. SBT has some fiddly logic for deciding which class loader to load classes for running tests, and the addition of ScalaCheck class files to build/pack/lib/scala-partest-extras.jar trips it up. The result is that tests compile, but when run we get a ClassNotFoundException for classes which would normally be found in SBT's test-interface.jar.

It would be possible to tweak the SBT class loading logic to accommodate this, but before doing that I'd like to be sure that we really do need dist/mkPack to be building and packaging scala-partest-extras.jar.

@milessabin
Copy link
Contributor

See the discussion here for more detail of how this manifests itself in SBT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
7 participants