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

Fix flaky tests #274

Merged
merged 2 commits into from
Nov 1, 2023
Merged

Fix flaky tests #274

merged 2 commits into from
Nov 1, 2023

Conversation

hofi1
Copy link

@hofi1 hofi1 commented Oct 31, 2023

com.github.jk1.license.filter.LicenseBundleNormalizerSpec."The regular expression must be matched"

Problem

The test com.github.jk1.license.filter.LicenseBundleNormalizerSpec."The regular expression must be matched" asserts two JSON strings for equality. The corresponding data is stored in sets. But the order, in which the data is returned by the Sets, is not deterministic. The test expects the values to be in certain order, even tho two JSON String are considered as equal if the data is in a different order (regarding the specification of JSON).
This leads to a flaky test.

This problem was found by the NonDex Engine.

@Sortable(includes = ["group", "name", "version"])
@Canonical
class ModuleData {
String group, name, version
Set<ManifestData> manifests = new TreeSet<ManifestData>()
Set<LicenseFileData> licenseFiles = new HashSet<LicenseFileData>()
Set<PomData> poms = new HashSet<PomData>()
boolean isEmpty() { manifests.isEmpty() && poms.isEmpty() && licenseFiles.isEmpty() }
}

Solution

The best solution would be to use JsonAsserts. But since it is unlikely that the owern of the repo would accept a new dependency, it was fixed by making sure the data is returned in a deterministic order.
By changing the HashSets to TreeSets (to guarantee the order of the elements in the JSON string) in the ModulData class and adding the @Sortable Annotation (needed by the TreeSet to order the elements in the set) to the PomData class, determinism is established.

@Sortable(includes = ["group", "name", "version"])
@Canonical
class ModuleData {
String group, name, version
Set<ManifestData> manifests = new TreeSet<ManifestData>()
Set<LicenseFileData> licenseFiles = new TreeSet<LicenseFileData>()
Set<PomData> poms = new TreeSet<PomData>()
boolean isEmpty() { manifests.isEmpty() && poms.isEmpty() && licenseFiles.isEmpty() }
}

@Canonical
@Sortable(includes = ["name", "description", "projectUrl", "inceptionYear"])
class PomData {
String name, description, projectUrl, inceptionYear
Set<License> licenses = new TreeSet<License>()
PomOrganization organization
Set<PomDeveloper> developers = new TreeSet<PomDeveloper>()
}

com.github.jk1.license.ProjectBuilderSpec."it creates modules with license-files"

Problem

The test com.github.jk1.license.ProjectBuilderSpec."it creates modules with license-files asserts that two lists contain the same elements. These elements are stored in a List. But the order, in which the elements in the data structure are returned by the set, is not deterministic. The test expects the values to be in certain order.
This leads to a flaky test.

This problem was found by the NonDex Engine.

data.configurations*.dependencies.flatten().poms.flatten().find { it.name == "pom1" }.licenses as List == [APACHE2_LICENSE()]
data.configurations*.dependencies.flatten().poms.flatten().find { it.name == "pom2" }.licenses as List == [MIT_LICENSE()]
data.configurations*.dependencies.flatten().poms.flatten().find { it.name == "pom3" }.licenses as List == [LGPL_LICENSE(), MIT_LICENSE()]

Solution

By sorting the elements in the lists, a deterministic behavior can be guaranteed.

data.configurations*.dependencies.flatten().licenseFiles.flatten().fileDetails.flatten()*.file.sort() ==["file1", "file2", "file3"].sort()
data.configurations*.dependencies.flatten().licenseFiles.flatten().fileDetails.flatten()*.license.sort() == ["lic1", "lic2", "lic3"].sort()
data.configurations*.dependencies.flatten().licenseFiles.flatten().fileDetails.flatten()*.licenseUrl.sort() == ["licUrl1", "licUrl2", "licUrl3"].sort()

Reproduce

To reproduce follow the steps:

  1. Run ./gradlew build -x test
  2. Add the following text to the top of the build.gradle file in $PROJ_DIR.
buildscript {
    repositories {
      maven {
        url = uri('https://plugins.gradle.org/m2/')
      }
    }
    dependencies {
      classpath('edu.illinois:plugin:2.1.1')
    }
}
  1. Add the following line to the end of the build.gradle file in $PROJ_DIR.
apply plugin: 'edu.illinois.nondex'
  1. Run
./gradlew --info nondexTest --tests=_full.qulified.identification.of.test_ --nondexRuns=50

@jk1
Copy link
Owner

jk1 commented Nov 1, 2023

@hofi1 thank you for your contribution. I suppose that the tests are not actually flaky. The reason being, a HashMap ordering is not guaranteed, but is still stable from run to run. Yet it's definitely not something to rely upon in the long run.

@jk1 jk1 merged commit 232fc7e into jk1:master Nov 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants