Adds AppLog breadcrumbs to the message-open path so a debug report can show where the reader's spinner time goes: - ImapClient.withStore: per-op connect vs. work timing plus a live connect-per-op connection gauge (issue #125's provider-ceiling context). - fetchBodyMarkingSeen: select/body/flag phase timings plus PII-free size counts (RFC822 size, body chars, attachment count). - MailRepositoryImpl.openMessage: end-to-end open latency plus the cached-vs-fetched branch, keyed by accountLogRef and logSafeFolderLabel. - ReaderViewModel: spinner-to-ready latency, split success vs. failure. All breadcrumbs are PII-free: accounts are logged via the existing accountLogRef hash, folders via the existing logSafeFolderLabel allowlist, and everything else is sizes/durations/booleans only. Fixes the 4 unit-test classes that exercise this code without mocking android.util.Log (a throwing stub under plain JVM tests): mockkStatic(Log) is now installed in MailRepositoryImplCoverageTest, ImapClientBackfillTest, ImapFolderOpenLatencyTest, and ReaderViewModelActionsTest, following the existing MailBackfillerTest/ImapClientTest conventions. detekt.yml gains two more ForbiddenImport excludes for the newly Log-importing test files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
103 lines
5.2 KiB
YAML
103 lines
5.2 KiB
YAML
# Project overrides merged onto detekt's bundled defaults (buildUponDefaultConfig = true in
|
|
# app/build.gradle.kts). Only rules that need tuning for this project appear here.
|
|
#
|
|
# Guiding principle: Compose UI intentionally breaks several OOP-era metrics, and a few default
|
|
# thresholds are stricter than this project's chosen style. Those are relaxed here with a reason.
|
|
# Genuine smells (swallowed exceptions, an over-complex condition, a misnamed file) are fixed in
|
|
# the code, not silenced.
|
|
|
|
complexity:
|
|
LongMethod:
|
|
# Declarative @Composable functions are read top-to-bottom and are legitimately long.
|
|
ignoreAnnotated: ['Composable']
|
|
CyclomaticComplexMethod:
|
|
# Branchy layout code (when/if inside a UI tree) isn't algorithmic complexity.
|
|
ignoreAnnotated: ['Composable']
|
|
TooManyFunctions:
|
|
# Screen files group many small @Composable helpers next to their screen. The data layer
|
|
# (repositories, DAOs, mappers) and complex mailbox screen/ViewModel are intentionally
|
|
# operation-rich cohesive APIs; detekt's default of 11 is far too low for them. Files past
|
|
# ~40 functions still flag as genuinely bloated.
|
|
ignoreAnnotated: ['Composable']
|
|
allowedFunctionsPerFile: 40
|
|
allowedFunctionsPerClass: 40
|
|
allowedFunctionsPerInterface: 40
|
|
LargeClass:
|
|
# MailRepositoryImplTest is one cohesive single-SUT suite (a test per MailRepositoryImpl method
|
|
# plus folder-resolution / spam cases) already at detekt's LLOC boundary; the reader-path perf
|
|
# logging (issue #358) added its required android.util.Log mock + one breadcrumb test, tipping it
|
|
# over. Excluded rather than artificially split — same "operation-rich cohesive suite" rationale as
|
|
# the TooManyFunctions relaxation above.
|
|
excludes: ['**/data/repository/MailRepositoryImplTest.kt']
|
|
|
|
naming:
|
|
FunctionNaming:
|
|
# @Composable functions are PascalCase by Compose convention.
|
|
ignoreAnnotated: ['Composable']
|
|
|
|
style:
|
|
ForbiddenImport:
|
|
# Strangler guard for epic #324: production code must log through
|
|
# org.libremail.reporting.AppLog, which mirrors every line into the debug-report
|
|
# RingLogBuffer. A raw android.util.Log import writes to Logcat only and bypasses that
|
|
# buffer, so it never reaches a user-reviewed DebugReport. Excludes cover the ONE allowed
|
|
# wrapper (AppLog) plus the unit tests that import Log solely to mockkStatic/verify it —
|
|
# AppLog forwards to Log, a throwing stub under plain JVM unit tests, so those tests must
|
|
# mock it. They do not bypass the facade. Do NOT add production files here; migrate them to
|
|
# AppLog instead.
|
|
active: true
|
|
forbiddenImports:
|
|
- value: 'android.util.Log'
|
|
reason: >-
|
|
Log via org.libremail.reporting.AppLog so lines reach the debug-report RingLogBuffer;
|
|
raw android.util.Log bypasses it (epic #324).
|
|
excludes:
|
|
# The facade itself — the single sanctioned wrapper around android.util.Log.
|
|
- '**/reporting/AppLog.kt'
|
|
# Facade tests: mockkStatic(Log) to verify AppLog forwards to Logcat.
|
|
- '**/reporting/AppLogTest.kt'
|
|
- '**/reporting/AppLogUninstalledTest.kt'
|
|
- '**/restart/RestartActivityLoggingTest.kt'
|
|
# Unit tests that mockkStatic(Log::class) because the code under test logs via AppLog,
|
|
# which forwards to android.util.Log (a no-op throwing stub under plain JVM unit tests).
|
|
- '**/ui/lock/AppLockViewModelTest.kt'
|
|
- '**/ui/accountsetup/AccountSetupViewModelTest.kt'
|
|
- '**/data/sync/SyncWorkerTest.kt'
|
|
- '**/data/sync/MailSyncerTest.kt'
|
|
- '**/data/sync/MailBackfillerTest.kt'
|
|
- '**/data/sync/MailPrunerTest.kt'
|
|
- '**/data/sync/MailMaintenanceGateTest.kt'
|
|
- '**/data/sync/MailSyncConcurrencyTest.kt'
|
|
- '**/data/sync/PruneWorkerTest.kt'
|
|
- '**/data/sync/BackfillWorkerTest.kt'
|
|
# Reader-path perf logging (issue #358): the repository's openMessage and the reader ViewModel
|
|
# log via AppLog, so their unit tests mockkStatic(Log) too.
|
|
- '**/data/repository/MailRepositoryImplTest.kt'
|
|
- '**/data/repository/MailRepositoryImplCoverageTest.kt'
|
|
- '**/ui/reader/ReaderViewModelTest.kt'
|
|
- '**/ui/reader/ReaderViewModelActionsTest.kt'
|
|
MagicNumber:
|
|
# dp / sp / duration literals are idiomatic inline in Compose.
|
|
ignoreAnnotated: ['Composable']
|
|
ignorePropertyDeclaration: true
|
|
ignoreNamedArgument: true
|
|
ReturnCount:
|
|
# Allow guard-clause-style early returns; detekt's default of 2 is overly strict.
|
|
max: 4
|
|
LoopWithTooManyJumpStatements:
|
|
# Clear early-continue / early-return loops (e.g. MIME-part walking) read fine; default 1 is strict.
|
|
maxJumpCount: 3
|
|
ThrowsCount:
|
|
# Guard-clause throws don't count; allow a few more for functions validating several
|
|
# preconditions (e.g. the OAuth token exchange checks response, auth code, and email).
|
|
excludeGuardClauses: true
|
|
max: 3
|
|
|
|
exceptions:
|
|
TooGenericExceptionCaught:
|
|
# Background sync, the push service, and the IMAP client intentionally catch broadly at their
|
|
# boundaries to stay resilient to arbitrary mail-library/network failures; each catch is logged
|
|
# and handled (retry with backoff, or SMTP fallback). The SwallowedException rule stays active
|
|
# (exceptions are logged, not dropped).
|
|
active: false
|