wip ~~
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt
index 4e10d39..2f04a68 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/invalidation.kt
@@ -55,6 +55,8 @@
 
     val dirtyFiles = mutableSetOf<String>()
 
+//    println("Invalidation Phase #0")
+
     for ((index, file) in libraryFiles.withIndex()) {
 
         // 1. get cached fingerprints
@@ -63,6 +65,8 @@
         // 2. calculate new fingerprints
         val fileNewFingerprint = library.fingerprint(index)
 
+//        println("  Finger-prints: old = $fileOldFingerprint, new = $fileNewFingerprint for file $file")
+
         if (fileOldFingerprint != fileNewFingerprint) {
             fileFingerPrints[file] = fileNewFingerprint
             cachedInlineHashesForFile.remove(file)
@@ -72,6 +76,10 @@
         }
     }
 
+    val p1 = dirtyFiles.size
+//    println("Phase #1 discovered ${dirtyFiles.size} dirty files")
+//    println("Dirty files #1: ${dirtyFiles.joinToString(",", "[", "]")}")
+
     // 4. extend dirty set with inline functions
 
     val graphCache = mutableMapOf<FilePath, Collection<Pair<IdSignature, TransHash>>>()
@@ -104,8 +112,12 @@
         }
     } while (oldSize != dirtyFiles.size)
 
+//    println("Phase #2 discovered ${dirtyFiles.size - p1} more dirty files")
+//    println("Dirty files #2: ${dirtyFiles.joinToString(",", "[", "]")}")
+
     // 5. invalidate file caches
     for (dirty in dirtyFiles) {
+//        println("   invalidate cache for file ${dirty}")
         cacheConsumer.invalidateForFile(dirty)
     }
 
@@ -384,6 +396,9 @@
             currentLibraryCacheProvider.inlineHashes(filePath) { s -> sigReader.deserializeIdSignature(s) }
     }
 
+//    println("Invalidate cache for library ${library.libraryName}")
+//    println("Files: ${libraryFiles.joinToString(",", "[", "]")}")
+
     val dirtySet = invalidateCacheForModule(
         library,
         libraryFiles,
diff --git a/js/js.tests/build.gradle.kts b/js/js.tests/build.gradle.kts
index 15efaff..7b01042 100644
--- a/js/js.tests/build.gradle.kts
+++ b/js/js.tests/build.gradle.kts
@@ -349,7 +349,7 @@
     setUpBoxTests()
 }
 
-projectTest("invalidationTest") {
+projectTest("invalidationTest", jUnitMode = JUnitMode.JUnit5) {
     workingDir = rootDir
 
     include("org/jetbrains/kotlin/incremental/*")
diff --git a/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt b/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt
index 8e53c6a..caccf66 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt
@@ -117,6 +117,14 @@
         return copy
     }
 
+    private fun refreshFileSystem() {
+
+        val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
+        fileSystem.refresh(false /* sync */)
+//        System.out.flush()
+
+    }
+
     private fun executeProjectSteps(
         projectInfo: ProjectInfo,
         moduleInfos: Map<String, ModuleInfo>,
@@ -132,8 +140,10 @@
                 val moduleInfo = moduleInfos[module] ?: error("No module info found for $module")
                 val moduleStep = moduleInfo.steps[projStep.id]
                 for (modification in moduleStep.modifications) {
+//                    println("Execute step for module $module at step ${projStep.id}")
                     modification.execute(moduleTestDir, moduleSourceDir)
                 }
+                refreshFileSystem()
 
                 val dependencies = moduleStep.dependencies.map { resolveModuleArtifact(it, buildDir) }
 
@@ -182,9 +192,11 @@
             val actualDirtyFiles = invalidatedDirtyFiles?.map { File(it).canonicalPath } ?: sourceDir.filteredKtFiles().map { it.canonicalPath }
             val expectedDirtyFilesCanonical = expectedDirtyFiles.map { it.canonicalPath }
 
-            JUnit5Assertions.assertSameElements(expectedDirtyFilesCanonical, actualDirtyFiles) { "For module $moduleKlibFile" }
+            JUnit5Assertions.assertSameElements(expectedDirtyFilesCanonical, actualDirtyFiles) { "For module $moduleKlibFile at step $stepId" }
         }
 
+//        println("////-- === Process step $stepId for module ${moduleKlibFile.name} === --\\\\\\\\")
+
         val dependenciesPaths = mutableListOf<String>()
         dependencies.mapTo(dependenciesPaths) { it.canonicalPath }
         dependenciesPaths.add(moduleKlibFile.canonicalPath)
@@ -203,15 +215,28 @@
             val filePaths = expectedDirtyFiles.joinToString(",", "[", "]")
             "Up to date is not expected for module $moduleKlibFile at step $stepId. Expected dirtyFiles are $filePaths"
         }
