Best Kotest code snippet using io.kotest.common.results.Result.flatMap
TestInsertToc.kt
Source:TestInsertToc.kt
1import com.github.blueboxware.tocme.TocMeOptions2import com.github.blueboxware.tocme.util.TocMeOptionsImpl3import com.github.blueboxware.tocme.util.Variant4import com.github.blueboxware.tocme.util.insertTocs5import com.vladsch.flexmark.parser.Parser6import com.vladsch.flexmark.parser.ParserEmulationProfile7import com.vladsch.flexmark.util.data.MutableDataSet8import io.kotest.common.ExperimentalKotest9import io.kotest.core.spec.style.BehaviorSpec10import io.kotest.datatest.withData11import io.kotest.engine.spec.tempfile12import java.io.File13import kotlin.test.assertNotNull14import kotlin.test.assertNull15import kotlin.test.assertTrue16/*17 * Copyright 2021 Blue Box Ware18 *19 * Licensed under the Apache License, Version 2.0 (the "License");20 * you may not use this file except in compliance with the License.21 * You may obtain a copy of the License at22 *23 * http://www.apache.org/licenses/LICENSE-2.024 *25 * Unless required by applicable law or agreed to in writing, software26 * distributed under the License is distributed on an "AS IS" BASIS,27 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.28 * See the License for the specific language governing permissions and29 * limitations under the License.30 */31@OptIn(ExperimentalKotest::class)32@Suppress("unused")33internal object TestInsertToc: BehaviorSpec({34 val TEST_DATA_DIR = File("src/test/resources")35 val TEST_DATA_FILES_DIR = File(TEST_DATA_DIR, "files/")36 var expectedTestNumber: Int37 data class Test(38 val label: String,39 val input: String,40 val variant: Variant,41 val expected: String42 )43 fun loadTests(filename: String): List<Test> = run {44 expectedTestNumber = 045 File(TEST_DATA_DIR, filename).readText().split(Regex("=====+\n")).flatMap { test ->46 Regex(47 """(.*)---*\s+(\d+)\s+---+\n(.*)""",48 RegexOption.DOT_MATCHES_ALL49 ).matchEntire(test)?.groupValues?.let { (_, input, nr, expectedOutput) ->50 expectedTestNumber++51 if (nr.toInt() != expectedTestNumber) {52 throw AssertionError("Test number is $nr, should be $expectedTestNumber")53 }54 Variant.values().map {55 Test("$nr ($it)", input.trimEnd('\n'), it, expectedOutput.trimEnd('\n'))56 }57 } ?: throw AssertionError()58 }59 }60 given("a Markdown document with correct toc tags") {61 withData(loadTests("valid.data")) { (label, input, variant, expected) ->62 `when`("inserting a toc: $label") {63 val parserOptions = MutableDataSet()64 variant.setIn(parserOptions)65 val tocMeOptions = TocMeOptionsImpl(null).apply { this.variant = variant }66 val parser = Parser.builder(parserOptions).build()67 val document = parser.parse(input)68 val (result, warnings, error) = document.insertTocs(tocMeOptions, checkCurrentContent = true)69 then("generates no errors") {70 assertNull(error, "Unexpected error: $error")71 }72 then("generates no warnings") {73 assertTrue(warnings.isEmpty(), "Unexpected warning(s): \n" + warnings.joinToString(separator = "\n"))74 }75 then("generates the correct results") {76 ProjectFixture.assertTextEquals(expected, result!!, document = document)77 }78 then("should 'update' without warnings, errors or actual changes") {79 val document2 = parser.parse(result!!)80 val (result2, warnings2, error2) = document2.insertTocs(tocMeOptions, checkCurrentContent = true)81 assertNull(error2, "Failed to update. Unexpected error: $error2")82 assertTrue(83 warnings2.isEmpty(),84 "Failed to update. Unexpected warning(s): \n" + warnings2.joinToString(separator = "\n")85 )86 ProjectFixture.assertTextEquals(result, result2!!)87 }88 }89 }90 }91 given("a Markdown document which should produces warnings") {92 withData(loadTests("warning.data")) { (label, input, variant, expected) ->93 `when`("inserting a toc: $label") {94 val parserOptions = MutableDataSet()95 variant.setIn(parserOptions)96 val tocMeOptions = TocMeOptionsImpl(null).apply { this.variant = variant }97 val parser = Parser.builder(parserOptions).build()98 val document = parser.parse(input)99 val (result, warnings, error) = document.insertTocs(tocMeOptions, checkCurrentContent = true)100 val warningRegex = expected.lines().first().let {101 Regex(102 ".*$it.*",103 RegexOption.IGNORE_CASE104 )105 }106 then("generates no errors") {107 assertNull(error, "Unexpected error: $error")108 }109 then("generates the expected warning") {110 assertTrue(111 warnings.any { it.matches(warningRegex) },112 "No warning which matches '$warningRegex' found.\nWarnings:\n" + warnings.joinToString(separator = "\n")113 )114 }115 then("generates the correct result") {116 expected.lines().drop(1).joinToString(separator = "\n").let {117 ProjectFixture.assertTextEquals(it, result!!)118 }119 }120 }121 }122 }123 given("a Markdown document which should produce an error") {124 withData(loadTests("error.data")) { (label, input, variant, expected) ->125 `when`("inserting a toc: $label") {126 val parserOptions = MutableDataSet()127 variant.setIn(parserOptions)128 val tocMeOptions = TocMeOptionsImpl(null).apply { this.variant = variant }129 val parser = Parser.builder(parserOptions).build()130 val document = parser.parse(input)131 val (_, _, error) = document.insertTocs(tocMeOptions, checkCurrentContent = true)132 val errorRegex = expected.lines().first().let { Regex(".*$it.*", RegexOption.IGNORE_CASE) }133 then("generates an error") {134 assertNotNull(error, "No error")135 assertTrue(error.matches(errorRegex), "Actual error ('$error') doesn't match regex ('$errorRegex')")136 }137 }138 }139 }140 given("a Markdown document from file") {141 val options = MutableDataSet().apply {142 setFrom(ParserEmulationProfile.GITHUB)143 }144 val parser = Parser.builder(options).build()145 TEST_DATA_FILES_DIR.walkTopDown().filter { it.extension == "md" && it.name != "variant_issues.md" }.map {146 Triple(147 it.absolutePath,148 it.readText(),149 File(TEST_DATA_FILES_DIR, it.nameWithoutExtension + ".out").readText()150 )151 }.forEach { (absolutePath, input, expected) ->152 `when`("inserting a toc ($absolutePath)") {153 val document = parser.parse(input)154 val (result, warnings, error) = document.insertTocs(TocMeOptionsImpl(null), checkCurrentContent = true)155 then("generates no errors") {156 assertNull(error, "Unexpected error: $error")157 }158 then("generates no warnings") {159 assertTrue(warnings.isEmpty(), "Unexpected warning(s): \n" + warnings.joinToString(separator = "\n"))160 }161 then("generates the correct results") {162 ProjectFixture.assertTextEquals(expected.trimEnd(), result!!.trimEnd())163 }164 then("should 'update' without warnings, errors or actual changes") {165 val document2 = parser.parse(result!!)166 val (result2, warnings2, error2) = document2.insertTocs(TocMeOptionsImpl(null), checkCurrentContent = true)167 assertNull(error2, "Failure on update. Unexpected error: $error2")168 assertTrue(169 warnings2.isEmpty(),170 "Failure on update. Unexpected warning(s): \n" + warnings2.joinToString(separator = "\n")171 )172 ProjectFixture.assertTextEquals(result, result2!!)173 }174 }175 }176 }177 given("a Markdown document with constructs which are handled different by different markdown variants") {178 Variant.values().map {179 Pair(180 it.name,181 it182 )183 }.forEach { (name, variant) ->184 `when`("inserting a toc with variant $name") {185 val tocmeOptions = TocMeOptionsImpl(null).apply {186 this.variant = variant187 }188 val tempFile = tempfile()189 val (result, warnings, error) =190 insertTocs(191 File(TEST_DATA_FILES_DIR, "variant_issues.md"),192 tempFile,193 tocmeOptions,194 writeChanges = true195 )196 then("generates no errors") {197 assertNull(error, "Unexpected error: $error")198 }199 then("generates no warnings") {200 assertTrue(201 warnings.isEmpty(),202 "Unexpected warning(s): \n" + warnings.joinToString(separator = "\n")203 )204 }205 then("generates the correct result") {206 File(TEST_DATA_FILES_DIR, "variant_issues.$name.out").let { expectedFile ->207 if (!expectedFile.exists()) {208 expectedFile.writeText(result!!)209 throw AssertionError("Expected file '${expectedFile.absolutePath}' does not exist. Creating.")210 }211 ProjectFixture.assertTextEquals(212 expectedFile.readText(),213 result!!214 )215 }216 }217 }218 }219 }220 given("a Markdown document with emojis in headers") {221 listOf("variant_issues", "nerdfonts").map {222 Pair("$it.md", "${it}_no_emojis.out")223 }.forEach { (file, expected) ->224 `when`("inserting a toc while removing emojis: $file") {225 val tempFile = tempfile()226 val tocmeOptions = TocMeOptionsImpl(null).apply {227 removeEmojis = true228 }229 val (_, warnings, error) = insertTocs(230 File(TEST_DATA_FILES_DIR, file),231 tempFile,232 tocmeOptions,233 writeChanges = true234 )235 then("generates no errors") {236 assertNull(error, "Unexpected error: $error")237 }238 then("generates no warnings") {239 assertTrue(warnings.isEmpty(), "Unexpected warning(s): \n" + warnings.joinToString(separator = "\n"))240 }241 then("generates the correct result") {242 ProjectFixture.assertFileEquals(File(TEST_DATA_FILES_DIR, expected), tempFile)243 }244 }245 }246 }247 given("a simple document with variant issues") {248 data class Test(249 val label: String,250 val contents: String,251 val conf: TocMeOptions.() -> Unit,252 val expected: String253 )254 val tests = mutableListOf<Test>()255 fun test(256 id: String,257 doc: String,258 cases: List<Triple<String, TocMeOptions.() -> Unit, String>>,259 trimIndent: Boolean = true,260 insertHeader: Boolean = true261 ) {262 val header = if (insertHeader) "<!--toc-->\n<!--/toc-->\n" else ""263 val contents = header + if (trimIndent) doc.trimIndent() else doc264 cases.forEach { (caseId, conf, expected) ->265 tests.add(Test("$id: $caseId", contents, conf, expected.trimIndent()))266 }267 }268 fun case(id: String, conf: TocMeOptions.() -> Unit, expected: String) = Triple(id, conf, expected)269 test(270 id = "allowSpace",271 doc = """272 # Header1273 #Header2274 """,275 cases = listOf(276 case(277 id = "true",278 conf = { requireSpace = false },279 expected = """280 <!--toc-->281 - __[Header1](#header1)__282 - __[Header2](#header2)__283 <!--/toc-->284 # Header1285 #Header2286 """287 ),288 case(289 id = "false",290 conf = { requireSpace = true },291 expected = """292 <!--toc-->293 - __[Header1](#header1)__294 <!--/toc-->295 # Header1296 #Header2297 """298 )299 )300 )301 test(302 id = "dupedDashes",303 doc = """304 # ----Header1 ---___--305 # ---__---Header--2--___--306 # Hea der 3307 """,308 cases = listOf(309 case(310 id = "true",311 conf = {312 dupedDashes = true313 dashChars = "-"314 },315 expected = """316 <!--toc-->317 - __[----Header1 ---___--](#----header1-----)__318 - __[------Header--2--_--](#------header--2----)__319 - __[Hea der 3](#header3)__320 <!--/toc-->321 # ----Header1 ---___--322 # ---__---Header--2--___--323 # Hea der 3324 """325 ),326 case(327 id = "false",328 conf = {329 dupedDashes = false330 dashChars = "-"331 },332 expected = """333 <!--toc-->334 - __[----Header1 ---___--](#-header1-)__335 - __[------Header--2--_--](#-header-2-)__336 - __[Hea der 3](#header3)__337 <!--/toc-->338 # ----Header1 ---___--339 # ---__---Header--2--___--340 # Hea der 3341 """342 ),343 case(344 id = "true + dashChars='_- '",345 conf = {346 dupedDashes = true347 dashChars = "_- "348 },349 expected = """350 <!--toc-->351 - __[----Header1 ---___--](#----header1---------)__352 - __[------Header--2--_--](#------header--2-----)__353 - __[Hea der 3](#hea-der---3)__354 <!--/toc-->355 # ----Header1 ---___--356 # ---__---Header--2--___--357 # Hea der 3358 """359 ),360 case(361 id = "false + dashChars='_- '",362 conf = {363 dupedDashes = false364 dashChars = "_- "365 },366 expected = """367 <!--toc-->368 - __[----Header1 ---___--](#-header1-)__369 - __[------Header--2--_--](#-header-2-)__370 - __[Hea der 3](#hea-der-3)__371 <!--/toc-->372 # ----Header1 ---___--373 # ---__---Header--2--___--374 # Hea der 3375 """376 )377 )378 )379 test(380 id = "resolveDupes",381 doc = """382 # Header1383 # Header2384 ## Header1385 # Header2386 Header1387 =====388 """,389 cases = listOf(390 case(391 id = "true",392 conf = { resolveDupes = true },393 expected = """394 <!--toc-->395 - __[Header1](#header1)__396 - __[Header2](#header2)__397 - __[Header1](#header1-1)__398 - __[Header2](#header2-1)__399 - __[Header1](#header1-2)__400 <!--/toc-->401 # Header1402 # Header2403 ## Header1404 # Header2405 Header1406 =====407 """408 ),409 case(410 id = "false",411 conf = { resolveDupes = false },412 expected = """413 <!--toc-->414 - __[Header1](#header1)__415 - __[Header2](#header2)__416 - __[Header1](#header1)__417 - __[Header2](#header2)__418 - __[Header1](#header1)__419 <!--/toc-->420 # Header1421 # Header2422 ## Header1423 # Header2424 Header1425 =====426 """427 )428 )429 )430 test(431 id = "allowedChars",432 doc = """433 # Header_+1()*#$@!434 # Header++2++$@!###435 # Header 3 ($)/\436 """,437 cases = listOf(438 case(439 id = "_+#$/ ",440 conf = { allowedChars = "_+#$/ " },441 expected = """442 <!--toc-->443 - __[Header_+1()*#${'$'}@!](#header_+1#${'$'})__444 - __[Header++2++${'$'}@!###](#header++2++${'$'}###)__445 - __[Header 3 (${'$'})/\](#header 3 ${'$'}/)__446 <!--/toc-->447 # Header_+1()*#${'$'}@!448 # Header++2++${'$'}@!###449 # Header 3 (${'$'})/\450 """451 ),452 case(453 id = "<none>",454 conf = { allowedChars = "" },455 expected = """456 <!--toc-->457 - __[Header_+1()*#${'$'}@!](#header-1)__458 - __[Header++2++${'$'}@!###](#header2)__459 - __[Header 3 (${'$'})/\](#header-3-)__460 <!--/toc-->461 # Header_+1()*#${'$'}@!462 # Header++2++${'$'}@!###463 # Header 3 (${'$'})/\464 """465 )466 )467 )468 test(469 id = "allowLeadingSpace",470 trimIndent = false,471 doc = """472 # Header1473 # Header2474 # Header 3475 Header4476 =====477 Header5478 ------479 Header6480 -------481""",482 cases = listOf(483 case(484 id = "true",485 conf = { allowLeadingSpace = true },486 expected = """487 <!--toc-->488 - __[Header2](#header2)__489 - __[Header 3](#header-3)__490 - __[Header4](#header4)__491 - __[Header5](#header5)__492 <!--/toc-->493 # Header1494 # Header2495 # Header 3496 Header4497 =====498 Header5499 ------500 Header6501 -------502 """503 ),504 case(505 id = "false",506 conf = { allowLeadingSpace = false },507 expected = """508 <!--toc-->509 <!--/toc-->510 # Header1511 # Header2512 # Header 3513 Header4514 =====515 Header5516 ------517 Header6518 -------519 """520 )521 )522 )523 test(524 id = "setextMarkerLength",525 doc = """526 Length 1527 =528 Length 1529 -530 Length 2531 ==532 Length 3533 ===534 Length 4535 ====536 Length 2537 --538 Length 3539 ---540 Length 4541 ----542 Length 5543 -----544 """,545 cases = listOf(546 case(547 id = "1",548 conf = { setextMarkerLength = 1 },549 expected = """550 <!--toc-->551 - __[Length 1](#length-1)__552 - __[Length 1](#length-1-1)__553 - __[Length 2](#length-2)__554 - __[Length 3](#length-3)__555 - __[Length 4](#length-4)__556 - __[Length 2](#length-2-1)__557 - __[Length 3](#length-3-1)__558 - __[Length 4](#length-4-1)__559 - __[Length 5](#length-5)__560 <!--/toc-->561 Length 1562 =563 Length 1564 -565 Length 2566 ==567 Length 3568 ===569 Length 4570 ====571 Length 2572 --573 Length 3574 ---575 Length 4576 ----577 Length 5578 -----579 """580 ),581 case(582 id = "2",583 conf = { setextMarkerLength = 2 },584 expected = """585 <!--toc-->586 - __[Length 2](#length-2)__587 - __[Length 3](#length-3)__588 - __[Length 4](#length-4)__589 - __[Length 2](#length-2-1)__590 - __[Length 3](#length-3-1)__591 - __[Length 4](#length-4-1)__592 - __[Length 5](#length-5)__593 <!--/toc-->594 Length 1595 =596 Length 1597 -598 Length 2599 ==600 Length 3601 ===602 Length 4603 ====604 Length 2605 --606 Length 3607 ---608 Length 4609 ----610 Length 5611 -----612 """613 ),614 case(615 id = "4",616 conf = { setextMarkerLength = 4 },617 expected = """618 <!--toc-->619 - __[Length 4](#length-4)__620 - __[Length 4](#length-4-1)__621 - __[Length 5](#length-5)__622 <!--/toc-->623 Length 1624 =625 Length 1626 -627 Length 2628 ==629 Length 3630 ===631 Length 4632 ====633 Length 2634 --635 Length 3636 ---637 Length 4638 ----639 Length 5640 -----641 """642 )643 )644 )645 test(646 id = "emptyHeadingWithoutSpace",647 doc = """648 # Header1649 ##650 ##s651 ###652 ###s653 ####654 """.replace("s", " "),655 cases = listOf(656 case(657 id = "true",658 conf = { emptyHeadingWithoutSpace = true },659 expected = """660 <!--toc-->661 - __[Header1](#header1)__662 - __[](#)__663 - __[](#)__664 - __[](#)__665 - __[](#)__666 <!--/toc-->667 # Header1668 ##669 ##s670 ###671 ###s672 ####673 """.replace("s", " ")674 ),675 case(676 id = "false",677 conf = { emptyHeadingWithoutSpace = false },678 expected = """679 <!--toc-->680 - __[Header1](#header1)__681 - __[](#)__682 - __[](#)__683 <!--/toc-->684 # Header1685 ##686 ##s687 ###688 ###s689 ####690 """.replace("s", " ")691 )692 )693 )694 test(695 id = "headingInterruptsItemParagraph",696 doc = """697 # Header1698 - item 1699 - item 2700 # Header2701 - item 3702 # Header 3703 """,704 cases = listOf(705 case(706 id = "true",707 conf = { headingInterruptsItemParagraph = true },708 expected = """709 <!--toc-->710 - __[Header1](#header1)__711 - __[Header2](#header2)__712 - __[Header 3](#header-3)__713 <!--/toc-->714 # Header1715 - item 1716 - item 2717 # Header2718 - item 3719 # Header 3720 """721 ),722 case(723 id = "false",724 conf = { headingInterruptsItemParagraph = false },725 expected = """726 <!--toc-->727 - __[Header1](#header1)__728 - __[Header 3](#header-3)__729 <!--/toc-->730 # Header1731 - item 1732 - item 2733 # Header2734 - item 3735 # Header 3736 """737 )738 )739 )740 test(741 id = "custom tag name",742 doc = """743 <!---- foobar numbered=true--->744 <!--/foobar --->745 # Header1746 ### Header2747 """,748 insertHeader = false,749 cases = listOf(750 case(751 id = "",752 conf = { tag = "foobar" },753 expected = """754 <!---- foobar numbered=true--->755 1. __[Header1](#header1)__756 1. __[Header2](#header2)__757 <!--/foobar --->758 # Header1759 ### Header2760 """761 )762 )763 )764 withData(tests.associateBy { it.label }) { (name, content, conf, expected) ->765 `when`("inserting a toc: $name") {766 val parentTocmeOptions = TocMeOptionsImpl(null).apply {767 variant = Variant.Commonmark768 }769 conf.let { parentTocmeOptions.it() }770 val tocmeOptions = TocMeOptionsImpl(parentTocmeOptions)771 val parser = Parser.builder(tocmeOptions.toParserOptions()).build()772 val document = parser.parse(content)773 val (result, warnings, error) = document.insertTocs(tocmeOptions, checkCurrentContent = true)774 then("generates no errors") {775 assertNull(error, "Unexpected error: $error")776 }777 then("generates no warnings") {778 assertTrue(warnings.isEmpty(), "Unexpected warning(s): \n" + warnings.joinToString(separator = "\n"))779 }780 then("generates the correct result") {781 ProjectFixture.assertTextEquals(expected, result!!)782 }783 }784 }785 }786})...
VulnerableCodeTest.kt
Source:VulnerableCodeTest.kt
1/*2 * Copyright (C) 2021 Bosch.IO GmbH3 * Copyright (C) 2021 HERE Europe B.V.4 *5 * Licensed under the Apache License, Version 2.0 (the "License");6 * you may not use this file except in compliance with the License.7 * You may obtain a copy of the License at8 *9 * https://www.apache.org/licenses/LICENSE-2.010 *11 * Unless required by applicable law or agreed to in writing, software12 * distributed under the License is distributed on an "AS IS" BASIS,13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14 * See the License for the specific language governing permissions and15 * limitations under the License.16 *17 * SPDX-License-Identifier: Apache-2.018 * License-Filename: LICENSE19 */20package org.ossreviewtoolkit.advisor.advisors21import com.github.tomakehurst.wiremock.WireMockServer22import com.github.tomakehurst.wiremock.client.WireMock.aResponse23import com.github.tomakehurst.wiremock.client.WireMock.equalTo24import com.github.tomakehurst.wiremock.client.WireMock.equalToJson25import com.github.tomakehurst.wiremock.client.WireMock.post26import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo27import com.github.tomakehurst.wiremock.core.WireMockConfiguration28import io.kotest.core.spec.style.WordSpec29import io.kotest.matchers.collections.beEmpty30import io.kotest.matchers.collections.containExactly31import io.kotest.matchers.collections.containExactlyInAnyOrder32import io.kotest.matchers.collections.shouldHaveSize33import io.kotest.matchers.maps.shouldNotBeEmpty34import io.kotest.matchers.should35import io.kotest.matchers.shouldBe36import java.io.File37import java.net.URI38import org.ossreviewtoolkit.model.AdvisorCapability39import org.ossreviewtoolkit.model.AdvisorDetails40import org.ossreviewtoolkit.model.Identifier41import org.ossreviewtoolkit.model.OrtResult42import org.ossreviewtoolkit.model.Package43import org.ossreviewtoolkit.model.Severity44import org.ossreviewtoolkit.model.Vulnerability45import org.ossreviewtoolkit.model.VulnerabilityReference46import org.ossreviewtoolkit.model.config.VulnerableCodeConfiguration47import org.ossreviewtoolkit.model.readValue48import org.ossreviewtoolkit.model.utils.toPurl49import org.ossreviewtoolkit.utils.common.enumSetOf50import org.ossreviewtoolkit.utils.test.shouldNotBeNull51class VulnerableCodeTest : WordSpec({52 val server = WireMockServer(53 WireMockConfiguration.options()54 .dynamicPort()55 .usingFilesUnderDirectory(TEST_FILES_ROOT)56 )57 beforeSpec {58 server.start()59 }60 afterSpec {61 server.stop()62 }63 beforeEach {64 server.resetAll()65 }66 "VulnerableCode" should {67 "return vulnerability information" {68 server.stubPackagesRequest("response_packages.json")69 val vulnerableCode = createVulnerableCode(server)70 val packagesToAdvise = inputPackages()71 val result = vulnerableCode.retrievePackageFindings(packagesToAdvise).mapKeys { it.key.id }72 result.shouldNotBeEmpty()73 result.keys should containExactlyInAnyOrder(idLang, idStruts)74 val langResults = result.getValue(idLang)75 langResults shouldHaveSize(1)76 langResults[0].advisor shouldBe vulnerableCode.details77 val expLangVulnerability = Vulnerability(78 id = "CVE-2014-8242",79 listOf(80 VulnerabilityReference(81 URI("https://nvd.nist.gov/vuln/detail/CVE-2014-8242"),82 scoringSystem = null,83 severity = null84 ),85 VulnerabilityReference(86 URI("https://github.com/apache/commons-lang/security/advisories/GHSA-2cxf-6567-7pp6"),87 scoringSystem = "cvssv3.1_qr",88 severity = "LOW"89 ),90 VulnerabilityReference(91 URI("https://github.com/advisories/GHSA-2cxf-6567-7pp6"),92 scoringSystem = null,93 severity = null94 )95 )96 )97 langResults.flatMap { it.vulnerabilities } should containExactly(expLangVulnerability)98 val strutsResults = result.getValue(idStruts)99 strutsResults shouldHaveSize(1)100 val expStrutsVulnerabilities = listOf(101 Vulnerability(102 id = "CVE-2009-1382",103 listOf(104 VulnerabilityReference(105 URI("https://nvd.nist.gov/vuln/detail/CVE-2009-1382"),106 scoringSystem = "cvssv2",107 severity = "7"108 )109 )110 ),111 Vulnerability(112 id = "CVE-2019-CoV19",113 listOf(114 VulnerabilityReference(115 URI("https://nvd.nist.gov/vuln/detail/CVE-2019-CoV19"),116 scoringSystem = "cvssv3",117 severity = "10"118 ),119 VulnerabilityReference(120 URI("https://nvd.nist.gov/vuln/detail/CVE-2019-CoV19"),121 scoringSystem = "cvssv3.1_qr",122 severity = "HIGH"123 )124 )125 )126 )127 strutsResults.flatMap { it.vulnerabilities } should containExactlyInAnyOrder(expStrutsVulnerabilities)128 }129 "handle a failure response from the server" {130 server.stubFor(131 post(urlPathEqualTo("/api/packages/bulk_search/"))132 .willReturn(133 aResponse().withStatus(500)134 )135 )136 val vulnerableCode = createVulnerableCode(server)137 val packagesToAdvise = inputPackages()138 val result = vulnerableCode.retrievePackageFindings(packagesToAdvise).mapKeys { it.key.id }139 result shouldNotBeNull {140 keys should containExactly(packageIdentifiers)141 packageIdentifiers.forEach { pkg ->142 val pkgResults = getValue(pkg)143 pkgResults shouldHaveSize 1144 val pkgResult = pkgResults[0]145 pkgResult.advisor shouldBe vulnerableCode.details146 pkgResult.vulnerabilities should beEmpty()147 pkgResult.summary.issues shouldHaveSize 1148 val issue = pkgResult.summary.issues[0]149 issue.severity shouldBe Severity.ERROR150 }151 }152 }153 "filter out packages without vulnerabilities" {154 server.stubPackagesRequest("response_packages_no_vulnerabilities.json")155 val vulnerableCode = createVulnerableCode(server)156 val packagesToAdvise = inputPackages()157 val result = vulnerableCode.retrievePackageFindings(packagesToAdvise).mapKeys { it.key.id }158 result.keys should containExactly(idStruts)159 }160 "handle unexpected packages in the query result" {161 server.stubPackagesRequest("response_unexpected_packages.json")162 val vulnerableCode = createVulnerableCode(server)163 val packagesToAdvise = inputPackages()164 val result = vulnerableCode.retrievePackageFindings(packagesToAdvise).mapKeys { it.key.id }165 result.keys should containExactlyInAnyOrder(idLang, idStruts)166 }167 "provide correct AdvisorDetails" {168 val vulnerableCode = createVulnerableCode(server)169 vulnerableCode.details shouldBe AdvisorDetails(170 ADVISOR_NAME,171 enumSetOf(AdvisorCapability.VULNERABILITIES)172 )173 }174 }175})176private const val ADVISOR_NAME = "VulnerableCodeTestAdvisor"177private const val TEST_FILES_ROOT = "src/test/assets"178private const val TEST_FILES_DIRECTORY = "vulnerable-code"179private const val TEST_RESULT_NAME = "ort-analyzer-result.yml"180private val idLang = Identifier("Maven:org.apache.commons:commons-lang3:3.5")181private val idText = Identifier("Maven:org.apache.commons:commons-text:1.1")182private val idStruts = Identifier("Maven:org.apache.struts:struts2-assembly:2.5.14.1")183private val idJUnit = Identifier("Maven:junit:junit:4.12")184private val idHamcrest = Identifier("Maven:org.hamcrest:hamcrest-core:1.3")185/**186 * The list with the identifiers of packages that are referenced in the test result file.187 */188private val packageIdentifiers = setOf(idJUnit, idLang, idText, idStruts, idHamcrest)189/**190 * The list of packages referenced by the test result. These packages should be requested by the vulnerability provider.191 */192private val packages = packageIdentifiers.map { it.toPurl() }193/**194 * The JSON request to query the test packages found in the result.195 */196private val packagesRequestJson = generatePackagesRequest()197/**198 * Prepare this server to expect a bulk request for the test packages and to answer it with the given [responseFile].199 */200private fun WireMockServer.stubPackagesRequest(responseFile: String) {201 stubFor(202 post(urlPathEqualTo("/api/packages/bulk_search"))203 .withRequestBody(equalToJson(packagesRequestJson, false, false))204 .withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))205 .willReturn(206 aResponse().withStatus(200)207 .withBodyFile("$TEST_FILES_DIRECTORY/$responseFile")208 )209 )210}211/**212 * Create a configuration for the [VulnerableCode] vulnerability provider that points to the local [server].213 */214private fun createConfig(server: WireMockServer): VulnerableCodeConfiguration {215 val url = "http://localhost:${server.port()}"216 return VulnerableCodeConfiguration(url)217}218/**219 * Create a test instance of [VulnerableCode] that communicates with the local [server].220 */221private fun createVulnerableCode(server: WireMockServer): VulnerableCode =222 VulnerableCode(ADVISOR_NAME, createConfig(server))223/**224 * Return the test file with an analyzer result.225 */226private fun resultFile(): File = File(TEST_FILES_ROOT).resolve(TEST_RESULT_NAME)227/**228 * Return a list with [Package]s from the analyzer result file that serve as input for the [VulnerableCode] advisor.229 */230private fun inputPackages(): List<Package> =231 resultFile().readValue<OrtResult>().getPackages(false).map { it.pkg }232/**233 * Generate the JSON body of the request to query information about packages. It mainly consists of an array with the234 * package URLs.235 */236private fun generatePackagesRequest(): String =237 packages.joinToString(prefix = "{ \"purls\": [", postfix = "] }") { "\"$it\"" }...
CUtil.kt
Source:CUtil.kt
1/*2 * Copyright 2022 Manuel Wrage. Use of this source code is governed by the Apache 2.0 license.3 */4@file:Suppress("UNCHECKED_CAST")5package com.ivianuu.essentials.compiler6import com.ivianuu.essentials.kotlin.compiler.EssentialsCommandLineProcessor7import com.ivianuu.essentials.kotlin.compiler.EssentialsComponentRegistrar8import com.ivianuu.injekt.compiler.transform.dumpAllFiles9import com.tschuchort.compiletesting.KotlinCompilation10import com.tschuchort.compiletesting.SourceFile11import com.tschuchort.compiletesting.SourceFileAccessor12import io.kotest.matchers.shouldBe13import io.kotest.matchers.string.shouldContain14import io.kotest.matchers.string.shouldNotContain15import org.intellij.lang.annotations.Language16import org.jetbrains.kotlin.name.FqName17import java.net.URLClassLoader18import java.nio.file.Files19import kotlin.reflect.KClass20var fileIndex = 021fun source(22 @Language("kotlin") source: String,23 name: String = "File${fileIndex++}.kt",24 packageFqName: FqName = FqName("com.ivianuu.essentials.integrationtests")25) = SourceFile.kotlin(26 name = name,27 contents = buildString {28 appendLine("package $packageFqName")29 appendLine()30 append(source)31 }32)33fun invokableSource(34 @Language("kotlin") source: String35) = source(source, "File.kt")36fun codegen(37 @Language("kotlin") source1: String,38 config: KotlinCompilation.() -> Unit = {},39 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() },40) = codegen(41 sources = listOf(invokableSource(source1)),42 config = config,43 assertions = assertions44)45fun codegen(46 @Language("kotlin") source1: String,47 @Language("kotlin") source2: String,48 config: KotlinCompilation.() -> Unit = {},49 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() },50) = codegen(51 sources = listOf(source(source1), invokableSource(source2)),52 config = config,53 assertions = assertions54)55fun codegen(56 @Language("kotlin") source1: String,57 @Language("kotlin") source2: String,58 @Language("kotlin") source3: String,59 config: KotlinCompilation.() -> Unit = {},60 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() },61) = codegen(62 sources = listOf(source(source1), source(source2), invokableSource(source3)),63 config = config,64 assertions = assertions65)66fun codegen(67 sources: List<SourceFile>,68 config: KotlinCompilation.() -> Unit = {},69 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() },70) {71 val result = compile {72 this.sources = sources.toList()73 config()74 }75 println("Result: ${result.exitCode} m: ${result.messages}")76 assertions(77 object : KotlinCompilationAssertionScope {78 override val result: KotlinCompilation.Result79 get() = result80 }81 )82}83fun singleAndMultiCodegen(84 @Language("kotlin") source1: String,85 @Language("kotlin") source2: String,86 config: KotlinCompilation.(Int) -> Unit = {},87 assertions: KotlinCompilationAssertionScope.(Boolean) -> Unit = { compilationShouldBeOk() }88) {89 singleAndMultiCodegen(90 listOf(listOf(source(source1)), listOf(invokableSource(source2))),91 config, assertions92 )93}94fun singleAndMultiCodegen(95 @Language("kotlin") source1: String,96 @Language("kotlin") source2: String,97 @Language("kotlin") source3: String,98 config: KotlinCompilation.(Int) -> Unit = {},99 assertions: KotlinCompilationAssertionScope.(Boolean) -> Unit = { compilationShouldBeOk() }100) {101 singleAndMultiCodegen(102 listOf(listOf(source(source1)), listOf(source(source2)), listOf(invokableSource(source3))),103 config, assertions104 )105}106fun singleAndMultiCodegen(107 sources: List<List<SourceFile>>,108 config: KotlinCompilation.(Int) -> Unit = {},109 assertions: KotlinCompilationAssertionScope.(Boolean) -> Unit = { compilationShouldBeOk() }110) {111 codegen(sources.flatten(), {112 workingDir = Files.createTempDirectory("single-compilation").toFile()113 moduleName = "single-compilation"114 config(-1)115 }, { assertions(false) })116 multiCodegen(sources, {117 workingDir = Files.createTempDirectory("multi-compilation-$it").toFile()118 config(it)119 }, { assertions(true) })120}121fun multiCodegen(122 @Language("kotlin") source1: String,123 @Language("kotlin") source2: String,124 config: KotlinCompilation.(Int) -> Unit = {},125 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() }126) {127 multiCodegen(128 listOf(listOf(source(source1)), listOf(invokableSource(source2))),129 config,130 assertions131 )132}133fun multiCodegen(134 @Language("kotlin") source1: String,135 @Language("kotlin") source2: String,136 @Language("kotlin") source3: String,137 config: KotlinCompilation.(Int) -> Unit = {},138 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() }139) {140 multiCodegen(141 listOf(142 listOf(source(source1)), listOf(source(source2)), listOf(143 invokableSource(source3)144 )145 ), config, assertions146 )147}148fun multiCodegen(149 sources: List<List<SourceFile>>,150 config: KotlinCompilation.(Int) -> Unit = {},151 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() }152) {153 val prevCompilations = mutableListOf<KotlinCompilation>()154 val results = sources.mapIndexed { index, sourceFiles ->155 compile {156 this.workingDir = Files.createTempDirectory("multi-compilation-$index").toFile()157 this.sources = sourceFiles158 this.moduleName = "multi-compilation-$index"159 this.classpaths += prevCompilations.map { it.classesDir }160 config(index)161 prevCompilations += this162 }163 }164 object : KotlinCompilationAssertionScope {165 override val result: KotlinCompilation.Result166 get() = results.last()167 override val classLoader: ClassLoader = URLClassLoader(168 results.flatMap { it.classLoader.urLs.toList() }169 .toTypedArray()170 )171 }.assertions()172}173fun multiPlatformCodegen(174 @Language("kotlin") commonSource: String,175 @Language("kotlin") platformSource: String,176 config: KotlinCompilation.() -> Unit = {},177 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() },178) {179 multiPlatformCodegen(180 commonSources = listOf(source(commonSource)),181 platformSources = listOf(invokableSource(platformSource)),182 config = config,183 assertions = assertions184 )185}186fun multiPlatformCodegen(187 commonSources: List<SourceFile>,188 platformSources: List<SourceFile>,189 config: KotlinCompilation.() -> Unit = {},190 assertions: KotlinCompilationAssertionScope.() -> Unit = { compilationShouldBeOk() },191) {192 val result = compile {193 kotlincArguments += "-Xmulti-platform=true"194 commonSources195 .map {196 SourceFileAccessor.writeIfNeeded(it, workingDir.resolve("sources")197 .also { it.mkdirs() })198 }199 .forEach { kotlincArguments += "-Xcommon-sources=$it" }200 this.sources = platformSources + commonSources201 config(this)202 }203 assertions(204 object : KotlinCompilationAssertionScope {205 override val result: KotlinCompilation.Result206 get() = result207 }208 )209}210fun compilation(block: KotlinCompilation.() -> Unit = {}) = KotlinCompilation().apply {211 compilerPlugins = listOf(EssentialsComponentRegistrar())212 commandLineProcessors = listOf(EssentialsCommandLineProcessor())213 useIR = true214 jvmTarget = "1.8"215 verbose = false216 inheritClassPath = true217 kotlincArguments += "-XXLanguage:+NewInference"218 dumpAllFiles = true219 block()220}221fun compile(block: KotlinCompilation.() -> Unit = {}) = compilation(block).compile()222fun KotlinCompilationAssertionScope.compilationShouldBeOk() {223 result.exitCode shouldBe KotlinCompilation.ExitCode.OK224}225interface KotlinCompilationAssertionScope {226 val result: KotlinCompilation.Result227 val classLoader: ClassLoader get() = result.classLoader228}229@JvmName("invokeSingleFileTypeless")230fun KotlinCompilationAssertionScope.invokeSingleFile(vararg args: Any?): Any? =231 invokeSingleFile<Any?>(*args)232fun <T> KotlinCompilationAssertionScope.invokeSingleFile(vararg args: Any?): T {233 val generatedClass = classLoader.getSingleClass().java234 return generatedClass.declaredMethods235 .single { it.name == "invoke" && it.parameterTypes.size == args.size }236 .invoke(null, *args) as T237}238private fun ClassLoader.getSingleClass(): KClass<*> =239 loadClass("com.ivianuu.essentials.integrationtests.FileKt").kotlin240fun KotlinCompilationAssertionScope.compilationShouldHaveFailed(message: String? = null) {241 result.exitCode shouldBe KotlinCompilation.ExitCode.COMPILATION_ERROR242 message?.let { shouldContainMessage(message) }243}244fun KotlinCompilationAssertionScope.shouldContainMessage(message: String) {245 result.messages shouldContain message246}247fun KotlinCompilationAssertionScope.shouldNotContainMessage(message: String) {248 result.messages shouldNotContain message249}250@Suppress("Assert")251inline fun KotlinCompilationAssertionScope.irAssertions(block: (String) -> Unit) {252 compilationShouldBeOk()253 result.outputDirectory254 .parentFile255 .resolve("injekt/dump")256 .walkTopDown()257 .filter { it.isFile }258 .map { it.readText() }259 .joinToString("\n")260 .also {261 assert(it.isNotEmpty()) {262 "Source is empty"263 }264 }265 .let(block)266}267@Suppress("Assert")268fun KotlinCompilationAssertionScope.irShouldContain(times: Int, text: String) {269 irAssertions {270 val matchesCount = it.countMatches(text)271 assert(matchesCount == times) {272 "expected '$text' $times times but was found $matchesCount times in '$it'"273 }274 }275}276private fun String.countMatches(other: String): Int = split(other)277 .dropLastWhile { it.isEmpty() }.size - 1278@Suppress("Assert")279fun KotlinCompilationAssertionScope.irShouldNotContain(text: String) {280 irAssertions {281 assert(text !in it) {282 "'$text' in source '$it'"283 }284 }285}...
InstancePerLeafSpecRunner.kt
Source:InstancePerLeafSpecRunner.kt
1package io.kotest.engine.spec.runners2import io.kotest.common.ExperimentalKotest3import io.kotest.common.flatMap4import io.kotest.core.concurrency.CoroutineDispatcherFactory5import io.kotest.core.config.ProjectConfiguration6import io.kotest.core.descriptors.Descriptor7import io.kotest.core.descriptors.root8import io.kotest.core.spec.Spec9import io.kotest.core.test.NestedTest10import io.kotest.core.test.TestCase11import io.kotest.core.test.TestResult12import io.kotest.core.test.TestScope13import io.kotest.engine.listener.TestEngineListener14import io.kotest.engine.spec.Materializer15import io.kotest.engine.spec.SpecExtensions16import io.kotest.engine.spec.SpecRunner17import io.kotest.engine.test.TestCaseExecutionListener18import io.kotest.engine.test.TestCaseExecutor19import io.kotest.engine.test.scheduler.TestScheduler20import io.kotest.engine.test.scopes.DuplicateNameHandlingTestScope21import io.kotest.mpp.Logger22import io.kotest.mpp.bestName23import kotlinx.coroutines.coroutineScope24import java.util.*25import java.util.concurrent.atomic.AtomicInteger26import kotlin.coroutines.CoroutineContext27@ExperimentalKotest28internal class InstancePerLeafSpecRunner(29 listener: TestEngineListener,30 scheduler: TestScheduler,31 private val defaultCoroutineDispatcherFactory: CoroutineDispatcherFactory,32 private val configuration: ProjectConfiguration,33) : SpecRunner(listener, scheduler, configuration) {34 private val logger = Logger(InstancePerLeafSpecRunner::class)35 private val extensions = SpecExtensions(configuration.registry)36 private val results = mutableMapOf<TestCase, TestResult>()37 // keeps track of tests we've already discovered38 private val seen = mutableSetOf<Descriptor>()39 // keeps track of tests we've already notified the listener about40 private val ignored = mutableSetOf<Descriptor>()41 private val started = mutableSetOf<Descriptor>()42 // we keep a count to break ties (first discovered)43 data class Enqueued(val testCase: TestCase, val count: Int)44 private val counter = AtomicInteger(0)45 // the queue contains tests discovered to run next. We always run the tests with the "furthest" path first.46 private val queue = PriorityQueue(Comparator<Enqueued> { o1, o2 ->47 val o1s = o1.testCase.descriptor.depth()48 val o2s = o2.testCase.descriptor.depth()49 if (o1s == o2s) o1.count.compareTo(o2.count) else o2s.compareTo(o1s)50 })51 // enqueues a test case that will execute in it's own spec instance52 private fun enqueue(testCase: TestCase) {53 queue.add(Enqueued(testCase, counter.incrementAndGet()))54 }55 /**56 * The intention of this runner is that each leaf [TestCase] executes in its own instance57 * of the containing [Spec] class.58 */59 override suspend fun execute(spec: Spec): Result<Map<TestCase, TestResult>> =60 runCatching {61 // we start by queuing up each root test to run in its own spec instance62 // when we find a leaf test for that instance, the spec is coloured and cannot be63 // used for further leaf tests.64 materializer.materialize(spec).forEach { root -> enqueue(root) }65 // with the queue seeded with the roots, we can keep picking a test from the queue66 // until it is empty. When it is empty that means all tests have finished and nothing67 // new is left to be found to be added to the queue68 while (queue.isNotEmpty()) {69 val (testCase, _) = queue.remove()70 executeInCleanSpec(testCase).getOrThrow()71 }72 results73 }74 private suspend fun executeInCleanSpec(test: TestCase): Result<Spec> {75 return createInstance(test.spec::class).flatMap { spec ->76 extensions.intercept(spec) {77 locateAndRunRoot(spec, test)78 } ?: Result.success(spec)79 }80 }81 // when we start a test from the queue, we must find the root test that is the ancestor of our82 // target test and begin executing that.83 private suspend fun locateAndRunRoot(spec: Spec, test: TestCase): Result<Spec> = runCatching {84 val root = materializer.materialize(spec)85 .firstOrNull { it.descriptor == test.descriptor.root() }86 ?: throw error("Unable to locate root test ${test.descriptor.path()}")87 logger.log { Pair(spec::class.bestName(), "Searching root '${root.name.testName}' for '${test.name.testName}'") }88 extensions.beforeSpec(spec).getOrThrow()89 locateAndRunRoot(root, test)90 extensions.afterSpec(spec).getOrThrow()91 spec92 }93 private suspend fun locateAndRunRoot(test: TestCase, target: TestCase) {94 logger.log { Pair(test.name.testName, "Executing test in search of target '${target.name.testName}'") }95 coroutineScope {96 val context = object : TestScope {97 var open = true98 override val testCase: TestCase = test99 override val coroutineContext: CoroutineContext = this@coroutineScope.coroutineContext100 override suspend fun registerTestCase(nested: NestedTest) {101 val t = Materializer(configuration).materialize(nested, testCase)102 // if this test is our target then we definitely run it103 // or if the test is on the path to our target we must run it104 if (t.descriptor.isOnPath(target.descriptor)) {105 open = false106 seen.add(t.descriptor)107 locateAndRunRoot(t, target)108 // otherwise, if we're already past our target we are finding new tests and so109 // the first new test we run, the rest we queue110 } else if (target.descriptor.isOnPath(t.descriptor)) {111 if (seen.add(t.descriptor)) {112 if (open) {113 open = false114 locateAndRunRoot(t, target)115 } else {116 enqueue(t)117 }118 }119 }120 }121 }122 val context2 = DuplicateNameHandlingTestScope(configuration.duplicateTestNameMode, context)123 val testExecutor = TestCaseExecutor(124 object : TestCaseExecutionListener {125 override suspend fun testStarted(testCase: TestCase) {126 if (started.add(testCase.descriptor)) {127 logger.log { Pair(test.name.testName, "Notifying test started '${testCase.name.testName}'") }128 listener.testStarted(testCase)129 }130 }131 override suspend fun testIgnored(testCase: TestCase, reason: String?) {132 if (ignored.add(testCase.descriptor))133 logger.log { Pair(test.name.testName, "Notifying test ignored '${testCase.name.testName}'") }134 listener.testIgnored(testCase, reason)135 }136 override suspend fun testFinished(testCase: TestCase, result: TestResult) {137 if (!queue.any { it.testCase.descriptor.isDescendentOf(testCase.descriptor) }) {138 logger.log { Pair(test.name.testName, "Notifying test finished '${testCase.name.testName}'") }139 listener.testFinished(testCase, result)140 }141 }142 },143 defaultCoroutineDispatcherFactory,144 configuration,145 )146 val result = testExecutor.execute(test, context2)147 results[test] = result148 }149 }150}...
InstancePerTestSpecRunner.kt
Source:InstancePerTestSpecRunner.kt
1package io.kotest.engine.spec.runners2import io.kotest.common.ExperimentalKotest3import io.kotest.common.flatMap4import io.kotest.core.concurrency.CoroutineDispatcherFactory5import io.kotest.core.config.ProjectConfiguration6import io.kotest.core.spec.Spec7import io.kotest.core.test.NestedTest8import io.kotest.core.test.TestCase9import io.kotest.core.test.TestResult10import io.kotest.core.test.TestScope11import io.kotest.core.test.TestType12import io.kotest.engine.listener.TestEngineListener13import io.kotest.engine.spec.Materializer14import io.kotest.engine.spec.SpecExtensions15import io.kotest.engine.spec.SpecRunner16import io.kotest.engine.test.TestCaseExecutionListener17import io.kotest.engine.test.TestCaseExecutor18import io.kotest.engine.test.scheduler.TestScheduler19import io.kotest.engine.test.scopes.DuplicateNameHandlingTestScope20import io.kotest.mpp.log21import kotlinx.coroutines.coroutineScope22import java.util.concurrent.ConcurrentHashMap23import kotlin.coroutines.CoroutineContext24/**25 * Implementation of [SpecRunner] that executes each [TestCase] in a fresh instance26 * of the [Spec] class.27 *28 * This differs from the [InstancePerLeafSpecRunner] in that29 * every single test, whether of type [TestType.Test] or [TestType.Container], will be30 * executed separately. Branch tests will ultimately be executed once as a standalone31 * test, and also as part of the "path" to any nested tests.32 *33 * So, given the following structure:34 *35 * outerTest {36 * innerTestA {37 * // test38 * }39 * innerTestB {40 * // test41 * }42 * }43 *44 * Three spec instances will be created. The execution process will be:45 *46 * spec1 = instantiate spec47 * spec1.outerTest48 * spec2 = instantiate spec49 * spec2.outerTest50 * spec2.innerTestA51 * spec3 = instantiate spec52 * spec3.outerTest53 * spec3.innerTestB54 */55@ExperimentalKotest56internal class InstancePerTestSpecRunner(57 listener: TestEngineListener,58 schedule: TestScheduler,59 private val defaultCoroutineDispatcherFactory: CoroutineDispatcherFactory,60 private val configuration: ProjectConfiguration,61) : SpecRunner(listener, schedule, configuration) {62 private val extensions = SpecExtensions(configuration.registry)63 private val results = ConcurrentHashMap<TestCase, TestResult>()64 /**65 * The intention of this runner is that each [TestCase] executes in its own instance66 * of the containing [Spec] class. Therefore, when we begin executing a test case from67 * the queue, we must first instantiate a new spec, and begin execution on _that_ instance.68 *69 * As test lambdas are executed, nested test cases will be registered, these should be ignored70 * if they are not an ancestor of the target. If they are then we can step into them, and71 * continue recursively until we find the target.72 *73 * Once the target is found it can be executed as normal, and any test lambdas it contains74 * can be registered back with the stack for execution later.75 */76 override suspend fun execute(spec: Spec): Result<Map<TestCase, TestResult>> =77 runCatching {78 launch(spec) {79 executeInCleanSpec(it)80 .getOrThrow()81 }82 results83 }84 /**85 * The intention of this runner is that each [TestCase] executes in its own instance86 * of the containing [Spec] class. Therefore, when we begin executing a test case from87 * the queue, we must first instantiate a new spec, and begin execution on _that_ instance.88 *89 * As test lambdas are executed, nested test cases will be registered, these should be ignored90 * if they are not an ancestor of the target. If they are then we can step into them, and91 * continue recursively until we find the target.92 *93 * Once the target is found it can be executed as normal, and any test lambdas it contains94 * can be registered back with the stack for execution later.95 */96 private suspend fun executeInCleanSpec(test: TestCase): Result<Spec> {97 return createInstance(test.spec::class)98 .flatMap { spec ->99 runCatching {100 extensions.intercept(spec) {101 extensions.beforeSpec(spec)102 .flatMap { run(it, test) }103 .flatMap { extensions.afterSpec(it) }104 }105 }.map { spec }106 }107 }108 private suspend fun run(spec: Spec, test: TestCase): Result<Spec> = kotlin.runCatching {109 log { "Created new spec instance $spec" }110 // we need to find the same root test but in the newly created spec111 val root = materializer.materialize(spec).first { it.descriptor.isOnPath(test.descriptor) }112 log { "Starting root test ${root.descriptor} in search of ${test.descriptor}" }113 run(root, test)114 spec115 }116 private suspend fun run(test: TestCase, target: TestCase) {117 val isTarget = test.descriptor == target.descriptor118 coroutineScope {119 val context = object : TestScope {120 override val testCase: TestCase = test121 override val coroutineContext: CoroutineContext = this@coroutineScope.coroutineContext122 override suspend fun registerTestCase(nested: NestedTest) {123 val t = Materializer(configuration).materialize(nested, testCase)124 // if we are currently executing the target, then any registered tests are new, and we125 // should begin execution of them in fresh specs126 // otherwise if the test is on the path we can continue in the same spec127 if (isTarget) {128 executeInCleanSpec(t).getOrThrow()129 } else if (t.descriptor.isOnPath(target.descriptor)) {130 run(t, target)131 }132 }133 }134 val context2 = DuplicateNameHandlingTestScope(configuration.duplicateTestNameMode, context)135 val testExecutor = TestCaseExecutor(136 object : TestCaseExecutionListener {137 override suspend fun testStarted(testCase: TestCase) {138 if (isTarget) listener.testStarted(testCase)139 }140 override suspend fun testIgnored(testCase: TestCase, reason: String?) {141 if (isTarget) listener.testIgnored(testCase, reason)142 }143 override suspend fun testFinished(testCase: TestCase, result: TestResult) {144 if (isTarget) listener.testFinished(testCase, result)145 }146 },147 defaultCoroutineDispatcherFactory,148 configuration149 )150 val result = testExecutor.execute(test, context2)151 results[test] = result152 }153 }154}...
SingleInstanceSpecRunner.kt
Source:SingleInstanceSpecRunner.kt
1package io.kotest.engine.spec.runners2import io.kotest.common.ExperimentalKotest3import io.kotest.common.flatMap4import io.kotest.core.concurrency.CoroutineDispatcherFactory5import io.kotest.core.config.ProjectConfiguration6import io.kotest.core.spec.Spec7import io.kotest.core.test.NestedTest8import io.kotest.core.test.TestCase9import io.kotest.core.test.TestResult10import io.kotest.core.test.TestScope11import io.kotest.engine.listener.TestEngineListener12import io.kotest.engine.spec.Materializer13import io.kotest.engine.spec.SpecExtensions14import io.kotest.engine.spec.SpecRunner15import io.kotest.engine.test.TestCaseExecutor16import io.kotest.engine.test.listener.TestCaseExecutionListenerToTestEngineListenerAdapter17import io.kotest.engine.test.scheduler.TestScheduler18import io.kotest.engine.test.scopes.DuplicateNameHandlingTestScope19import io.kotest.mpp.Logger20import io.kotest.mpp.bestName21import kotlinx.coroutines.coroutineScope22import java.util.concurrent.ConcurrentHashMap23import kotlin.coroutines.CoroutineContext24/**25 * Implementation of [SpecRunner] that executes all tests against the26 * same [Spec] instance. In other words, only a single instance of the spec class27 * is instantiated for all the test cases.28 */29@ExperimentalKotest30internal class SingleInstanceSpecRunner(31 listener: TestEngineListener,32 scheduler: TestScheduler,33 private val defaultCoroutineDispatcherFactory: CoroutineDispatcherFactory,34 private val configuration: ProjectConfiguration,35) : SpecRunner(listener, scheduler, configuration) {36 private val results = ConcurrentHashMap<TestCase, TestResult>()37 private val extensions = SpecExtensions(configuration.registry)38 private val logger = Logger(SingleInstanceSpecRunner::class)39 override suspend fun execute(spec: Spec): Result<Map<TestCase, TestResult>> {40 logger.log { Pair(spec::class.bestName(), "executing spec $spec") }41 suspend fun interceptAndRun(context: CoroutineContext) = runCatching {42 val rootTests = materializer.materialize(spec)43 logger.log { Pair(spec::class.bestName(), "Materialized root tests: ${rootTests.size}") }44 launch(spec) {45 logger.log { Pair(it.name.testName, "Executing test $it") }46 runTest(it, context, null)47 }48 }49 try {50 return coroutineScope {51 extensions.beforeSpec(spec)52 .flatMap { interceptAndRun(coroutineContext) }53 .flatMap { SpecExtensions(configuration.registry).afterSpec(spec) }54 .map { results }55 }56 } catch (e: Exception) {57 e.printStackTrace()58 throw e59 }60 }61 /**62 * A [TestScope] that runs discovered tests as soon as they are registered in the same spec instance.63 *64 * This implementation tracks fail fast if configured via TestCase config or globally.65 */66 inner class SingleInstanceTestScope(67 override val testCase: TestCase,68 override val coroutineContext: CoroutineContext,69 private val parentScope: SingleInstanceTestScope?,70 ) : TestScope {71 // set to true if we failed fast and should ignore further tests72 private var skipRemaining = false73 // in the single instance runner we execute each nested test as soon as they are registered74 override suspend fun registerTestCase(nested: NestedTest) {75 logger.log { Pair(testCase.name.testName, "Nested test case discovered '${nested}") }76 val nestedTestCase = Materializer(configuration).materialize(nested, testCase)77 if (skipRemaining) {78 logger.log { Pair(testCase.name.testName, "Skipping test due to fail fast") }79 listener.testIgnored(nestedTestCase, "Skipping test due to fail fast")80 } else {81 // if running this nested test results in an error, we won't launch anymore nested tests82 val result = runTest(nestedTestCase, coroutineContext, this@SingleInstanceTestScope)83 if (result.isErrorOrFailure) {84 if (testCase.config.failfast || configuration.projectWideFailFast) {85 logger.log { Pair(testCase.name.testName, "Test failed - setting skipRemaining = true") }86 skipRemaining = true87 parentScope?.skipRemaining = true88 }89 }90 }91 }92 }93 private suspend fun runTest(94 testCase: TestCase,95 coroutineContext: CoroutineContext,96 parentScope: SingleInstanceTestScope?,97 ): TestResult {98 val testExecutor = TestCaseExecutor(99 TestCaseExecutionListenerToTestEngineListenerAdapter(listener),100 defaultCoroutineDispatcherFactory,101 configuration,102 )103 val scope = DuplicateNameHandlingTestScope(104 configuration.duplicateTestNameMode,105 SingleInstanceTestScope(testCase, coroutineContext, parentScope)106 )107 val result = testExecutor.execute(testCase, scope)108 results[testCase] = result109 return result110 }111}...
Result.flatMap
Using AI Code Generation
1val result1 = Result.success(1)2val result2 = Result.success(2)3val result3 = Result.success(3)4val result4 = Result.success(4)5val result5 = Result.success(5)6val result6 = Result.success(6)7val result7 = Result.success(7)8val result8 = Result.success(8)9val result9 = Result.success(9)10val result10 = Result.success(10)11val result11 = Result.success(11)12val result12 = Result.success(12)13val result13 = Result.success(13)14val result14 = Result.success(14)15val result15 = Result.success(15)16val result16 = Result.success(16)17val result17 = Result.success(17)18val result18 = Result.success(18)19val result19 = Result.success(19)20val result20 = Result.success(20)21val result21 = Result.success(21)22val result22 = Result.success(22)23val result23 = Result.success(23)24val result24 = Result.success(24)25val result25 = Result.success(25)26val result26 = Result.success(26)27val result27 = Result.success(27)28val result28 = Result.success(28)29val result29 = Result.success(29)30val result30 = Result.success(30)31val result31 = Result.success(31)32val result32 = Result.success(32)33val result33 = Result.success(33)34val result34 = Result.success(34)35val result35 = Result.success(35)36val result36 = Result.success(36)37val result37 = Result.success(37)38val result38 = Result.success(38)39val result39 = Result.success(39)40val result40 = Result.success(40)41val result41 = Result.success(41)42val result42 = Result.success(42)43val result43 = Result.success(43)44val result44 = Result.success(44)45val result45 = Result.success(45)46val result46 = Result.success(46)47val result47 = Result.success(47)48val result48 = Result.success(48)49val result49 = Result.success(49)50val result50 = Result.success(50)51val result51 = Result.success(51)52val result52 = Result.success(52)53val result53 = Result.success(53)54val result54 = Result.success(54)55val result55 = Result.success(55)
Result.flatMap
Using AI Code Generation
1 import io.kotest.core.spec.style.StringSpec2 import io.kotest.matchers.shouldBe3 import io.kotest.common.results.flatMap4 import io.kotest.common.results.Result5 class StringSpecExample : StringSpec({6 "Result.flatMap should work" {7 val result = Result.success( 1 )8 result.flatMap { Result.success(it + 1 ) } shouldBe Result.success( 2 )9 }10})11 import io.kotest.core.spec.style.StringSpec12 import io.kotest.matchers.shouldBe13 import io.kotest.common.results.map14 import io.kotest.common.results.Result15 class StringSpecExample : StringSpec({16 "Result.map should work" {17 val result = Result.success( 1 )18 result.map { it + 1 } shouldBe Result.success( 2 )19 }20})21 import io.kotest.core.spec.style.StringSpec22 import io.kotest.matchers.shouldBe23 import io.kotest.common.results.orElse24 import io.kotest.common.results.Result25 class StringSpecExample : StringSpec({26 "Result.orElse should work" {27 val result = Result.success( 1 )28 result.orElse { Result.success( 2 ) } shouldBe Result.success( 1 )29 }30})31 import io.kotest.core.spec.style.StringSpec32 import io.kotest.matchers.shouldBe33 import io.kotest.common.results.recover34 import io.kotest.common.results.Result35 class StringSpecExample : StringSpec({36 "Result.recover should work" {37 val result = Result.failure( 1 )38 result.recover { Result.success(it + 1 ) } shouldBe Result.success( 2 )39 }40})41 import io.kotest.core.spec.style.StringSpec42 import io.kotest.matchers.shouldBe43 import io.kotest.common.results.recoverWith44 import
Result.flatMap
Using AI Code Generation
1 import io.kotest.core.spec.style.FunSpec2 import io.kotest.matchers.shouldBe3 class ResultTest : FunSpec({4 test("Result.flatMap") {5 val result = Result.success(1).flatMap { a ->6 Result.success(a + 1)7 }8 result shouldBe Result.success(2)9 }10 })
Result.flatMap
Using AI Code Generation
1 val result: Result<String> = Result.success("Hello")2 val result2: Result<String> = Result.success("Kotest")3 val result3: Result<String> = result.flatMap { r1 ->4 result2.map { r2 ->5 }6 }7[Image] [GitHub](github.com/kotest/kotest/blob/...) 8### [kotest/kotest](github.com/kotest/kotest/blob/...)
Result.flatMap
Using AI Code Generation
1val result = Result.runCatching { "Hello World" }2val either = result.flatMap { value ->3if (value.length > 5) Either.Right(value)4else Either.Left("Value is too short")5}6either shouldBe Either.Right("Hello World")7val result = Result.runCatching { "Hello World" }8val either = result.map { value ->9if (value.length > 5) Either.Right(value)10else Either.Left("Value is too short")11}12either shouldBe Either.Right("Hello World")13val result = Result.runCatching { throw RuntimeException("Error!") }14val either = result.mapError { error ->15Either.Left(error.message ?: "Unknown error")16}17either shouldBe Either.Left("Error!")18val result = Result.runCatching { throw RuntimeException("Error!") }19val either = result.onFailure { error ->20Either.Left(error.message ?: "Unknown error")21}22either shouldBe Either.Left("Error!")23val result = Result.runCatching { "Hello World" }24val either = result.onFailure { error ->25Either.Left(error.message ?: "Unknown error")26}27either shouldBe Either.Right("Hello World")28val result = Result.runCatching { "Hello World" }29val either = result.onSuccess { value ->30if (value.length > 5) Either.Right(value)31else Either.Left("Value is too short")32}33either shouldBe Either.Right("Hello World")34val result = Result.runCatching { "Hello World" }35val either = result.onSuccess { value ->36if (value.length > 5) Either.Right(value)37else Either.Left("Value is too short")38}39either shouldBe Either.Right("Hello World")
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!