Skip to content

Commit ab7d279

Browse files
authored
Don't let fix built-in rules to omit test dependencies in test sources (#3425)
1 parent 40beba7 commit ab7d279

File tree

3 files changed

+78
-11
lines changed

3 files changed

+78
-11
lines changed

modules/cli/src/main/scala/scala/cli/commands/fix/BuiltInRules.scala

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ object BuiltInRules extends CommandHelpers {
2828
DirectivesPreprocessingUtils.usingDirectiveWithReqsHandlers
2929
.flatMap(_.keys)
3030

31+
private lazy val directiveTestPrefix = "test."
32+
extension (strictDirective: StrictDirective) {
33+
private def hasTestPrefix: Boolean = strictDirective.key.startsWith(directiveTestPrefix)
34+
private def existsTestEquivalent: Boolean =
35+
!strictDirective.hasTestPrefix &&
36+
usingDirectivesWithTestPrefixKeysGrouped
37+
.exists(_.nameAliases.contains(directiveTestPrefix + strictDirective.key))
38+
}
39+
3140
private val newLine: String = System.lineSeparator()
3241

3342
def runRules(
@@ -114,7 +123,13 @@ object BuiltInRules extends CommandHelpers {
114123

115124
val allDirectives = for {
116125
directivesWithTestPrefix <- transformedTestDirectives.map(_.withTestPrefix)
117-
directive <- directivesWithTestPrefix ++ testDirectivesFromMain
126+
directivesWithNoTestPrefixEquivalents <-
127+
transformedTestDirectives.map {
128+
_.noTestPrefixAvailable
129+
.filter(_.existsTestEquivalent)
130+
}
131+
directive <-
132+
directivesWithTestPrefix ++ directivesWithNoTestPrefixEquivalents ++ testDirectivesFromMain
118133
} yield directive
119134

120135
createFormattedLinesAndAppend(allDirectives, projectFileContents, isTest = true)
@@ -139,7 +154,12 @@ object BuiltInRules extends CommandHelpers {
139154
.foreach(d => removeDirectivesFrom(d.position))
140155
directivesFromWritableTestInputs
141156
.filterNot(ttd => isProjectFile(ttd.positions))
142-
.foreach(ttd => removeDirectivesFrom(ttd.positions, toKeep = ttd.noTestPrefixAvailable))
157+
.foreach(ttd =>
158+
removeDirectivesFrom(
159+
position = ttd.positions,
160+
toKeep = ttd.noTestPrefixAvailable.filterNot(_.existsTestEquivalent)
161+
)
162+
)
143163
}
144164

145165
private def getProjectSources(inputs: Inputs, logger: Logger)(using
@@ -262,18 +282,15 @@ object BuiltInRules extends CommandHelpers {
262282
extractedFromSingleElement <- extractedDirectives
263283
directives = extractedFromSingleElement.directives
264284
} yield {
265-
val (withTestEquivalent, noTestEquivalent) = directives.partition { directive =>
266-
usingDirectivesWithTestPrefixKeysGrouped.exists(
267-
_.nameAliases.contains("test." + directive.key)
268-
)
269-
}
270-
285+
val (withInitialTestPrefix, noInitialTestPrefix) = directives.partition(_.hasTestPrefix)
286+
val (withTestEquivalent, noTestEquivalent) =
287+
noInitialTestPrefix.partition(_.existsTestEquivalent)
271288
val transformedToTestEquivalents = withTestEquivalent.map {
272289
case StrictDirective(key, values, _) => StrictDirective("test." + key, values)
273290
}
274291

275292
TransformedTestDirectives(
276-
withTestPrefix = transformedToTestEquivalents,
293+
withTestPrefix = transformedToTestEquivalents ++ withInitialTestPrefix,
277294
noTestPrefixAvailable = noTestEquivalent,
278295
positions = extractedFromSingleElement.position
279296
)
@@ -328,7 +345,9 @@ object BuiltInRules extends CommandHelpers {
328345
StrictDirective(key, directives.flatMap(_.values))
329346
}
330347
// group by key prefixes to create splits between groups
331-
.groupBy(dir => (if (isTest) dir.key.stripPrefix("test.") else dir.key).takeWhile(_ != '.'))
348+
.groupBy(dir =>
349+
(if (isTest) dir.key.stripPrefix(directiveTestPrefix) else dir.key).takeWhile(_ != '.')
350+
)
332351
.map { (_, directives) =>
333352
directives.flatMap(_.explodeToStringsWithColLimit()).toSeq.sorted
334353
}

modules/integration/src/test/scala/scala/cli/integration/FixBuiltInRulesTestDefinitions.scala

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,4 +481,50 @@ trait FixBuiltInRulesTestDefinitions { _: FixTestDefinitions =>
481481
}
482482
}
483483
}
484+
485+
if (!Properties.isWin)
486+
test("all test directives get extracted into project.scala") {
487+
val osLibDep = "com.lihaoyi::os-lib:0.11.3"
488+
val munitDep = "org.scalameta::munit:1.0.4"
489+
val pprintDep = "com.lihaoyi::pprint:0.9.0"
490+
val osLibDepDirective = s"//> using dependency $osLibDep"
491+
val osLibTestDepDirective = s"//> using test.dependency $osLibDep"
492+
val munitTestDepDirective = s"//> using test.dependency $munitDep"
493+
val pprintTestDepDirective = s"//> using test.dependency $pprintDep"
494+
val mainFilePath = os.rel / "Main.scala"
495+
val testFilePath = os.rel / "MyTests.test.scala"
496+
TestInputs(
497+
mainFilePath -> s"""$munitTestDepDirective
498+
|object Main extends App {
499+
| def hello: String = "Hello, world!"
500+
| println(hello)
501+
|}
502+
|""".stripMargin,
503+
testFilePath -> s"""$osLibDepDirective
504+
|$pprintTestDepDirective
505+
|import munit.FunSuite
506+
|
507+
|class MyTests extends FunSuite {
508+
| test("hello") {
509+
| pprint.pprintln(os.pwd)
510+
| assert(Main.hello == "Hello, world!")
511+
| }
512+
|}
513+
|""".stripMargin
514+
).fromRoot { root =>
515+
os.proc(TestUtil.cli, "--power", "fix", ".", extraOptions).call(cwd = root)
516+
val expectedProjectFileContents =
517+
s"""// Test
518+
|$osLibTestDepDirective
519+
|$pprintTestDepDirective
520+
|$munitTestDepDirective""".stripMargin
521+
val projectFileContents = os.read(root / projectFileName)
522+
expect(projectFileContents.trim() == expectedProjectFileContents)
523+
val mainFileContents = os.read(root / mainFilePath)
524+
expect(!mainFileContents.contains("//> using"))
525+
val testFileContents = os.read(root / testFilePath)
526+
expect(!testFileContents.contains("//> using"))
527+
os.proc(TestUtil.cli, "test", ".", extraOptions).call(cwd = root)
528+
}
529+
}
484530
}

website/docs/commands/fix.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ Files containing (experimental) `using target` directives, e.g. `//> using targe
3030
The original scope (`main` or `test`) of each extracted directive is respected. `main` scope directives are transformed
3131
them into their `test.*` equivalent when needed.
3232

33-
Note: directives won't be extracted for single-file projects.
33+
Exceptions:
34+
- directives won't be extracted for single-file projects;
35+
- directives in test inputs with no test scope equivalents won't be extracted to preserve their initial scope.
3436

3537
## `scalafix` integration
3638

0 commit comments

Comments
 (0)