+
+//        println("\\\\\\\\-- === Processed step $stepId for module ${moduleKlibFile.name} === --////")
+
     }
 
     private fun KotlinCoreEnvironment.createPsiFile(fileName: String): KtFile {
         val psiManager = PsiManager.getInstance(project)
         val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
 
+//        val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName")
+//        fileSystem.refresh(false)
+//        System.out.flush()
         val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName")
-
-        return psiManager.findFile(file) as KtFile
+//        file.refresh(false, true)
+//        psiManager.dropResolveCaches()
+        return (psiManager.findFile(file) as KtFile).also {
+            if (file.name.endsWith("l1.kt")) {
+                println(it.text)
+                val diskText = file.canonicalPath?.let { File(it).readText() } ?: "<NA>"
+                println(diskText)
+            }
+        }
     }
 
     private fun buildArtifact(
@@ -227,6 +252,12 @@
 
         val sourceFiles = sourceDir.filteredKtFiles().map { environment.createPsiFile(it.canonicalPath) }
 
+//        println("Compile files")
+//        for (sf in sourceFiles) {
+//            println("  ${sf.name}")
+//            println("${sf.text}")
+//        }
+
         val sourceModule = prepareAnalyzedSourceModule(
             projectJs,
             sourceFiles,
diff --git a/js/js.tests/test/org/jetbrains/kotlin/incremental/TestModel.kt b/js/js.tests/test/org/jetbrains/kotlin/incremental/TestModel.kt
index efbe0f1..e0e95aa 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/incremental/TestModel.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/incremental/TestModel.kt
@@ -102,7 +102,7 @@
             when (op) {
                 "libs" -> {
                     val args = splitted[1]
-                    args.split(",").forEach { order.add(it.trim()) }
+                    args.split(",").filter { it.isNotBlank() }.forEach { order.add(it.trim()) }
                 }
                 else -> println(diagnosticMessage("Unknown op $op", line))
             }
@@ -135,7 +135,7 @@
             when {
                 op == MODULES_LIST -> {
                     val arguments = splitted[1]
-                    arguments.split(",").forEach { libraries.add(it.trim()) }
+                    arguments.split(",").filter { it.isNotBlank() }.forEach { libraries.add(it.trim()) }
                 }
                 op.matches(STEP_PATTERN.toRegex()) -> {
                     val m = STEP_PATTERN.matcher(op)
@@ -196,8 +196,8 @@
             val op = line.substring(0, opIndex)
 
             when (op) {
-                "dependencies" -> line.substring(opIndex + 1).split(",").forEach { dependencies.add(it.trim()) }
-                "dirty" -> line.substring(opIndex + 1).split(",").forEach { dirtyFiles.add(it.trim()) }
+                "dependencies" -> line.substring(opIndex + 1).split(",").filter { it.isNotBlank() }.forEach { dependencies.add(it.trim()) }
+                "dirty" -> line.substring(opIndex + 1).split(",").filter { it.isNotBlank() }.forEach { dirtyFiles.add(it.trim()) }
                 "modifications" -> modifications.addAll(parseModifications())
                 else -> println(diagnosticMessage("Unknown op $op", line))
             }
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java
index d4b206b..4103af7 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java
@@ -30,13 +30,13 @@
         KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/incremental/invalidation"), Pattern.compile("^([^_](.+))$"), null, TargetBackend.JS_IR, true);
     }
 
-    @TestMetadata("complex")
-    public void testComplex() throws Exception {
-        runTest("js/js.translator/testData/incremental/invalidation/complex/");
-    }
-
     @TestMetadata("simple")
     public void testSimple() throws Exception {
         runTest("js/js.translator/testData/incremental/invalidation/simple/");
     }
+
+    @TestMetadata("transitiveInlineFunction")
+    public void testTransitiveInlineFunction() throws Exception {
+        runTest("js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/");
+    }
 }
diff --git a/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt b/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt
index 12a0600..859b384 100644
--- a/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt
+++ b/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt
@@ -1,2 +1 @@
-
 fun foo() = 42
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt.1 b/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt.1
index 2a26d28..8e3c133 100644
--- a/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt.1
+++ b/js/js.translator/testData/incremental/invalidation/simple/lib1/l1.kt.1
@@ -1,2 +1 @@
-
 fun foo() = 33
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/l1.kt b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/l1.kt
new file mode 100644
index 0000000..d03afda
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/l1.kt
@@ -0,0 +1,2 @@
+
+inline fun foo() = 42
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/l1.kt.1 b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/l1.kt.1
new file mode 100644
index 0000000..b9ec1aa
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/l1.kt.1
@@ -0,0 +1,2 @@
+
+inline fun foo() = 33
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/module.info b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/module.info
new file mode 100644
index 0000000..5963605
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib1/module.info
@@ -0,0 +1,8 @@
+STEP 0:
+    dependencies: stdlib
+    dirty: l1.kt
+STEP 1:
+    dependencies: stdlib
+    modifications:
+        U : l1.kt.1 -> l1.kt
+    dirty: l1.kt
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib2/l2.kt b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib2/l2.kt
new file mode 100644
index 0000000..39b7239
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib2/l2.kt
@@ -0,0 +1,2 @@
+
+inline fun qux() = foo()
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib2/module.info b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib2/module.info
new file mode 100644
index 0000000..acda14b
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/lib2/module.info
@@ -0,0 +1,7 @@
+STEP 0:
+    dependencies: stdlib, lib1
+    dirty: l2.kt
+
+STEP 1:
+    dependencies: stdlib, lib1
+    dirty: l2.kt
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/main/m.kt b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/main/m.kt
new file mode 100644
index 0000000..3ed427f
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/main/m.kt
@@ -0,0 +1,2 @@
+
+fun box() = qux()
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/main/module.info b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/main/module.info
new file mode 100644
index 0000000..df595ac
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/main/module.info
@@ -0,0 +1,7 @@
+STEP 0:
+    dependencies: stdlib, lib1, lib2
+    dirty: m.kt
+
+STEP 1:
+    dependencies: stdlib, lib1, lib2
+    dirty: m.kt
\ No newline at end of file
diff --git a/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/project.info b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/project.info
new file mode 100644
index 0000000..710e856
--- /dev/null
+++ b/js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/project.info
@@ -0,0 +1,7 @@
+MODULES: lib1, lib2, main
+
+STEP 0:
+    libs: lib1, lib2, main
+
+STEP 1:
+    libs: lib1, lib2, main
\ No newline at end of file