Skip to content

Commit

Permalink
Excluding performance and integration tests from build task (#1925)
Browse files Browse the repository at this point in the history
This PR moves all tests in `cpg-neo4j` that depend on external frontends to an integration test and moves integration tests and performance tests out of the `build` task.

Fixes #1915
  • Loading branch information
oxisto authored Jan 10, 2025
1 parent b6f20d9 commit 0546b09
Show file tree
Hide file tree
Showing 18 changed files with 298 additions and 234 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ jobs:
# See https://github.com/codecov/feedback/issues/564 and https://github.com/Kotlin/kotlinx-kover/issues/699.
# Actually these lines should just not exist in the coverage XML file, since they are only structural elements, such
# as brackets.
cat cpg-all/build/reports/kover/report.xml | grep -v 'mi="0" ci="0" mb="0" cb="0"' > cpg-all/build/reports/kover/report-codecov.xml
rm cpg-all/build/reports/kover/report.xml
cat ./build/reports/kover/report.xml | grep -v 'mi="0" ci="0" mb="0" cb="0"' > ./build/reports/kover/report-codecov.xml
rm ./build/reports/kover/report.xml
- name: Upload Code Coverage
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
files: ./cpg-all/build/reports/kover/report-codecov.xml
files: ./build/reports/kover/report-codecov.xml
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
- name: Prepare test and coverage reports
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ logs
*.log

gradle.properties
LibrariesTest.kt

93 changes: 48 additions & 45 deletions buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.services.BuildServiceParameters.None
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.gradle.api.services.BuildService;
import org.gradle.api.services.BuildServiceParameters;
import org.gradle.api.services.BuildServiceParameters.None;

plugins {
id("cpg.formatting-conventions")

`java-library`
`jvm-test-suite`
jacoco
signing
`maven-publish`
kotlin("jvm")
id("org.jetbrains.dokka")
id("org.jetbrains.kotlinx.kover")
}

java {
Expand Down Expand Up @@ -112,64 +110,69 @@ tasks.withType<KotlinCompile> {
}
}

//
// common testing configuration
//
tasks.test {
useJUnitPlatform() {
excludeTags("integration")
excludeTags("performance")
}
// Configure our test suites
@Suppress("UnstableApiUsage")
testing {
suites {
// The default unit-test suite
val test by getting(JvmTestSuite::class) {
useJUnitJupiter()
}

maxHeapSize = "4048m"
}
// Our integration tests
val integrationTest by registering(JvmTestSuite::class) {
description = "Runs the integration tests"
dependencies {
implementation(project())
implementation(testFixtures(project(":cpg-core")))
}

val integrationTest = tasks.register<Test>("integrationTest") {
description = "Runs integration tests."
group = "verification"
useJUnitPlatform() {
includeTags("integration")
}
// For legacy reasons we also include the unit-test resources in the integration tests,
// because some of them are shared
sources {
resources {
srcDirs("src/test/resources")
}
}

maxHeapSize = "4048m"
testType = TestSuiteType.INTEGRATION_TEST
}

shouldRunAfter(tasks.test)
}
// Our performance tests
val performanceTest by registering(JvmTestSuite::class) {
description = "Runs the performance tests"
dependencies {
implementation(project())
implementation(testFixtures(project(":cpg-core")))
}

val performanceTest = tasks.register<Test>("performanceTest") {
description = "Runs performance tests."
group = "verification"
useJUnitPlatform() {
includeTags("performance")
testType = TestSuiteType.PERFORMANCE_TEST
targets {
all {
testTask.configure {
// do not parallelize tests within the task
maxParallelForks = 1
// make sure that several performance tests (e.g. in different frontends) also do NOT run in parallel
usesService(serialExecutionService)
}
}
}
}
}

maxHeapSize = "4048m"

// do not parallelize tests within the task
maxParallelForks = 1
// make sure that several performance tests (e.g. in different frontends) also do NOT run in parallel
usesService(serialExecutionService)
}

// A build service that ensures serial execution of a group of tasks
abstract class SerialExecutionService : BuildService<BuildServiceParameters.None>
abstract class SerialExecutionService : BuildService<None>
val serialExecutionService =
gradle.sharedServices.registerIfAbsent("serialExecution", SerialExecutionService::class.java) {
this.maxParallelUsages.set(1)
}

kover {
currentProject {
instrumentation {
disabledForTestTasks.add("performanceTest")
}
}
}

// Common dependencies that we need for all modules
val libs = the<LibrariesForLibs>() // necessary to be able to use the version catalog in buildSrc
dependencies {
implementation(libs.apache.commons.lang3)
implementation(libs.neo4j.ogm.core)
implementation(libs.jackson)
}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
plugins {
kotlin("jvm")
`java-library`
id("org.jetbrains.kotlinx.kover")
}

val enableJavaFrontend: Boolean by rootProject.extra
Expand All @@ -17,38 +16,29 @@ val enableINIFrontend: Boolean by rootProject.extra
dependencies {
if (enableJavaFrontend) {
api(project(":cpg-language-java"))
kover(project(":cpg-language-java"))
}
if (enableJVMFrontend) {
api(project(":cpg-language-jvm"))
kover(project(":cpg-language-jvm"))
}
if (enableCXXFrontend) {
api(project(":cpg-language-cxx"))
kover(project(":cpg-language-cxx"))
}
if (enableGoFrontend) {
api(project(":cpg-language-go"))
kover(project(":cpg-language-go"))
}
if (enablePythonFrontend) {
api(project(":cpg-language-python"))
kover(project(":cpg-language-python"))
}
if (enableLLVMFrontend) {
api(project(":cpg-language-llvm"))
kover(project(":cpg-language-llvm"))
}
if (enableTypeScriptFrontend) {
api(project(":cpg-language-typescript"))
kover(project(":cpg-language-typescript"))
}
if (enableRubyFrontend) {
api(project(":cpg-language-ruby"))
kover(project(":cpg-language-ruby"))
}
if (enableINIFrontend) {
api(project(":cpg-language-ini"))
kover(project(":cpg-language-ini"))
}
}
5 changes: 0 additions & 5 deletions cpg-all/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,4 @@ dependencies {
api(projects.cpgCore)
api(projects.cpgAnalysis)
api(projects.cpgNeo4j)

kover(projects.cpgConsole)
kover(projects.cpgCore)
kover(projects.cpgAnalysis)
kover(projects.cpgNeo4j)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
* Copyright (c) 2025, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,23 +25,23 @@
*/
package de.fraunhofer.aisec.cpg.graph

import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration
import de.fraunhofer.aisec.cpg.graph.edges.astEdges
import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal
import de.fraunhofer.aisec.cpg.helpers.Benchmark
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
import de.fraunhofer.aisec.cpg.test.*
import de.fraunhofer.aisec.cpg.test.BaseTest
import java.time.Duration
import java.time.temporal.ChronoUnit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import org.junit.jupiter.api.Tag
import kotlin.test.*
import org.junit.jupiter.api.assertTimeout

@Tag("performance")
class WalkerTest : BaseTest() {
@Test
fun testWalkerSpeed() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2025, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.frontends.cxx

import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration
import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge
import de.fraunhofer.aisec.cpg.graph.newLiteral
import de.fraunhofer.aisec.cpg.graph.primitiveType
import de.fraunhofer.aisec.cpg.graph.statements.expressions.InitializerListExpression
import de.fraunhofer.aisec.cpg.helpers.Benchmark
import de.fraunhofer.aisec.cpg.test.analyzeAndGetFirstTU
import java.time.Duration
import java.time.temporal.ChronoUnit
import kotlin.io.path.createTempFile
import kotlin.io.path.writeText
import kotlin.test.*
import org.junit.jupiter.api.assertTimeout

class PerformanceRegressionTest {
/**
* This test demonstrates two performance bottlenecks.
* * First, we want to make a large initializer list with literals and make sure we parse this
* in reasonable time. We had issues with literals and their hashcode when they were inserted
* into a set.
* * Second, we want to make that list essentially a one-liner because we had issues when
* populating the [de.fraunhofer.aisec.cpg.graph.Node.location] property using
* [CXXLanguageFrontend.locationOf].
*/
@Test
fun testParseLargeList() {
val range = 0..40000
// intentionally make this one very long line, because we had problems with that
val string = "static int my_array[] = {" + range.toList().joinToString(", ") + "};"

val tmp = createTempFile("c_range", ".c")
tmp.writeText(string)

// this should not exceed 30 seconds (it takes about 2800ms on a good machine, about
// 10-20s on GitHub, depending on the slowness of the runner)
assertTimeout(Duration.of(30, ChronoUnit.SECONDS)) {
val tu =
analyzeAndGetFirstTU(listOf(tmp.toFile()), tmp.parent, true) {
// No need for parallel processing for a single file. this might make it fast
// enough for those special moments where for some reasons the GitHub runners
// are slowing down (maybe because of some hidden quota).
it.useParallelFrontends(false)
it.registerLanguage<CLanguage>()
}
assertNotNull(tu)
}
}

@Test
fun testTraversal() {
with(TestLanguageFrontend()) {
val tu = TranslationUnitDeclaration()
val decl = VariableDeclaration()
val list = InitializerListExpression()

for (i in 0 until 50000) {
list.initializerEdges.add(AstEdge(list, newLiteral(i, primitiveType("int"), null)))
}

decl.initializer = list
tu.addDeclaration(decl)

// Even on a slow machine, this should not exceed 1 second (it should be more like
// 200-300ms)
assertTimeout(Duration.of(1, ChronoUnit.SECONDS)) {
val b = Benchmark(PerformanceRegressionTest::class.java, "getAstChildren")
doNothing(tu)
b.addMeasurement()
}
}
}

fun doNothing(node: Node) {
for (child in node.astChildren) {
doNothing(child)
}
}
}
Loading

0 comments on commit 0546b09

Please sign in to comment.