summaryrefslogtreecommitdiff
path: root/logcat
diff options
context:
space:
mode:
authorAlon Albert <aalbert@google.com>2022-08-01 10:43:06 -0700
committerTreeHugger Robot <treehugger-gerrit@google.com>2022-08-01 18:20:27 +0000
commit5b223faa63f399729a1c7d4fb6171ca6d77add78 (patch)
tree86b5bcc8a39194e4240d55ff4c02270ee48d55f6 /logcat
parent79a6a2f7bc8eb9940814401abe25cca80a44f795 (diff)
downloadidea-5b223faa63f399729a1c7d4fb6171ca6d77add78.tar.gz
Add a displayText Property to LogcatFilter
This cl has no user visible changes. The displayText is not used yet. It will be used downstream to render a tooltip. Bug: 240713976 Test: Added Change-Id: Idb1963da00b35869e66b5e12cf3f3628c9b40669
Diffstat (limited to 'logcat')
-rw-r--r--logcat/resources/messages/LogcatBundle.properties29
-rw-r--r--logcat/src/com/android/tools/idea/logcat/LogcatBundle.kt4
-rw-r--r--logcat/src/com/android/tools/idea/logcat/filters/LogcatFilter.kt89
-rw-r--r--logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterCompletionContributor.kt22
-rw-r--r--logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterParser.kt31
-rw-r--r--logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterParserTest.kt20
-rw-r--r--logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterTest.kt136
7 files changed, 256 insertions, 75 deletions
diff --git a/logcat/resources/messages/LogcatBundle.properties b/logcat/resources/messages/LogcatBundle.properties
index 2951adb8a38..f284cc26ac3 100644
--- a/logcat/resources/messages/LogcatBundle.properties
+++ b/logcat/resources/messages/LogcatBundle.properties
@@ -91,24 +91,29 @@ logcat.filter.completion.hint.level=Filter by min log level
logcat.filter.completion.hint.is=Filter by type of log message
logcat.filter.completion.hint.is.crash=Filter crashes
logcat.filter.completion.hint.is.stacktrace=Filter stack traces
+logcat.filter.completion.hint.age.second=second
+logcat.filter.completion.hint.age.minute=minute
+logcat.filter.completion.hint.age.hour=hour
+logcat.filter.completion.hint.age.day=day
logcat.filter.completion.hint.age=Filter by max age
-logcat.filter.completion.hint.age.30s=Filter logs from past 30 seconds
-logcat.filter.completion.hint.age.5m=Filter logs from past 5 minutes
-logcat.filter.completion.hint.age.3h=Filter logs from past 3 hours
-logcat.filter.completion.hint.age.1d=Filter logs from past day
-logcat.filter.completion.hint.package.mine=Filter logs from current project package id(s)
+logcat.filter.completion.hint.age.value=Filter logs from past {0} {1}
+logcat.filter.completion.hint.package.mine=Filter logs from current project id(s)
+logcat.filter.completion.hint.package.mine.empty=No project ids detected. Is the project synced?
logcat.filter.completion.hint.name=Specify a name for this filter
+logcat.filter.completion.hint.name.value=This filter''s name is ''{0}''
+logcat.filter.completion.hint.key.line=Log line
logcat.filter.completion.hint.key.message=Log message
logcat.filter.completion.hint.key.package=Package name
logcat.filter.completion.hint.key.process=Process name
logcat.filter.completion.hint.key.tag=Log tag
-logcat.filter.completion.hint.key={0} contains string
-logcat.filter.completion.hint.key.negated={0} does not contain string
-logcat.filter.completion.hint.key.regex={0} matches regular expression
-logcat.filter.completion.hint.key.regex.negated={0} does not match regular expression
-logcat.filter.completion.hint.key.exact={0} is exactly string
-logcat.filter.completion.hint.key.exact.negated={0} is not exactly string
-
+logcat.filter.completion.hint.value.string=string
+logcat.filter.completion.hint.value.regex=string
+logcat.filter.completion.hint.key={0} contains {1}
+logcat.filter.completion.hint.key.negated={0} does not contain {1}
+logcat.filter.completion.hint.key.regex={0} matches {1}
+logcat.filter.completion.hint.key.regex.negated={0} does not match {1}
+logcat.filter.completion.hint.key.exact={0} is exactly {1}
+logcat.filter.completion.hint.key.exact.negated={0} is not exactly {1}
logcat.filter.got.it.link.text=More information
diff --git a/logcat/src/com/android/tools/idea/logcat/LogcatBundle.kt b/logcat/src/com/android/tools/idea/logcat/LogcatBundle.kt
index 7c9ead651ea..0d69e800c88 100644
--- a/logcat/src/com/android/tools/idea/logcat/LogcatBundle.kt
+++ b/logcat/src/com/android/tools/idea/logcat/LogcatBundle.kt
@@ -18,7 +18,7 @@ package com.android.tools.idea.logcat
import com.android.tools.idea.localization.MessageBundleReference
import org.jetbrains.annotations.PropertyKey
-private const val BUNDLE_NAME = "messages.LogcatBundle"
+internal const val BUNDLE_NAME = "messages.LogcatBundle"
/**
* Message bundle for the logcat module.
@@ -27,5 +27,5 @@ internal object LogcatBundle {
private val bundleRef = MessageBundleReference(BUNDLE_NAME)
@JvmStatic
- fun message(@PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg params: String): String = bundleRef.message(key, *params)
+ fun message(@PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg params: Any): String = bundleRef.message(key, *params)
} \ No newline at end of file
diff --git a/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilter.kt b/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilter.kt
index 13ba7f1bc04..06a79acbdfc 100644
--- a/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilter.kt
+++ b/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilter.kt
@@ -15,6 +15,8 @@
*/
package com.android.tools.idea.logcat.filters
+import com.android.tools.idea.logcat.BUNDLE_NAME
+import com.android.tools.idea.logcat.LogcatBundle.message
import com.android.tools.idea.logcat.PackageNamesProvider
import com.android.tools.idea.logcat.SYSTEM_HEADER
import com.android.tools.idea.logcat.message.LogLevel
@@ -22,10 +24,14 @@ import com.android.tools.idea.logcat.message.LogLevel.ASSERT
import com.android.tools.idea.logcat.message.LogLevel.ERROR
import com.android.tools.idea.logcat.message.LogcatMessage
import com.intellij.openapi.util.TextRange
+import com.intellij.openapi.util.text.Strings
import com.intellij.psi.impl.source.tree.PsiErrorElementImpl
+import org.jetbrains.annotations.PropertyKey
+import org.jetbrains.annotations.VisibleForTesting
import java.time.Clock
import java.time.Duration
import java.time.ZoneId
+import java.util.concurrent.TimeUnit
import java.util.regex.PatternSyntaxException
/**
@@ -46,6 +52,8 @@ internal class LogcatMasterFilter(private val logcatFilter: LogcatFilter?) {
* Matches a [LogcatMessage]
*/
internal abstract class LogcatFilter(open val textRange: TextRange) {
+ abstract val displayText: String
+
/**
* Prepare the filter.
*
@@ -69,6 +77,8 @@ internal abstract class LogcatFilter(open val textRange: TextRange) {
internal abstract class ParentFilter(open val filters: List<LogcatFilter>)
: LogcatFilter(TextRange(filters.first().textRange.startOffset, filters.last().textRange.endOffset)) {
+ override val displayText: String = ""
+
override fun prepare() {
filters.forEach(LogcatFilter::prepare)
}
@@ -92,23 +102,23 @@ internal data class OrLogcatFilter(override val filters: List<LogcatFilter>) : P
override fun matches(message: LogcatMessageWrapper) = filters.any { it.matches(message) }
}
-internal enum class LogcatFilterField {
- TAG {
+internal enum class LogcatFilterField(val displayName: String) {
+ TAG(message("logcat.filter.completion.hint.key.tag")) {
override fun getValue(message: LogcatMessageWrapper) = message.logcatMessage.header.tag
},
- APP {
+ APP(message("logcat.filter.completion.hint.key.package")) {
override fun getValue(message: LogcatMessageWrapper) = message.logcatMessage.header.applicationId
},
- MESSAGE {
+ MESSAGE(message("logcat.filter.completion.hint.key.message")) {
override fun getValue(message: LogcatMessageWrapper) = message.logcatMessage.message
},
- LINE {
+ LINE(message("logcat.filter.completion.hint.key.line")) {
override fun getValue(message: LogcatMessageWrapper) = message.logLine
},
- IMPLICIT_LINE {
+ IMPLICIT_LINE(message("logcat.filter.completion.hint.key.line")) {
override fun getValue(message: LogcatMessageWrapper) = message.logLine
},
- PROCESS {
+ PROCESS(message("logcat.filter.completion.hint.key.process")) {
override fun getValue(message: LogcatMessageWrapper) = message.logcatMessage.header.processName
},
;
@@ -116,11 +126,21 @@ internal enum class LogcatFilterField {
abstract fun getValue(message: LogcatMessageWrapper): String
}
+internal abstract class FieldFilter(
+ string: String,
+ field: LogcatFilterField,
+ override val textRange: TextRange,
+ @PropertyKey(resourceBundle = BUNDLE_NAME) stringResource: String,
+) : LogcatFilter(textRange) {
+ override val displayText: String = message(stringResource, field.displayName, "'${string}'")
+
+}
+
internal data class StringFilter(
val string: String,
val field: LogcatFilterField,
override val textRange: TextRange,
-) : LogcatFilter(textRange) {
+) : FieldFilter(string, field, textRange, "logcat.filter.completion.hint.key") {
override fun matches(message: LogcatMessageWrapper) = field.getValue(message).contains(string, ignoreCase = true)
}
@@ -128,7 +148,7 @@ internal data class NegatedStringFilter(
val string: String,
val field: LogcatFilterField,
override val textRange: TextRange,
-) : LogcatFilter(textRange) {
+) : FieldFilter(string, field, textRange, "logcat.filter.completion.hint.key.negated") {
override fun matches(message: LogcatMessageWrapper) = !field.getValue(message).contains(string, ignoreCase = true)
}
@@ -136,7 +156,7 @@ internal data class ExactStringFilter(
val string: String,
val field: LogcatFilterField,
override val textRange: TextRange,
-) : LogcatFilter(textRange) {
+) : FieldFilter(string, field, textRange, "logcat.filter.completion.hint.key.exact") {
override fun matches(message: LogcatMessageWrapper) = field.getValue(message) == string
}
@@ -144,7 +164,7 @@ internal data class NegatedExactStringFilter(
val string: String,
val field: LogcatFilterField,
override val textRange: TextRange,
-) : LogcatFilter(textRange) {
+) : FieldFilter(string, field, textRange, "logcat.filter.completion.hint.key.exact.negated") {
override fun matches(message: LogcatMessageWrapper) = field.getValue(message) != string
}
@@ -152,7 +172,7 @@ internal data class RegexFilter(
val string: String,
val field: LogcatFilterField,
override val textRange: TextRange,
-) : LogcatFilter(textRange) {
+) : FieldFilter(string, field, textRange, "logcat.filter.completion.hint.key.regex") {
private val regex = try {
string.toRegex()
}
@@ -167,7 +187,7 @@ internal data class NegatedRegexFilter(
val string: String,
val field: LogcatFilterField,
override val textRange: TextRange,
-) : LogcatFilter(textRange) {
+) : FieldFilter(string, field, textRange, "logcat.filter.completion.hint.key.regex.negated") {
private val regex = try {
string.toRegex()
}
@@ -182,14 +202,44 @@ internal data class LevelFilter(
val level: LogLevel,
override val textRange: TextRange,
) : LogcatFilter(textRange) {
+ override val displayText: String = message("logcat.filter.completion.hint.level.value", level.name)
override fun matches(message: LogcatMessageWrapper) = message.logcatMessage.header.logLevel >= level
}
internal data class AgeFilter(
- val age: Duration,
+ private val text: String,
private val clock: Clock,
override val textRange: TextRange,
) : LogcatFilter(textRange) {
+ @VisibleForTesting
+ val age: Duration
+
+ override val displayText: String
+
+ init {
+ if (!text.isValidLogAge()) {
+ throw IllegalArgumentException("Invalid age: $text")
+ }
+ val count = try {
+ text.substring(0, text.length - 1).toLong()
+ }
+ catch (e: NumberFormatException) {
+ throw LogcatFilterParseException(PsiErrorElementImpl("Invalid duration: $text"))
+ }
+
+ fun pluralize(word: String, count: Long) : String = if (count == 1L) word else Strings.pluralize(word)
+
+ val (seconds, display) = when (text.last()) {
+ 's' -> Pair(count, pluralize(message("logcat.filter.completion.hint.age.second"), count))
+ 'm' -> Pair(TimeUnit.MINUTES.toSeconds(count), pluralize(message("logcat.filter.completion.hint.age.minute"), count))
+ 'h' -> Pair(TimeUnit.HOURS.toSeconds(count), pluralize(message("logcat.filter.completion.hint.age.hour"), count))
+ 'd' -> Pair(TimeUnit.DAYS.toSeconds(count), pluralize(message("logcat.filter.completion.hint.age.day"), count))
+ else -> throw LogcatFilterParseException(PsiErrorElementImpl("Invalid duration: $text")) // should not happen
+ }
+ age = Duration.ofSeconds(seconds)
+ displayText = message("logcat.filter.completion.hint.age.value", count, display)
+ }
+
override fun matches(message: LogcatMessageWrapper) =
clock.millis() - message.logcatMessage.header.timestamp.toEpochMilli() <= age.toMillis()
}
@@ -204,6 +254,11 @@ internal class ProjectAppFilter(
private var packageNames: Set<String> = emptySet()
private var packageNamesRegex: Regex? = null
+ override val displayText: String = when (packageNamesProvider.getPackageNames().size) {
+ 0 -> message("logcat.filter.completion.hint.package.mine.empty")
+ else -> "${message("logcat.filter.completion.hint.package.mine")}:\n ${packageNamesProvider.getPackageNames().joinToString("\n ")}"
+ }
+
override fun prepare() {
packageNames = packageNamesProvider.getPackageNames()
packageNamesRegex = if (packageNames.isNotEmpty()) packageNames.joinToString("|") { it.replace(".", "\\.") }.toRegex() else null
@@ -241,6 +296,8 @@ internal class ProjectAppFilter(
etc
*/
internal data class CrashFilter(override val textRange: TextRange) : LogcatFilter(textRange) {
+ override val displayText: String = message("logcat.filter.completion.hint.is.crash")
+
override fun matches(message: LogcatMessageWrapper): Boolean {
val header = message.logcatMessage.header
val level = header.logLevel
@@ -254,6 +311,8 @@ internal data class NameFilter(
val name: String,
override val textRange: TextRange,
) : LogcatFilter(textRange) {
+ override val displayText: String = message("logcat.filter.completion.hint.name.value", name)
+
override fun matches(message: LogcatMessageWrapper): Boolean = true
override fun getFilterName(): String = name
@@ -262,5 +321,7 @@ internal data class NameFilter(
private val EXCEPTION_LINE_PATTERN = Regex("\n\\s*at .+\\(.+\\)\n")
internal data class StackTraceFilter(override val textRange: TextRange) : LogcatFilter(textRange) {
+ override val displayText: String = message("logcat.filter.completion.hint.is.stacktrace")
+
override fun matches(message: LogcatMessageWrapper): Boolean = EXCEPTION_LINE_PATTERN.find(message.logcatMessage.message) != null
}
diff --git a/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterCompletionContributor.kt b/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterCompletionContributor.kt
index 50b8332f0aa..f43bb65e61c 100644
--- a/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterCompletionContributor.kt
+++ b/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterCompletionContributor.kt
@@ -53,22 +53,24 @@ private const val NAME_KEY = "name:"
private class StringKey(name: String, hint: String) {
val normalKey = "$name:"
- val normalHint = message("logcat.filter.completion.hint.key", hint)
+ val normalHint = message("logcat.filter.completion.hint.key", hint, message("logcat.filter.completion.hint.value.string"))
val negatedKey = "-$name:"
- val negatedHint = message("logcat.filter.completion.hint.key.negated", hint)
+ val negatedHint = message("logcat.filter.completion.hint.key.negated", hint,message("logcat.filter.completion.hint.value.string"))
val regexKey = "$name~:"
- val regexHint = message("logcat.filter.completion.hint.key.regex", hint)
+ val regexHint = message("logcat.filter.completion.hint.key.regex", hint, message("logcat.filter.completion.hint.value.regex"))
val regexNegatedKey = "-$name~:"
- val regexNegatedHint = message("logcat.filter.completion.hint.key.regex.negated", hint)
+ val regexNegatedHint =
+ message("logcat.filter.completion.hint.key.regex.negated", hint, message("logcat.filter.completion.hint.value.regex"))
val exactKey = "$name=:"
- val exactHint = message("logcat.filter.completion.hint.key.exact", hint)
+ val exactHint = message("logcat.filter.completion.hint.key.exact", hint, message("logcat.filter.completion.hint.value.string"))
val exactNegatedKey = "-$name=:"
- val exactNegatedHint = message("logcat.filter.completion.hint.key.exact.negated", hint)
+ val exactNegatedHint =
+ message("logcat.filter.completion.hint.key.exact.negated", hint,message("logcat.filter.completion.hint.value.string"))
val keys = setOf(normalKey, negatedKey, regexKey, regexNegatedKey, exactKey, exactNegatedKey)
}
@@ -126,10 +128,10 @@ private val ALL_KEY_LOOKUPS = BASE_KEY_LOOKUPS + LEVEL_LOOKUPS + IS_LOOKUPS + ST
}
private val AGE_LOOKUPS = listOf(
- createLookupElement("30s ", message("logcat.filter.completion.hint.age.30s")),
- createLookupElement("5m ", message("logcat.filter.completion.hint.age.5m")),
- createLookupElement("3h ", message("logcat.filter.completion.hint.age.3h")),
- createLookupElement("1d ", message("logcat.filter.completion.hint.age.1d")),
+ createLookupElement("30s ", message("logcat.filter.completion.hint.age.value", 30, message("logcat.filter.completion.hint.age.second"))),
+ createLookupElement("5m ", message("logcat.filter.completion.hint.age.value", 5, message("logcat.filter.completion.hint.age.minute"))),
+ createLookupElement("3h ", message("logcat.filter.completion.hint.age.value", 3, message("logcat.filter.completion.hint.age.hour"))),
+ createLookupElement("1d ", message("logcat.filter.completion.hint.age.value", 1, message("logcat.filter.completion.hint.age.day"))),
)
// Do not complete a key if previous char is one of these
diff --git a/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterParser.kt b/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterParser.kt
index 66dcca62cce..671a76f398a 100644
--- a/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterParser.kt
+++ b/logcat/src/com/android/tools/idea/logcat/filters/LogcatFilterParser.kt
@@ -57,8 +57,6 @@ import com.intellij.refactoring.suggested.endOffset
import com.intellij.refactoring.suggested.startOffset
import java.text.ParseException
import java.time.Clock
-import java.time.Duration
-import java.util.concurrent.TimeUnit
private val DURATION_RE = "\\d+[smhd]".toRegex()
@@ -303,7 +301,7 @@ private fun LogcatFilterLiteralExpression.toKeyFilter(
val textRange = TextRange(startOffset, endOffset)
return when (val key = firstChild.text.trim(':', '-', '~', '=')) {
"level" -> LevelFilter(lastChild.asLogLevel(), textRange)
- "age" -> AgeFilter(lastChild.asDuration(), clock, textRange)
+ "age" -> createAgeFilter(lastChild.text, clock)
"is" -> createIsFilter(lastChild.text)
"name" -> createNameFilter(lastChild.toText())
else -> {
@@ -338,6 +336,15 @@ private fun LogcatFilterLiteralExpression.toKeyFilter(
}
}
+private fun LogcatFilterLiteralExpression.createAgeFilter(text: String, clock: Clock): LogcatFilter {
+ return try {
+ AgeFilter(text, clock, TextRange(startOffset, endOffset))
+ }
+ catch (e: IllegalArgumentException) {
+ throw LogcatFilterParseException(PsiErrorElementImpl(e.message ?: "Parse error"))
+ }
+}
+
private fun LogcatFilterLiteralExpression.createIsFilter(text: String): LogcatFilter {
return when {
!StudioFlags.LOGCAT_IS_FILTER.get() -> throw LogcatFilterParseException(PsiErrorElementImpl("Invalid key: is"))
@@ -356,24 +363,6 @@ private fun PsiElement.asLogLevel(): LogLevel =
internal fun String.isValidLogLevel(): Boolean = LogLevel.getByString(lowercase()) != null
internal fun String.isValidIsFilter(): Boolean = this == "crash" || this == "stacktrace"
-private fun PsiElement.asDuration(): Duration {
- DURATION_RE.matchEntire(text) ?: throw LogcatFilterParseException(PsiErrorElementImpl("Invalid duration: $text"))
- val count = try {
- text.substring(0, text.length - 1).toLong()
- }
- catch (e: NumberFormatException) {
- throw LogcatFilterParseException(PsiErrorElementImpl("Invalid duration: $text"))
- }
- val l = when (text.last()) {
- 's' -> count
- 'm' -> TimeUnit.MINUTES.toSeconds(count)
- 'h' -> TimeUnit.HOURS.toSeconds(count)
- 'd' -> TimeUnit.DAYS.toSeconds(count)
- else -> throw LogcatFilterParseException(PsiErrorElementImpl("Invalid duration: $text")) // should not happen
- }
- return Duration.ofSeconds(l)
-}
-
internal fun String.isValidLogAge(): Boolean {
DURATION_RE.matchEntire(this) ?: return false
try {
diff --git a/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterParserTest.kt b/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterParserTest.kt
index f3fe3f06441..30803c096dc 100644
--- a/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterParserTest.kt
+++ b/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterParserTest.kt
@@ -43,8 +43,6 @@ import com.intellij.testFramework.RunsInEdt
import org.junit.Rule
import org.junit.Test
import java.time.Clock
-import java.time.Duration
-import java.util.concurrent.TimeUnit
private val KEYS = mapOf(
"tag" to TAG,
@@ -53,11 +51,11 @@ private val KEYS = mapOf(
"line" to LINE,
)
-private val AGE_VALUES = mapOf(
- "10s" to Duration.ofSeconds(10),
- "10m" to Duration.ofSeconds(TimeUnit.MINUTES.toSeconds(10)),
- "10h" to Duration.ofSeconds(TimeUnit.HOURS.toSeconds(10)),
- "10d" to Duration.ofSeconds(TimeUnit.DAYS.toSeconds(10)),
+private val AGE_VALUES = listOf(
+ "10s",
+ "10m",
+ "10h",
+ "10d",
)
private val INVALID_AGES = listOf(
@@ -192,10 +190,10 @@ class LogcatFilterParserTest {
@Test
fun parse_age() {
- for ((key, duration) in AGE_VALUES) {
+ for (key in AGE_VALUES) {
val clock = Clock.systemUTC()
- assertThat(logcatFilterParser(clock = clock).parse("age: $key")).isEqualTo(AgeFilter(duration, clock, "age: $key".asRange()))
- assertThat(logcatFilterParser(clock = clock).parse("age:$key")).isEqualTo(AgeFilter(duration, clock, "age:$key".asRange()))
+ assertThat(logcatFilterParser(clock = clock).parse("age: $key")).isEqualTo(AgeFilter(key, clock, "age: $key".asRange()))
+ assertThat(logcatFilterParser(clock = clock).parse("age:$key")).isEqualTo(AgeFilter(key, clock, "age:$key".asRange()))
}
}
@@ -210,7 +208,7 @@ class LogcatFilterParserTest {
@Test
fun isValidLogAge() {
- for (age in AGE_VALUES.keys) {
+ for (age in AGE_VALUES) {
assertThat(age.isValidLogAge()).named(age).isTrue()
}
for (age in INVALID_AGES) {
diff --git a/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterTest.kt b/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterTest.kt
index 401d82214f5..be1493811ee 100644
--- a/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterTest.kt
+++ b/logcat/testSrc/com/android/tools/idea/logcat/filters/LogcatFilterTest.kt
@@ -18,12 +18,15 @@ package com.android.tools.idea.logcat.filters
import com.android.tools.idea.logcat.FakePackageNamesProvider
import com.android.tools.idea.logcat.SYSTEM_HEADER
import com.android.tools.idea.logcat.filters.LogcatFilterField.APP
+import com.android.tools.idea.logcat.filters.LogcatFilterField.IMPLICIT_LINE
import com.android.tools.idea.logcat.filters.LogcatFilterField.LINE
import com.android.tools.idea.logcat.filters.LogcatFilterField.MESSAGE
+import com.android.tools.idea.logcat.filters.LogcatFilterField.PROCESS
import com.android.tools.idea.logcat.filters.LogcatFilterField.TAG
import com.android.tools.idea.logcat.logcatMessage
import com.android.tools.idea.logcat.message.LogLevel
import com.android.tools.idea.logcat.message.LogLevel.ASSERT
+import com.android.tools.idea.logcat.message.LogLevel.DEBUG
import com.android.tools.idea.logcat.message.LogLevel.ERROR
import com.android.tools.idea.logcat.message.LogLevel.INFO
import com.android.tools.idea.logcat.message.LogLevel.VERBOSE
@@ -51,6 +54,8 @@ class LogcatFilterTest {
@Test
fun logcatMasterFilter() {
val filter = object : LogcatFilter(EMPTY_RANGE) {
+ override val displayText: String = ""
+
override fun matches(message: LogcatMessageWrapper) = message.logcatMessage == MESSAGE1
}
assertThat(LogcatMasterFilter(filter).filter(listOf(MESSAGE1, MESSAGE2))).containsExactly(MESSAGE1)
@@ -59,6 +64,7 @@ class LogcatFilterTest {
@Test
fun logcatMasterFilter_systemMessages() {
val filter = object : LogcatFilter(EMPTY_RANGE) {
+ override val displayText: String = ""
override fun matches(message: LogcatMessageWrapper) = false
}
val systemMessage = LogcatMessage(SYSTEM_HEADER, "message")
@@ -193,12 +199,21 @@ class LogcatFilterTest {
}
@Test
- fun ageFilter() {
+ fun ageFilter_parsing() {
+ val clock = Clock.fixed(Instant.EPOCH, ZONE_ID)
+ assertThat(AgeFilter("10s", clock, EMPTY_RANGE).age).isEqualTo(Duration.ofSeconds(10))
+ assertThat(AgeFilter("5m", clock, EMPTY_RANGE).age).isEqualTo(Duration.ofMinutes(5))
+ assertThat(AgeFilter("3h", clock, EMPTY_RANGE).age).isEqualTo(Duration.ofHours(3))
+ assertThat(AgeFilter("1d", clock, EMPTY_RANGE).age).isEqualTo(Duration.ofDays(1))
+ }
+
+ @Test
+ fun ageFilter_matches() {
val clock = Clock.fixed(Instant.EPOCH, ZONE_ID)
val message = logcatMessage(timestamp = clock.instant())
- assertThat(AgeFilter(Duration.ofSeconds(10), Clock.offset(clock, Duration.ofSeconds(5)), EMPTY_RANGE).matches(message)).isTrue()
- assertThat(AgeFilter(Duration.ofSeconds(10), Clock.offset(clock, Duration.ofSeconds(15)), EMPTY_RANGE).matches(message)).isFalse()
+ assertThat(AgeFilter("10s", Clock.offset(clock, Duration.ofSeconds(5)), EMPTY_RANGE).matches(message)).isTrue()
+ assertThat(AgeFilter("10s", Clock.offset(clock, Duration.ofSeconds(15)), EMPTY_RANGE).matches(message)).isFalse()
}
@Test
@@ -230,7 +245,8 @@ class LogcatFilterTest {
val message3 = logcatMessage(logLevel = WARN, message = "Warning message from com.app2")
val message4 = logcatMessage(logLevel = ERROR, message = "Error message from com.app3")
- assertThat(ProjectAppFilter(FakePackageNamesProvider("app1", "app2"), EMPTY_RANGE).filter(listOf(message1, message2, message3, message4)))
+ assertThat(
+ ProjectAppFilter(FakePackageNamesProvider("app1", "app2"), EMPTY_RANGE).filter(listOf(message1, message2, message3, message4)))
.containsExactly(
message1,
message2,
@@ -299,7 +315,7 @@ class LogcatFilterTest {
assertThat(RegexFilter("string", TAG, EMPTY_RANGE).getFilterName()).isNull()
assertThat(NegatedRegexFilter("string", TAG, EMPTY_RANGE).getFilterName()).isNull()
assertThat(LevelFilter(INFO, EMPTY_RANGE).getFilterName()).isNull()
- assertThat(AgeFilter(Duration.ofSeconds(60), Clock.systemDefaultZone(), EMPTY_RANGE).getFilterName()).isNull()
+ assertThat(AgeFilter("60s", Clock.systemDefaultZone(), EMPTY_RANGE).getFilterName()).isNull()
assertThat(CrashFilter(EMPTY_RANGE).getFilterName()).isNull()
assertThat(StackTraceFilter(EMPTY_RANGE).getFilterName()).isNull()
}
@@ -321,13 +337,123 @@ class LogcatFilterTest {
NameFilter("name2", EMPTY_RANGE),
).getFilterName()).isEqualTo("name2")
}
+
+ @Test
+ fun displayText_stringFilter() {
+ assertThat(StringFilter("foo", APP, EMPTY_RANGE).displayText).isEqualTo("Package name contains 'foo'")
+ assertThat(StringFilter("foo", IMPLICIT_LINE, EMPTY_RANGE).displayText).isEqualTo("Log line contains 'foo'")
+ assertThat(StringFilter("foo", LINE, EMPTY_RANGE).displayText).isEqualTo("Log line contains 'foo'")
+ assertThat(StringFilter("foo", MESSAGE, EMPTY_RANGE).displayText).isEqualTo("Log message contains 'foo'")
+ assertThat(StringFilter("foo", PROCESS, EMPTY_RANGE).displayText).isEqualTo("Process name contains 'foo'")
+ assertThat(StringFilter("foo", TAG, EMPTY_RANGE).displayText).isEqualTo("Log tag contains 'foo'")
+ }
+
+ @Test
+ fun displayText_negatedStringFilter() {
+ assertThat(NegatedStringFilter("foo", APP, EMPTY_RANGE).displayText).isEqualTo("Package name does not contain 'foo'")
+ assertThat(NegatedStringFilter("foo", IMPLICIT_LINE, EMPTY_RANGE).displayText).isEqualTo("Log line does not contain 'foo'")
+ assertThat(NegatedStringFilter("foo", LINE, EMPTY_RANGE).displayText).isEqualTo("Log line does not contain 'foo'")
+ assertThat(NegatedStringFilter("foo", MESSAGE, EMPTY_RANGE).displayText).isEqualTo("Log message does not contain 'foo'")
+ assertThat(NegatedStringFilter("foo", PROCESS, EMPTY_RANGE).displayText).isEqualTo("Process name does not contain 'foo'")
+ assertThat(NegatedStringFilter("foo", TAG, EMPTY_RANGE).displayText).isEqualTo("Log tag does not contain 'foo'")
+ }
+
+ @Test
+ fun exactStringFilter() {
+ assertThat(ExactStringFilter("foo", APP, EMPTY_RANGE).displayText).isEqualTo("Package name is exactly 'foo'")
+ assertThat(ExactStringFilter("foo", IMPLICIT_LINE, EMPTY_RANGE).displayText).isEqualTo("Log line is exactly 'foo'")
+ assertThat(ExactStringFilter("foo", LINE, EMPTY_RANGE).displayText).isEqualTo("Log line is exactly 'foo'")
+ assertThat(ExactStringFilter("foo", MESSAGE, EMPTY_RANGE).displayText).isEqualTo("Log message is exactly 'foo'")
+ assertThat(ExactStringFilter("foo", PROCESS, EMPTY_RANGE).displayText).isEqualTo("Process name is exactly 'foo'")
+ assertThat(ExactStringFilter("foo", TAG, EMPTY_RANGE).displayText).isEqualTo("Log tag is exactly 'foo'")
+ }
+
+ @Test
+ fun displayText_negatedExactStringFilter() {
+ assertThat(NegatedExactStringFilter("foo", APP, EMPTY_RANGE).displayText).isEqualTo("Package name is not exactly 'foo'")
+ assertThat(NegatedExactStringFilter("foo", IMPLICIT_LINE, EMPTY_RANGE).displayText).isEqualTo("Log line is not exactly 'foo'")
+ assertThat(NegatedExactStringFilter("foo", LINE, EMPTY_RANGE).displayText).isEqualTo("Log line is not exactly 'foo'")
+ assertThat(NegatedExactStringFilter("foo", MESSAGE, EMPTY_RANGE).displayText).isEqualTo("Log message is not exactly 'foo'")
+ assertThat(NegatedExactStringFilter("foo", PROCESS, EMPTY_RANGE).displayText).isEqualTo("Process name is not exactly 'foo'")
+ assertThat(NegatedExactStringFilter("foo", TAG, EMPTY_RANGE).displayText).isEqualTo("Log tag is not exactly 'foo'")
+ }
+
+ @Test
+ fun displayText_regexFilter() {
+ assertThat(RegexFilter("foo", APP, EMPTY_RANGE).displayText).isEqualTo("Package name matches 'foo'")
+ assertThat(RegexFilter("foo", IMPLICIT_LINE, EMPTY_RANGE).displayText).isEqualTo("Log line matches 'foo'")
+ assertThat(RegexFilter("foo", LINE, EMPTY_RANGE).displayText).isEqualTo("Log line matches 'foo'")
+ assertThat(RegexFilter("foo", MESSAGE, EMPTY_RANGE).displayText).isEqualTo("Log message matches 'foo'")
+ assertThat(RegexFilter("foo", PROCESS, EMPTY_RANGE).displayText).isEqualTo("Process name matches 'foo'")
+ assertThat(RegexFilter("foo", TAG, EMPTY_RANGE).displayText).isEqualTo("Log tag matches 'foo'")
+ }
+
+ @Test
+ fun displayText_negatedRegexFilter() {
+ assertThat(NegatedRegexFilter("foo", APP, EMPTY_RANGE).displayText).isEqualTo("Package name does not match 'foo'")
+ assertThat(NegatedRegexFilter("foo", IMPLICIT_LINE, EMPTY_RANGE).displayText).isEqualTo("Log line does not match 'foo'")
+ assertThat(NegatedRegexFilter("foo", LINE, EMPTY_RANGE).displayText).isEqualTo("Log line does not match 'foo'")
+ assertThat(NegatedRegexFilter("foo", MESSAGE, EMPTY_RANGE).displayText).isEqualTo("Log message does not match 'foo'")
+ assertThat(NegatedRegexFilter("foo", PROCESS, EMPTY_RANGE).displayText).isEqualTo("Process name does not match 'foo'")
+ assertThat(NegatedRegexFilter("foo", TAG, EMPTY_RANGE).displayText).isEqualTo("Log tag does not match 'foo'")
+ }
+
+ @Test
+ fun displayText_levelFilter() {
+ assertThat(LevelFilter(VERBOSE, EMPTY_RANGE).displayText).isEqualTo("Filter by VERBOSE or higher")
+ assertThat(LevelFilter(DEBUG, EMPTY_RANGE).displayText).isEqualTo("Filter by DEBUG or higher")
+ assertThat(LevelFilter(INFO, EMPTY_RANGE).displayText).isEqualTo("Filter by INFO or higher")
+ assertThat(LevelFilter(WARN, EMPTY_RANGE).displayText).isEqualTo("Filter by WARN or higher")
+ assertThat(LevelFilter(ERROR, EMPTY_RANGE).displayText).isEqualTo("Filter by ERROR or higher")
+ assertThat(LevelFilter(ASSERT, EMPTY_RANGE).displayText).isEqualTo("Filter by ASSERT or higher")
+ }
+
+ @Test
+ fun displayText_ageFilter() {
+ assertThat(AgeFilter("1s", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 1 second")
+ assertThat(AgeFilter("5s", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 5 seconds")
+ assertThat(AgeFilter("1m", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 1 minute")
+ assertThat(AgeFilter("5m", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 5 minutes")
+ assertThat(AgeFilter("1h", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 1 hour")
+ assertThat(AgeFilter("5h", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 5 hours")
+ assertThat(AgeFilter("1d", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 1 day")
+ assertThat(AgeFilter("5d", Clock.systemDefaultZone(), EMPTY_RANGE).displayText).isEqualTo("Filter logs from past 5 days")
+ }
+
+ @Test
+ fun displayText_projectAppFilter() {
+ assertThat(ProjectAppFilter(FakePackageNamesProvider("app1", "app2"), EMPTY_RANGE).displayText).isEqualTo("""
+ Filter logs from current project id(s):
+ app1
+ app2
+ """.trimIndent())
+ assertThat(ProjectAppFilter(FakePackageNamesProvider(), EMPTY_RANGE).displayText)
+ .isEqualTo("No project ids detected. Is the project synced?")
+ }
+
+ @Test
+ fun displayText_crashFilter() {
+ assertThat(CrashFilter(EMPTY_RANGE).displayText).isEqualTo("Filter crashes")
+ }
+
+ @Test
+ fun displayText_stackTraceFilter() {
+ assertThat(StackTraceFilter(EMPTY_RANGE).displayText).isEqualTo("Filter stack traces")
+ }
+
+ @Test
+ fun displayText_nameFilter() {
+ assertThat(NameFilter("name", EMPTY_RANGE).displayText).isEqualTo("This filter's name is 'name'")
+ }
}
private class TrueFilter : LogcatFilter(EMPTY_RANGE) {
+ override val displayText: String = ""
override fun matches(message: LogcatMessageWrapper) = true
}
private class FalseFilter : LogcatFilter(EMPTY_RANGE) {
+ override val displayText: String = ""
override fun matches(message: LogcatMessageWrapper) = false
}