Skip to content

Commit 91621be

Browse files
committed
Add e2e tests for sourcelinks of standard library of scala3
1 parent a72ecf5 commit 91621be

File tree

9 files changed

+116
-70
lines changed

9 files changed

+116
-70
lines changed

.github/workflows/scaladoc.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,25 @@ jobs:
7070
echo uplading docs to https://scala3doc.virtuslab.com/$DOC_DEST
7171
az storage container create --name $DOC_DEST --account-name scala3docstorage --public-access container
7272
az storage blob upload-batch -s scaladoc/output -d $DOC_DEST --account-name scala3docstorage
73+
74+
stdlib-sourcelinks-test:
75+
runs-on: ubuntu-latest
76+
if: "( github.event_name == 'pull_request'
77+
&& !contains(github.event.pull_request.body, '[skip ci]')
78+
&& !contains(github.event.pull_request.body, '[skip docs]')
79+
)
80+
|| contains(github.event.ref, 'scaladoc')
81+
|| contains(github.event.ref, 'scala3doc')
82+
|| contains(github.event.ref, 'master')"
83+
84+
steps:
85+
- name: Git Checkout
86+
uses: actions/checkout@v2
87+
88+
- name: Set up JDK 8
89+
uses: actions/setup-java@v1
90+
with:
91+
java-version: 8
92+
93+
- name: Test sourcelinks to stdlib
94+
run: ./project/scripts/sbt scaladoc/sourceLinksIntegrationTest:test

