diff options
author | Alon Albert <aalbert@google.com> | 2022-08-01 10:43:06 -0700 |
---|---|---|
committer | TreeHugger Robot <treehugger-gerrit@google.com> | 2022-08-01 18:20:27 +0000 |
commit | 5b223faa63f399729a1c7d4fb6171ca6d77add78 (patch) | |
tree | 86b5bcc8a39194e4240d55ff4c02270ee48d55f6 /logcat | |
parent | 79a6a2f7bc8eb9940814401abe25cca80a44f795 (diff) | |
download | idea-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')
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 } |