-
Notifications
You must be signed in to change notification settings - Fork 339
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A framework for creating error reports in metals.
- Loading branch information
1 parent
aa943d5
commit 8199993
Showing
8 changed files
with
214 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
metals/src/main/scala/scala/meta/internal/metals/ReportsProvider.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package scala.meta.internal.metals | ||
|
||
import java.io.File | ||
import java.nio.file.Files | ||
import java.nio.file.Path | ||
import java.util.zip.ZipEntry | ||
import java.util.zip.ZipOutputStream | ||
|
||
import scala.meta.internal.metals.MetalsEnrichments._ | ||
import scala.meta.io.AbsolutePath | ||
import scala.meta.io.RelativePath | ||
|
||
class Reports(workspace: AbsolutePath) { | ||
private lazy val reportsDir = | ||
workspace.resolve(Directories.reports).withExec { d => | ||
Files.createDirectories(d.toNIO) | ||
} | ||
val unsanitized = | ||
new ReportsProvider(workspace, Directories.reports.resolve("metals-full")) | ||
val incognito = | ||
new ReportsProvider(workspace, Directories.reports.resolve("metals")) | ||
val bloop = | ||
new ReportsProvider(workspace, Directories.reports.resolve("bloop")) | ||
|
||
def all: List[ReportsProvider] = List(unsanitized, incognito, bloop) | ||
def allToZip: List[ReportsProvider] = List(incognito, bloop) | ||
|
||
def zipReports(): Path = { | ||
val path = reportsDir.resolve(Reports.ZIP_FILE_NAME).toNIO | ||
val zipOut = new ZipOutputStream(Files.newOutputStream(path)) | ||
|
||
for { | ||
reportsProvider <- allToZip | ||
report <- reportsProvider.getReports | ||
} { | ||
val zipEntry = new ZipEntry(report.name) | ||
zipOut.putNextEntry(zipEntry) | ||
zipOut.write(Files.readAllBytes(report.toPath)) | ||
} | ||
zipOut.close() | ||
|
||
path | ||
} | ||
|
||
def cleanUpOldReports( | ||
maxReportsNumber: Int = Reports.MAX_NUMBER_OF_REPORTS | ||
): Unit = { | ||
all.foreach(_.cleanUpOldReports(maxReportsNumber)) | ||
} | ||
|
||
def deleteAll(): Unit = { | ||
all.foreach(_.deleteAll()) | ||
Files.delete(reportsDir.resolve(Reports.ZIP_FILE_NAME).toNIO) | ||
} | ||
} | ||
|
||
class ReportsProvider(workspace: AbsolutePath, pathToReports: RelativePath) { | ||
private lazy val reportsDir = | ||
workspace.resolve(pathToReports).withExec { d => | ||
Files.createDirectories(d.toNIO) | ||
} | ||
|
||
private lazy val userHome = Option(System.getProperty("user.home")) | ||
|
||
def createReport(name: String, text: String): AbsolutePath = | ||
reportsDir | ||
.resolve(s"r_${name}_${System.currentTimeMillis()}") | ||
.withExec(_.writeText(sanitize(text))) | ||
|
||
private def sanitize(text: String) = { | ||
val textAfterWokspaceReplace = | ||
text.replaceAll(workspace.toString(), Reports.WORKSPACE_STR) | ||
userHome | ||
.map(textAfterWokspaceReplace.replaceAll(_, Reports.HOME_STR)) | ||
.getOrElse(textAfterWokspaceReplace) | ||
} | ||
|
||
def cleanUpOldReports( | ||
maxReportsNumber: Int = Reports.MAX_NUMBER_OF_REPORTS | ||
): List[Report] = { | ||
val reports = getReports | ||
if (reports.length > maxReportsNumber) { | ||
val filesToDelete = reports | ||
.sortBy(_.timestamp) | ||
.slice(0, reports.length - maxReportsNumber) | ||
filesToDelete.foreach { f => Files.delete(f.toPath) } | ||
filesToDelete | ||
} else List() | ||
} | ||
|
||
def getReports: List[Report] = { | ||
val reportsDir = workspace.resolve(pathToReports) | ||
if (reportsDir.exists && reportsDir.isDirectory) { | ||
reportsDir.toFile.listFiles().toList.map(Report.fromFile(_)).collect { | ||
case Some(l) => l | ||
} | ||
} else List() | ||
} | ||
|
||
def deleteAll(): Unit = getReports.foreach(r => Files.delete(r.toPath)) | ||
} | ||
|
||
object Reports { | ||
val MAX_NUMBER_OF_REPORTS = 30 | ||
val WORKSPACE_STR = "<WORKSPACE>" | ||
val HOME_STR = "<HOME>" | ||
val ZIP_FILE_NAME = "reports.zip" | ||
} | ||
|
||
case class Report(file: File, timestamp: Long) { | ||
def toPath: Path = file.toPath() | ||
def name: String = file.getName() | ||
} | ||
|
||
object Report { | ||
def fromFile(file: File): Option[Report] = { | ||
val reportRegex = "r_.*_([-+]?[0-9]+)".r | ||
file.getName() match { | ||
case reportRegex(time) => Some(Report(file, time.toLong)) | ||
case _: String => None | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package tests | ||
|
||
import java.nio.file.Files | ||
import java.nio.file.Paths | ||
|
||
import scala.meta.internal.metals.Report | ||
import scala.meta.internal.metals.Reports | ||
import scala.meta.io.AbsolutePath | ||
|
||
class ReportsSuite extends BaseSuite { | ||
val workspace: AbsolutePath = AbsolutePath(Paths.get(".")) | ||
val reportsProvider = new Reports(workspace) | ||
|
||
def exampleText(workspaceStr: String = workspace.toString()): String = | ||
s"""|An error happend in the file: | ||
|${workspaceStr}/WrongFile.scala | ||
|""".stripMargin | ||
|
||
override def afterEach(context: AfterEach): Unit = { | ||
reportsProvider.deleteAll() | ||
super.afterEach(context) | ||
} | ||
|
||
test("create-report") { | ||
val path = | ||
reportsProvider.incognito.createReport("test_error", exampleText()) | ||
val obtained = Files.readString(path.toNIO) | ||
assertEquals(exampleText(Reports.WORKSPACE_STR), obtained) | ||
assert(Report.fromFile(path.toFile).nonEmpty) | ||
} | ||
|
||
test("delete-old-reports") { | ||
reportsProvider.incognito.createReport("some_test_error_old", exampleText()) | ||
reportsProvider.incognito.createReport( | ||
"some_different_test_error_old", | ||
exampleText(), | ||
) | ||
reportsProvider.incognito.createReport("some_test_error_new", exampleText()) | ||
reportsProvider.incognito.createReport( | ||
"some_different_test_error_new", | ||
exampleText(), | ||
) | ||
val deleted = reportsProvider.incognito.cleanUpOldReports(2) | ||
assertEquals(deleted.length, 2) | ||
deleted.foreach(f => assert(f.name.contains("old"))) | ||
val reports = reportsProvider.incognito.getReports | ||
assertEquals(reports.length, 2) | ||
reports.foreach(f => assert(f.name.contains("new"))) | ||
} | ||
|
||
test("zip-reports") { | ||
reportsProvider.incognito.createReport("test_error", exampleText()) | ||
reportsProvider.incognito.createReport( | ||
"different_test_error", | ||
exampleText(), | ||
) | ||
val pathToZip = reportsProvider.zipReports() | ||
assertEquals(pathToZip.toFile.getName(), Reports.ZIP_FILE_NAME) | ||
} | ||
} |