project/Build.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,17 @@ object Build {
11921192
// Note: the two tasks below should be one, but a bug in Tasty prevents that
11931193
val generateScalaDocumentation = inputKey[Unit]("Generate documentation for dotty lib")
11941194
val generateTestcasesDocumentation = taskKey[Unit]("Generate documentation for testcases, usefull for debugging tests")
1195-
lazy val `scaladoc` = project.in(file("scaladoc")).asScaladoc
1195+
1196+
val SourceLinksIntegrationTest = config("sourceLinksIntegrationTest") extend Test
1197+
1198+
lazy val `scaladoc` = project.in(file("scaladoc"))
1199+
.configs(SourceLinksIntegrationTest)
1200+
.asScaladoc
1201+
.settings(inConfig(SourceLinksIntegrationTest)(Defaults.testSettings))
1202+
.settings(
1203+
scalaSource in SourceLinksIntegrationTest := baseDirectory.value / "test-source-links",
1204+
test in SourceLinksIntegrationTest := ((test in SourceLinksIntegrationTest) dependsOn generateScalaDocumentation.toTask("")).value,
1205+
)
11961206
lazy val `scaladoc-nonBootstrapped` = project.in(file("scaladoc")).scaladocBasic(NonBootstrapped).settings(
11971207
// Unit tests in scaladoc depends on scaladoc-testcases
11981208
// we do not want to just cross compile them with bootstrapped and unbootstrapped compiler

scaladoc-testcases/src/toplevel.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
def toplevelDef = 123
22

3-
class ToplevelClass
3+
class ToplevelClass

scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ trait NameNormalizer { self: TastyParser =>
1717
private val ignoredKeywords: Set[String] = Set("this")
1818

1919
private def escapedName(name: String) =
20-
val simpleIdentifierRegex = raw"(?:\w+_[^\[\(\s_]+)|\w+|[^\[\(\s\w_]+".r
20+
val simpleIdentifierRegex = "([([{}]) ]|[^A-Za-z0-9$]_)".r
2121
name match
2222
case n if ignoredKeywords(n) => n
23-
case n if keywords(termName(n)) => s"`$n`"
24-
case simpleIdentifierRegex() => name
25-
case n => s"`$n`"
23+
case n if keywords(termName(n)) || simpleIdentifierRegex.findFirstIn(n).isDefined => s"`$n`"
24+
case _ => name
2625
}

scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,3 @@ trait SyntheticsSupport:
114114
typeForClass(c).asInstanceOf[dotc.core.Types.Type]
115115
.memberInfo(symbol.asInstanceOf[dotc.core.Symbols.Symbol])
116116
.asInstanceOf[TypeRepr]
117-

scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/DocFlexmarkExtension.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,4 @@ case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String)
6666
object DocFlexmarkRenderer:
6767
def render(node: Node)(renderLink: (DocLink, String) => String) =
6868
val opts = MarkdownParser.mkMarkdownOptions(Seq(DocFlexmarkRenderer(renderLink)))
69-
HtmlRenderer.builder(opts).build().render(node)
69+
HtmlRenderer.builder(opts).escapeHtml(true).build().render(node)

scaladoc/src/dotty/tools/scaladoc/util/html.scala

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@ object HTML:
1111
case class Tag(name: String):
1212
def apply(tags: TagArg*): AppliedTag = apply()(tags:_*)
1313
def apply(first: AttrArg, rest: AttrArg*): AppliedTag = apply((first +: rest):_*)()
14-
def apply(attrs: AttrArg*)(tags: TagArg*): AppliedTag = {
14+
def apply(attrs: AttrArg*)(tags: TagArg*): AppliedTag =
15+
def unpackTags(tags: TagArg*)(using sb: StringBuilder): StringBuilder =
16+
tags.foreach {
17+
case t: AppliedTag =>
18+
sb.append(t)
19+
case s: String =>
20+
sb.append(s.escapeReservedTokens)
21+
case s: Seq[AppliedTag | String] =>
22+
unpackTags(s:_*)
23+
}
24+
sb
1525
val sb = StringBuilder()
1626
sb.append(s"<$name")
1727
attrs.filter(_ != Nil).foreach{
@@ -21,22 +31,9 @@ object HTML:
2131
sb.append(" ").append(e)
2232
}
2333
sb.append(">")
24-
tags.foreach{
25-
case t: AppliedTag =>
26-
sb.append(t)
27-
case s: String =>
28-
sb.append(s.escapeReservedTokens)
29-
case s: Seq[AppliedTag | String] =>
30-
s.foreach{
31-
case a: AppliedTag =>
32-
sb.append(a)
33-
case s: String =>
34-
sb.append(s.escapeReservedTokens)
35-
}
36-
}
34+
unpackTags(tags:_*)(using sb)
3735
sb.append(s"</$name>")
3836
sb
39-
}
4037

4138
extension (s: String) private def escapeReservedTokens: String =
4239
s.replace("&", "&amp;")
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package dotty.tools.scaladoc
2+
package sourcelinks
3+
4+
import scala.io.Source
5+
import scala.jdk.CollectionConverters._
6+
import scala.util.matching.Regex
7+
import dotty.tools.scaladoc.test.BuildInfo
8+
import java.nio.file.Path
9+
import java.nio.file.Paths
10+
import org.jsoup.Jsoup
11+
import org.jsoup.nodes.Document
12+
import util.IO
13+
import org.junit.Assert.assertTrue
14+
import org.junit.Test
15+
16+
class RemoteLinksTest:
17+
18+
@Test
19+
def runTest =
20+
val mtsl = membersToSourceLinks(using testDocContext())
21+
val pageToMtsl: Map[String, List[(String, String)]] = mtsl.groupMap(_._2.split("#L").head)(v => (v._1, v._2.split("#L").last))
22+
pageToMtsl.foreach { case (link, members) =>
23+
try
24+
val doc = getDocumentFromUrl(link)
25+
members.foreach { (member, line) =>
26+
if !member.startsWith("given_") then // TODO: handle synthetic givens, for now we disable them from testing
27+
val loc = doc.select(s"#LC$line").text
28+
val memberToMatch = member.replace("`", "")
29+
assertTrue(s"Expected to find $memberToMatch at $link at line $line", loc.contains(memberToMatch))
30+
}
31+
catch
32+
case e: java.lang.IllegalArgumentException =>
33+
report.error(s"Could not open link for $link - invalid URL")(using testContext)
34+
case e: org.jsoup.HttpStatusException => e.getStatusCode match
35+
case 404 => throw AssertionError(s"Page $link does not exists")
36+
case n => report.warning(s"Could not open link for $link, return code $n")(using testContext)
37+
}
38+
assertNoErrors(testContext.reportedDiagnostics)
39+
40+
private def getDocumentFromUrl(link: String): Document =
41+
try
42+
Jsoup.connect(link).get
43+
catch
44+
case e: org.jsoup.HttpStatusException => e.getStatusCode match
45+
case 429 =>
46+
Thread.sleep(10)
47+
getDocumentFromUrl(link)
48+
case n =>
49+
throw e
50+
51+
private def membersToSourceLinks(using DocContext): List[(String, String)] =
52+
val output = Paths.get("scaladoc", "output", "scala3", "api").toAbsolutePath
53+
val mtsl = List.newBuilder[(String, String)]
54+
def processFile(path: Path): Unit =
55+
val document = Jsoup.parse(IO.read(path))
56+
if document.select("span.kind").first.text == "package" then
57+
document.select(".documentableElement").forEach { dElem =>
58+
if dElem.select("span.kind").first.text != "package" then
59+
dElem.select("dt").forEach { elem =>
60+
val content = elem.text
61+
if content == "Source" then
62+
mtsl += dElem.select(".documentableName").first.text -> elem.nextSibling.childNode(0).attr("href")
63+
}
64+
}
65+
IO.foreachFileIn(output, processFile)
66+
mtsl.result

scaladoc/test/dotty/tools/scaladoc/source-links/RemoteLinksTest.scala

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)