diff --git a/TESTING_GUIDE.md b/TESTING_GUIDE.md index 4596d1f08..2bff3acb5 100644 --- a/TESTING_GUIDE.md +++ b/TESTING_GUIDE.md @@ -834,15 +834,18 @@ Prefer the existing helper before writing new setup code. - `ResolveQueryDataDirWithinWorkspace(workspacePath)` bounds query DB ancestor discovery at the fixture workspace root. Use it when a resolver test must ignore unrelated `.cdidx` directories above a shared temp root while preserving workspace-source fallback behavior. - `ProjectPath(projectRoot, ...)` resolves fixture paths relative to the temp project and rejects absolute paths or `..` escapes outside that root. - `CreateDirectory(projectRoot, ...)`, `WriteTextFile(...)`, `WriteTextFiles(...)`, `WriteBinaryFile(...)`, `AppendTextFile(...)`, and `ReadTextFile(...)` centralize fixture directory creation and file setup. Prefer them over local `Path.Combine` + `Directory.CreateDirectory` + `File.*` chains when the path belongs to a temp project. +- When related extractor/query regressions require the production CLI indexing boundary but only read the resulting graph, give each case a collision-free file and query symbol, index one shared immutable workspace, and keep the case-specific assertions together. Retain separate workspaces when a case mutates the source/index, verifies indexing failure or diagnostics, or needs independently observable test discovery. - Use the `WriteTextFile(..., Encoding)` overload when fixture encoding is part of the behavior under test; do not drop back to `File.WriteAllText(Path.Combine(...), ..., encoding)` for temp-project files. - In `FileIndexerTests`, use the local relative-path helpers for scan result assertions instead of repeating `Path.GetRelativePath(...)`, separator normalization, sorting, or set creation at each call site. - `InitializeGitRepo(projectRoot)` initializes git and sets repo-local `user.name` and `user.email`. - `CreateProjectDb(projectRoot)` creates `/.cdidx/codeindex.db`, initializes schema, and seeds `codeindex_meta.indexed_project_root` to match the project root. - `InsertIndexedFile(...)` inserts a realistic indexed file with content-derived checksum, chunks, symbols, and references, and now passes the file path into Python symbol extraction so `__init__.py`-based re-export tests can exercise qualified package names. +- `InsertIndexedFiles(...)` seeds an immutable multi-file fixture through one caller-owned transaction and performs deferred hotspot/reference-identity refresh once for the batch. Prefer it when a test builds many independent files before read-only queries, including mixed-language fixtures. Keep `InsertIndexedFile(...)` when the scenario observes per-file commits, failures, cancellation, refresh boundaries, or performs reads or mutations between inserts. - `InsertIndexedFile(...)` does not clear process-wide SQLite pools after an ordinary disposed write, and `DeleteSqliteDatabaseFiles(...)` attempts deletion before requesting a pool release. Keep pool clearing as a Windows retry response to an observed deletion failure instead of charging every seeded file and clean database cleanup. When a fixture must immediately read or copy raw database bytes, pass `releasePoolForFileAccess: true` so only that connection pool is invalidated before disposal. - `RunGit(...)` executes git without shell quoting issues. - `DeleteDirectory(...)` attempts the recursive delete before walking the fixture to normalize attributes. Keep attribute normalization, SQLite pool release, and the bounded retry delay on the failure path so ordinary cleanup pays only one filesystem traversal while read-only or late-released Windows fixtures still recover. - `DeleteDirectory(path)` retries temp-project cleanup and normalizes attributes. To avoid process-global cross-test interference, it only requests SQLite pool cleanup through `SqlitePoolCleanup` as a Windows-specific retry fallback after a delete failure. +- The `IndexCommandRunnerTests` partials rely on that failure-driven cleanup; do not call `SqliteConnection.ClearAllPools()` immediately before `DeleteDirectory(...)`. Retain an explicit pool release only when the scenario itself must reopen, replace, copy, or exclusively lock raw database files before cleanup. - Use `DeleteDirectory(path)` in temp-workspace `finally` / `Dispose` cleanup paths, including tests that intentionally remove the workspace earlier in the scenario. - Call `DeleteDirectory(path)` directly instead of wrapping it in `Directory.Exists(...)`; the helper already handles missing paths. - Apply the same direct-call rule to local `DeleteDirectory` wrappers that only delegate to `TestProjectHelper.DeleteDirectory`. @@ -1805,13 +1808,16 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests" - `ResolveQueryDataDirWithinWorkspace(workspacePath)` は query DB の ancestor 探索を fixture の workspace root で打ち切ります。workspace source の fallback 挙動を維持しつつ、共有 temp root より上にある無関係な `.cdidx` directory を resolver test から除外する場合に使ってください。 - `ProjectPath(projectRoot, ...)` は temp project からの相対 fixture path を解決し、その root の外へ出る絶対 path や `..` escape を拒否します。 - `CreateDirectory(projectRoot, ...)`、`WriteTextFile(...)`、`WriteTextFiles(...)`、`WriteBinaryFile(...)`、`AppendTextFile(...)`、`ReadTextFile(...)` は fixture directory 作成と file setup を集約します。path が temp project に属する場合は、ローカルな `Path.Combine` + `Directory.CreateDirectory` + `File.*` の連鎖より優先してください。 +- 関連する extractor / query 回帰テストが production CLI の indexing 境界を必要とし、作成後の graph を読むだけなら、case ごとに衝突しない file と query symbol を与え、1 つの共有不変 workspace を 1 回だけ index して、case 固有の assertion をまとめて維持してください。source / index を変更する場合、indexing failure や diagnostic を検証する場合、または test discovery 上で独立して観測する必要がある場合は workspace を分けたままにします。 - fixture encoding がテスト対象の挙動に含まれる場合は `WriteTextFile(..., Encoding)` overload を使い、temp project 配下の file に対して `File.WriteAllText(Path.Combine(...), ..., encoding)` へ戻さないでください。 - `FileIndexerTests` では、scan result assertion ごとに `Path.GetRelativePath(...)`、separator normalization、sorting、set creation を繰り返さず、ローカルの relative-path helper を使ってください。 - `InitializeGitRepo(projectRoot)` は git を初期化し、repo-local の `user.name` と `user.email` を設定します。 - `CreateProjectDb(projectRoot)` は `/.cdidx/codeindex.db` を作成し、スキーマを初期化したうえで `codeindex_meta.indexed_project_root` に project root を書き込みます。 - `InsertIndexedFile(...)` は内容由来の checksum、chunks、symbols、references を含む現実的なインデックス済みファイルを挿入し、Python の symbol extraction には file path も渡すため、`__init__.py` ベースの再エクスポートテストで package 修飾名を扱えます。 +- `InsertIndexedFiles(...)` は、変更しない複数ファイル fixture を 1 つの caller-owned transaction で投入し、遅延した hotspot / reference-identity refresh を batch 全体で 1 回だけ実行します。複数の独立ファイルを作成してから read-only query を行うテストでは、複数言語 fixture も含めてこちらを優先してください。file ごとの commit、failure、cancellation、refresh 境界を観測する場合や、挿入の途中で read / mutation を行う場合は `InsertIndexedFile(...)` を維持してください。 - `RunGit(...)` は shell の quoting 問題に依存せず git を実行します。 - `DeleteDirectory(path)` は temp project cleanup のリトライと属性正規化を扱います。プロセス全体への干渉を避けるため、SQLite pool の解放は Windows で削除に失敗した場合のリトライ時だけに限定します。 +- `IndexCommandRunnerTests` の partial 群も、この失敗時解放に委ねます。`DeleteDirectory(...)` の直前で `SqliteConnection.ClearAllPools()` を呼ばないでください。cleanup 前に raw database file を再オープン、置換、コピー、または排他 lock すること自体が scenario の一部である場合だけ、明示的な pool 解放を残します。 - 一時 workspace の `finally` / `Dispose` cleanup では、そのテストシナリオ内で workspace を意図的に先に削除する場合も含めて、`DeleteDirectory(path)` を使ってください。 - `DeleteDirectory(path)` は存在しない path を内部で扱うため、`Directory.Exists(...)` で囲まず直接呼び出してください。 - `TestProjectHelper.DeleteDirectory` に委譲するだけの local `DeleteDirectory` wrapper でも、同じく直接呼び出してください。 diff --git a/tests/CodeIndex.Tests/IndexCommandRunnerDryRunTests.cs b/tests/CodeIndex.Tests/IndexCommandRunnerDryRunTests.cs index 20331313f..d913d7b15 100644 --- a/tests/CodeIndex.Tests/IndexCommandRunnerDryRunTests.cs +++ b/tests/CodeIndex.Tests/IndexCommandRunnerDryRunTests.cs @@ -535,7 +535,6 @@ public void Run_DryRun_ReusesUnchangedIndexedBinaryFile_Issue4893() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -606,7 +605,6 @@ ON CONFLICT(key) DO UPDATE SET value = excluded.value } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -951,7 +949,6 @@ checksum TEXT } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1665,7 +1662,6 @@ public void Run_DryRunAndFullScan_FollowSymlinksAllAgreeForExternalFileLink_Issu } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteDirectory(outsideRoot); } @@ -1725,7 +1721,6 @@ public void Run_DryRunAndFullScan_ClassifyDanglingSymlinkAsWarning_Issue4829() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } diff --git a/tests/CodeIndex.Tests/IndexCommandRunnerFullScanTests.cs b/tests/CodeIndex.Tests/IndexCommandRunnerFullScanTests.cs index c1fc4ec86..cae2f2e8e 100644 --- a/tests/CodeIndex.Tests/IndexCommandRunnerFullScanTests.cs +++ b/tests/CodeIndex.Tests/IndexCommandRunnerFullScanTests.cs @@ -62,7 +62,6 @@ public void Run_FullScanAndScopedUpdateUseGuardedAtomicFileReferenceScope() { DbWriter.AtomicFileReferenceInsertForTesting = previousAtomicHook; DbWriter.HotspotAggregateRefreshStatementExecutingForTesting = previousAggregateRefreshHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -134,7 +133,6 @@ GROUP BY f.lang finally { DbWriter.BatchStatementExecutingForTesting = previousStatementHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -183,7 +181,6 @@ public void Run_MemoryTrace_ReportsFullScanAndUpdatePhaseBoundaries() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -229,7 +226,6 @@ public void Run_FullScanJson_ProjectMarkerBudgetWarningIncludesTruncatedWarning( { FileIndexer.EnumerateProjectMarkerDirectoriesForTesting = previousEnumerator; FileIndexer.ProjectMarkerFingerprintDirectoryBudgetForTesting = previousDirectoryBudget; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -261,7 +257,6 @@ def helper(): } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -393,7 +388,6 @@ public void Run_FullScan_CompletenessMatrixMatchesImmediateStatus_Issue4826( } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -441,7 +435,6 @@ public void Run_FullScan_FileSizePolicyTransitionReprocessesUnchangedFile_Issue4 } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -492,7 +485,6 @@ WHERE key IN (@version, @fingerprint) } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -560,7 +552,6 @@ DELETE FROM codeindex_meta } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -655,7 +646,6 @@ public void Run_FullScan_ExtractorFailurePersistsSuccessfulGraphAndTruthfulParti finally { IndexCommandRunner.FullScanFilePhaseForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -840,7 +830,6 @@ public void Run_FullScan_ReferenceCapHitPersistsPerFileAndRunCompleteness_Issue4 finally { ReferenceExtractor.SafetyLimitsForTesting = previousLimits; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -915,7 +904,6 @@ public void Run_FullScan_RechecksIndexabilityBeforeContentRead() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteDirectory(outsideRoot); } @@ -947,7 +935,6 @@ def helper(): } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -979,7 +966,6 @@ public static class FullScanCycleB } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1004,7 +990,6 @@ public void Run_FullScan_SkipsOversizedGitExclude() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1081,7 +1066,6 @@ public void Run_FullScanAfterHeadChange_ParallelizesOnlyChangedFiles() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1142,7 +1126,6 @@ public void Run_FullScan_DenseDeletionCapsReusableStatSnapshotCapacityToRetained DbWriter.ReusableStatSnapshotInitialCapacityForTesting = previousCapacityHook; DbWriter.ReusableStatSnapshotFilterModeForTesting = previousFilterModeHook; DbWriter.ReusableStatSnapshotCandidateRowForTesting = previousCandidateRowHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1193,7 +1176,6 @@ public void Run_FullScan_IncompleteLegacyStatReindexesFile() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1236,7 +1218,6 @@ public void Run_FullScan_StatSnapshotObservesCancellationToken() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1271,7 +1252,6 @@ public void Run_FullScan_GraphNeutralChangeSkipsMutualRecursionRefresh() finally { DbWriter.MutualRecursionRefreshForTesting = previousRefreshHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1314,7 +1294,6 @@ public void Run_FullScan_ChangedReferenceUsesDirtyGraphScope() finally { DbWriter.ReferenceGraphRefreshScopeForTesting = previousScopeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1352,7 +1331,6 @@ public void Run_FullScan_CancelledDuringMutualRecursionRefresh_LeavesReadinessDe finally { DbWriter.MutualRecursionRefreshForTesting = previousRefreshHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1394,7 +1372,6 @@ public void Run_FullScanAfterTypeScriptConfigChange_ReprocessesUnchangedTypeScri } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1440,7 +1417,6 @@ public void Run_FullScanAfterTypeScriptConfigContentChangeWithStableStat_Reproce } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1480,7 +1456,6 @@ public void Run_FullScanAfterDerivedTypeScriptConfigDelete_ReprocessesUnchangedT } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1514,7 +1489,6 @@ public void Run_FullScanWithSequentialExtraction_PersistsValidationIssues() { IndexCommandRunner.FullScanExtractionSchedulingForTesting = null; IndexCommandRunner.FullScanExtractionQueueCapacityForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1540,7 +1514,6 @@ public void Run_FullScanWithoutCSharp_DoesNotRunCSharpPrepass() finally { IndexCommandRunner.FullScanCSharpPrepassForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1657,7 +1630,6 @@ BEFORE UPDATE ON files } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRootA); DeleteDirectory(projectRootB); DeleteFile(dbPath); @@ -1699,7 +1671,6 @@ public void Run_FullScanExplicitDb_SuccessfulNoOpBackfillsMissingIndexedProjectR } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteFile(dbPath); } @@ -1762,7 +1733,6 @@ FROM symbol_references r } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRootA); DeleteDirectory(projectRootB); DeleteFile(dbPath); @@ -2146,7 +2116,6 @@ public void Run_FullScan_RetargetedExternalFileLinkFailsWorkspaceValidation_Issu finally { IndexCommandRunner.FullScanFileContentLoadForTesting = previousContentLoadHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteDirectory(outsideRoot); } @@ -3305,7 +3274,6 @@ public void Run_FullScan_CancelledAfterReadinessDemotion_RollsBackExistingIndex( finally { IndexCommandRunner.FullScanWritePhaseStartedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3366,7 +3334,6 @@ public void Run_FullScan_SubdirectoryProjectRoot_UsesRepositoryIgnoreCaseConfigW } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -3394,7 +3361,6 @@ public void Run_FullScan_SubdirectoryProjectRoot_UsesRepositoryIgnoreCaseConfigW } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -3423,7 +3389,6 @@ public void Run_FullScan_SubdirectoryProjectRoot_RespectsAncestorGitignore() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -3450,7 +3415,6 @@ public void Run_FullScan_SubdirectoryProjectRoot_RespectsAncestorDirectoryGitign } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -3475,7 +3439,6 @@ public void Run_FullScan_ProjectRootNamedNodeModules_IndexesExplicitProjectRoot( } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(tempRoot); } } @@ -5134,7 +5097,6 @@ FROM hotspot_reference_counts } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5514,7 +5476,6 @@ public void Run_FullScan_DoesNotStampFoldReadyWhenLegacyRowsRemain() } finally { - Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5575,7 +5536,6 @@ public void Call(Api api) } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5614,7 +5574,6 @@ public void Run_FullScan_KeepsCsharpHotspotFamilyTrustWhenOnlyVbMarkersChange() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5666,7 +5625,6 @@ public void Run_FullScan_RestampsHotspotFamilyTrustWhenOnlyMetadataWasCleared() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5760,7 +5718,6 @@ public void Call(Api api) } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5818,7 +5775,6 @@ public void Call(Api api) } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5880,7 +5836,6 @@ public void Run_FullScan_DoesNotRestampFoldReadyWhenFoldKeyVersionMismatches() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5934,7 +5889,6 @@ public void Run_FullScan_DoesNotRestampFoldReadyWhenFoldFingerprintMismatchesAnd } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5990,7 +5944,6 @@ public void Run_FullScan_DoesNotRestampFoldReadyWhenSkippedRowsCarryStaleFoldKey } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6050,7 +6003,6 @@ public void Run_FullScan_RestampsFoldReadyWhenUserVersionWasClearedButFoldMetada } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6080,7 +6032,6 @@ public void Run_FullScan_PersistsCurrentHeadCommit() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6128,7 +6079,6 @@ public void Run_FullScan_AfterBranchSwitch_JsonReportsHeadChangedAndWarning() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6168,7 +6118,6 @@ public void Run_FullScan_LegacyDbWithoutCapturedHead_DoesNotReportHeadChange() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6193,7 +6142,6 @@ public void Run_FullScanJson_WritesLivenessToStderrOnly() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6215,7 +6163,6 @@ public void Run_FullScan_NonGitWorkspace_DoesNotReportHeadChange() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6248,7 +6195,6 @@ public void Run_FullScan_Rebuild_DoesNotReportHeadChangeEvenIfHeadDiffers() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } diff --git a/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs b/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs index 782e40671..1f972dd41 100644 --- a/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs +++ b/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs @@ -326,7 +326,6 @@ public void Run_FilesMode_WhenSymbolExtractionStalls_ReportsStallInsteadOfInterr finally { IndexCommandRunner.IndexExtractionStallTimeoutForTesting = priorTimeout; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteFile(dbPath); } @@ -743,7 +742,6 @@ FROM symbols s finally { ExtractorPluginRegistry.ResetForTests(); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteFile(dbPath); } @@ -1426,7 +1424,6 @@ public void Run_PublishedSingleFileBinary_IndexesWithIsolatedSymbolWorker() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteDirectory(publishDir); DeleteFile(dbPath); @@ -1599,7 +1596,6 @@ public void Run_NullByteFile_PersistsNullByteIssueWithoutPartialRows() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1638,7 +1634,6 @@ public void Run_FileAboveMaxFileBytes_PersistsFileTooLargeIssue() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1685,7 +1680,6 @@ public void Run_FileAboveMaxSymbolsPerFile_PersistsSymbolCountExceededIssueOnly( } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1743,7 +1737,6 @@ public void Run_FileAboveMaxReferencesPerFile_FullScanAndUpdatePersistReferenceC } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1811,7 +1804,6 @@ public void Run_SymbolsOnly_FullScanSkipsReferenceGraphUntilNormalIndex() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1863,7 +1855,6 @@ public void Run_SymbolsOnly_OnGraphReadyDbDemotesReferencesAndSqlContract() finally { IndexCommandRunner.FullScanTypeScriptAugmentationRebuildForTesting = previousTypeScriptRebuildHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1895,7 +1886,6 @@ public void RunFiles_FileAboveMaxFileBytes_PersistsFileTooLargeIssue() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1923,7 +1913,6 @@ public void Run_NewIndexDatabase_RunsAnalyzeAfterSuccessfulIndex() finally { DbContext.PlannerStatisticsCommandExecutedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1959,7 +1948,6 @@ public void Run_PlannerStatisticsMaintenanceFailure_AddsLastIndexRunDiagnostic_I finally { DbContext.PlannerStatisticsCommandCreatedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1997,7 +1985,6 @@ public void Run_PlannerStatisticsMaintenanceDiagnosticStampFailure_DoesNotFailSu { DbContext.PlannerStatisticsCommandCreatedForTesting = null; IndexCommandRunner.PlannerStatisticsMaintenanceDiagnosticStampingForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2035,7 +2022,6 @@ public void Run_CancelDuringFreshIndex_ReturnsInterruptedJson() finally { IndexCommandRunner.FullScanExtractionSchedulingForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2072,7 +2058,6 @@ public void Run_CancelDuringDryRunScan_ReturnsInterruptedJson() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2109,7 +2094,6 @@ public void Run_CancelBeforeFreshScan_ReturnsInterruptedJson() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2145,7 +2129,6 @@ public void Run_CancelAtFtsOptimize_InterruptsAndLeavesRecoverableState_Issue459 finally { IndexCommandRunner.FullScanFtsOptimizeForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2173,7 +2156,6 @@ public void Run_CancelAtPlannerMaintenance_StopsWithoutDuplicateResult_Issue4591 finally { DbContext.PlannerStatisticsCommandCreatedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2205,7 +2187,6 @@ public void Run_ExistingIndexDatabase_RunsPragmaOptimizeAfterSuccessfulIndex() finally { DbContext.PlannerStatisticsCommandExecutedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2515,7 +2496,6 @@ public void Run_NoOpFullScan_DoesNotOptimizeFts() IndexCommandRunner.FullScanTypeScriptAugmentationRebuildForTesting = null; IndexCommandRunner.FullScanExtractionWorkStartedForTesting = null; IndexCommandRunner.FullScanExtractionSchedulingForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2562,7 +2542,6 @@ public void Run_IncrementalFullScan_ScopesTypeScriptAugmentationToDirtyNames() finally { DbWriter.TypeScriptAugmentationGroupingForTesting = previousGroupingHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2615,7 +2594,6 @@ public void Run_IncrementalFullScan_TypeScriptToCSharpLanguageTransitionRemovesA finally { DbWriter.TypeScriptAugmentationGroupingForTesting = previousGroupingHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2686,7 +2664,6 @@ public void Run_IncrementalFullScan_DefersIncrementalFtsMergeUntilWriteThreshold { IndexCommandRunner.FullScanFtsOptimizeForTesting = previousOptimizeHook; IndexCommandRunner.FullScanFtsMergeForTesting = previousMergeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2734,7 +2711,6 @@ static string SizedSource(char fill, int size) { IndexCommandRunner.FullScanFtsOptimizeForTesting = previousOptimizeHook; IndexCommandRunner.FullScanFtsMergeForTesting = previousMergeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2778,7 +2754,6 @@ static string SizedSource(char fill, int size) finally { IndexCommandRunner.FullScanFtsOptimizeForTesting = previousOptimizeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2854,7 +2829,6 @@ static string SizedSource(string token, char fill, int size) { IndexCommandRunner.FullScanFtsOptimizeForTesting = previousOptimizeHook; IndexCommandRunner.FullScanFilePhaseForTesting = previousFilePhaseHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2942,7 +2916,6 @@ static string SizedSource(string token, char fill, int size) IndexCommandRunner.FullScanFtsOptimizeForTesting = previousOptimizeHook; IndexCommandRunner.FullScanStaleFilePurgeForTesting = previousPurgeHook; IndexCommandRunner.FullScanReferencePurgeForTesting = previousReferencePurgeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2992,7 +2965,6 @@ static string SizedSource(char fill, int size) { IndexCommandRunner.FullScanFtsOptimizeForTesting = previousOptimizeHook; IndexCommandRunner.FullScanStaleFilePurgeForTesting = previousPurgeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3049,7 +3021,6 @@ static string SizedSource(char fill, int size) { IndexCommandRunner.FullScanFtsOptimizeForTesting = previousOptimizeHook; IndexCommandRunner.FullScanStaleFilePurgeForTesting = previousPurgeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3126,7 +3097,6 @@ public void Run_FilesUpdate_ReportsIncrementalFtsMergeAndPreservesOptimizeRecomm } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3156,7 +3126,6 @@ public void Run_NoOpUpdate_DoesNotStartExtractionWork() finally { IndexCommandRunner.UpdateExtractionWorkStartedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5500,7 +5469,6 @@ public void Run_ReadOnlyDbFile_ReturnsDatabaseErrorWithoutStackTrace() { if (File.Exists(dbPath)) SetUnixPermissions(dbPath, UnixFileMode.UserRead | UnixFileMode.UserWrite); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6382,7 +6350,6 @@ public void Run_IndexOptimizeWithDryRun_PreviewsWithoutWriting_Issue4577() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -8206,7 +8173,6 @@ public void Run_Rebuild_CancelledAfterReadinessDemotion_PreservesExistingIndex_I finally { IndexCommandRunner.FullScanWritePhaseStartedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -8241,7 +8207,6 @@ public void Run_Rebuild_WhenIndexedFileBecomesBinary_PersistsNullByteIssue() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -8898,7 +8863,6 @@ public void Run_Rebuild_UnreadableDirectoryPreservesPriorRowsAndTrust() { if (originalMode.HasValue && Directory.Exists(unreadableDir)) File.SetUnixFileMode(unreadableDir, originalMode.Value); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -8974,7 +8938,6 @@ public void RunStatusCheck_AfterBranchSwitch_ReportsHeadChanged() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -9006,7 +8969,6 @@ public void RunStatusCheck_CdidxSidecarIsExcludedFromScanAndWorkspaceMembership_ } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -9062,7 +9024,6 @@ public void RunStatusCheck_FollowSymlinksAll_UsesPersistedPolicy_Issue4352() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteDirectory(outsideRoot); } @@ -9113,7 +9074,6 @@ public void RunStatusCheck_FilesRefreshStaysStaleUntilCommitScopedRefreshAtHead( } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -9193,7 +9153,6 @@ public void RunStatusCheck_AfterChangedBetweenRefreshAtHead_TreatsCurrentIndexed } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } diff --git a/tests/CodeIndex.Tests/IndexCommandRunnerUpdateTests.cs b/tests/CodeIndex.Tests/IndexCommandRunnerUpdateTests.cs index 1596f6bc9..275d74f4a 100644 --- a/tests/CodeIndex.Tests/IndexCommandRunnerUpdateTests.cs +++ b/tests/CodeIndex.Tests/IndexCommandRunnerUpdateTests.cs @@ -53,7 +53,6 @@ public void Run_UpdateMode_AmbiguousProjectMarkerChangeReclassifiesExistingFiles } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -74,7 +73,6 @@ public void Run_UpdateMode_PreservesUnchangedWorkspacePluginReferences_Issue4602 finally { ExtractorPluginRegistry.ResetForTests(); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -192,7 +190,6 @@ UPDATE symbol_references } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -263,7 +260,6 @@ WHERE reference.resolution_state IN ('resolved', 'resolved_group') } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -387,7 +383,6 @@ FROM symbol_reference_candidates AS candidate } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -468,7 +463,6 @@ FROM symbol_reference_candidates AS candidate } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -531,7 +525,6 @@ public void Run_UpdateMode_TargetOnlyMarkdownAnchorChangesRefreshExactReferences finally { DbWriter.ReferenceGraphRefreshScopeForTesting = previousScopeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -651,7 +644,6 @@ public void Run_UpdateMode_RefreshesMutualRecursionOncePerBatchIncludingDeleteOn { DbWriter.MutualRecursionRefreshForTesting = previousRefreshHook; DbWriter.ReferenceGraphRefreshScopeForTesting = previousScopeHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -701,7 +693,6 @@ public void Run_UpdateMode_CancelledDuringMutualRecursionRefresh_LeavesReadiness finally { DbWriter.MutualRecursionRefreshForTesting = previousRefreshHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -733,7 +724,6 @@ def helper(): } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -766,7 +756,6 @@ def helper(): } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -807,7 +796,6 @@ public void Run_UpdateNonCSharpFile_DoesNotResolveCSharpMetadataTargets() IndexCommandRunner.UpdateCSharpPrepassForTesting = null; IndexCommandRunner.UpdateCSharpMetadataResolveForTesting = null; IndexCommandRunner.UpdateTypeScriptAugmentationRebuildForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -896,7 +884,6 @@ public void Run_UpdateFiles_UnrelatedHardlinkIsNotTreatedAsCaseAliasCleanup() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1433,7 +1420,6 @@ BEFORE UPDATE ON files } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); DeleteFile(dbPath); } @@ -1598,7 +1584,6 @@ BEFORE UPDATE ON files } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1675,7 +1660,6 @@ public void Run_UpdateFiles_TypeScriptConfigChangeFallsBackToFullScanForAliasSym } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1734,7 +1718,6 @@ public void Run_UpdateFiles_JavaScriptExtendedConfigChangeFallsBackToFullScanFor } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1892,7 +1875,6 @@ public void Run_UpdateFiles_CsharpContractPreflightAvoidsRedundantWorkspacePasse DbWriter.CSharpContractPreflightForTesting = previousPreflightHook; DbWriter.CSharpContractWorkspaceReadForTesting = previousWorkspaceReadHook; IndexCommandRunner.UpdateCSharpPrepassForTesting = previousUpdatePrepassHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -1965,7 +1947,6 @@ public void Run_UpdateFiles_InPlaceCdidxignoreChangeDuringExpandedPrepassDefersU finally { IndexCommandRunner.UpdateCSharpPrepassForTesting = previousPrepassHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2136,7 +2117,6 @@ public void Run_UpdateFiles_OneSidedCsharpContractRenameRefreshesVisibleReferenc } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2191,7 +2171,6 @@ public void Run_UpdateFiles_ChangedExistingRetainedTargetPreplansMatchingCsharpA finally { IndexCommandRunner.UpdateCleanupChecksumReadForTesting = previousCleanupChecksumHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2254,7 +2233,6 @@ public void Run_UpdateFiles_CommonChecksumPreWorkspaceCleanupQueriesCSharpCandid finally { _ = DbDebug.EndProfile(); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2301,7 +2279,6 @@ public void Run_UpdateFiles_ChangedContentAsciiCaseOnlyRenameRemovesOldAlias() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2387,7 +2364,6 @@ public void Run_UpdateCommits_CaseOnlyRenameUsesExactGitSourceWithoutChecksumRea finally { IndexCommandRunner.UpdateCleanupChecksumReadForTesting = previousCleanupChecksumHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2443,7 +2419,6 @@ public void Run_UpdateCommits_CaseFoldedDistinctLiveFilesPreserveBothExactRows() finally { IndexCommandRunner.UpdateCleanupChecksumReadForTesting = previousCleanupChecksumHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2578,7 +2553,6 @@ public void Run_UpdateFiles_ExpandedScanValidatesInputExactlyBeforeWriteAndReadi finally { IndexCommandRunner.UpdateScanInputSnapshotBarrierForTesting = previousBarrierHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2663,7 +2637,6 @@ public void Run_UpdateFiles_FirstExpandedSnapshotBarrierDriftPreservesAllRowsAnd finally { IndexCommandRunner.UpdateScanInputSnapshotBarrierForTesting = previousBarrierHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2736,7 +2709,6 @@ public void Run_UpdateFiles_FinalExpandedSnapshotBarrierDriftPersistsFilesButBlo finally { IndexCommandRunner.UpdateScanInputSnapshotBarrierForTesting = previousBarrierHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2835,7 +2807,6 @@ public void Run_UpdateFiles_CsharpTargetDriftAfterWorkspaceReadPreservesRowsUnti finally { DbWriter.CSharpContractWorkspaceReadForTesting = previousWorkspaceReadHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2901,7 +2872,6 @@ public void Run_UpdateFiles_CsharpExpandedScanLateContractInEnumeratedDirectoryD finally { IndexCommandRunner.UpdateCSharpPrepassForTesting = previousPrepassHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -2963,7 +2933,6 @@ public void Run_UpdateFiles_CsharpExpandedScanLateContractDuringTargetLoopRefuse finally { IndexCommandRunner.UpdateFileCommittedForTesting = previousCommittedHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3017,7 +2986,6 @@ public void Run_UpdateFiles_CsharpExpandedScanIgnoresChurnInsideSkippedDirectory finally { IndexCommandRunner.UpdateCSharpPrepassForTesting = previousPrepassHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3056,7 +3024,6 @@ public void Run_UpdateFiles_IgnoredContractIsExcludedFromExpandedCsharpWorkspace } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3132,7 +3099,6 @@ public void Run_UpdateFiles_CsharpIntentionalSkipAfterPrepassDefersChangedContra finally { IndexCommandRunner.UpdateFileContentLoadForTesting = previousContentLoadHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3207,7 +3173,6 @@ public void Run_UpdateFiles_CsharpIntentionalSkipRecordDriftRollsBackBeforeUpser finally { IndexCommandRunner.UpdateSkippedFileRecordBuiltForTesting = previousSkippedRecordHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3270,7 +3235,6 @@ public void Run_UpdateFiles_IntentionalSkipUnexpectedWriteFailureClearsBatchMark finally { IndexCommandRunner.UpdateSkippedFileRecordBuiltForTesting = previousSkippedRecordHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3347,7 +3311,6 @@ public void Run_UpdateFiles_IntentionalSkipCleanupDriftClearsBatchMarkerAndPrese finally { IndexCommandRunner.UpdateCSharpPrepassForTesting = previousPrepassHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3413,7 +3376,6 @@ public void Run_UpdateFiles_UnchangedNonCsharpTargetWithPositiveContractEvidence IndexCommandRunner.UpdateCleanupChecksumReadForTesting = previousCleanupChecksumHook; if (File.Exists(targetPath)) File.SetUnixFileMode(targetPath, UnixFileMode.UserRead | UnixFileMode.UserWrite); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3490,7 +3452,6 @@ public void Run_UpdateModeFallbackFullScan_CancelledAfterReadinessDemotion_Repor finally { IndexCommandRunner.FullScanWritePhaseStartedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3564,7 +3525,6 @@ public void Run_UpdateMode_CancelledAfterCommittedFile_ReportsPersistedProgress( finally { IndexCommandRunner.UpdateFileCommittedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3626,7 +3586,6 @@ public void Run_UpdateMode_CancelledAfterTypeScriptCommit_ClearsAugmentationVers finally { IndexCommandRunner.UpdateFileCommittedForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3668,7 +3627,6 @@ public void Run_UpdateMode_DeleteTypeScriptFile_RebuildsAugmentationReferences() { IndexCommandRunner.UpdateTypeScriptAugmentationRebuildForTesting = null; DbWriter.TypeScriptAugmentationGroupingForTesting = previousGroupingHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3721,7 +3679,6 @@ public void Run_UpdateMode_TypeScriptToCSharpLanguageTransitionRemovesAugmentati finally { DbWriter.TypeScriptAugmentationGroupingForTesting = previousGroupingHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -3795,7 +3752,6 @@ public void Run_UpdateMode_CapPersistsIncompleteWhenIssueReadinessWasUnset_Issue } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4117,7 +4073,6 @@ public void Run_UpdateMode_WithFiles_SkipsMutationWhenIgnoreRulesAreUnreadable() { if (originalMode.HasValue && File.Exists(ignorePath)) SetUnixPermissions(ignorePath, originalMode.Value); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4183,7 +4138,6 @@ public void Run_UpdateMode_WithCommits_SkipsMutationWhenIgnoreRulesAreUnreadable { if (originalMode.HasValue && File.Exists(ignorePath)) SetUnixPermissions(ignorePath, originalMode.Value); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4236,7 +4190,6 @@ public void Run_UpdateMode_WithFiles_UnreadableIgnoreRulesDemoteReadinessForUnch { if (originalMode.HasValue && File.Exists(ignorePath)) SetUnixPermissions(ignorePath, originalMode.Value); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4291,7 +4244,6 @@ public void Run_UpdateMode_WithFiles_DemotesReadinessWhenIgnoreFileChangedThenBe { if (originalMode.HasValue && File.Exists(ignorePath)) SetUnixPermissions(ignorePath, originalMode.Value); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4346,7 +4298,6 @@ public void Run_UpdateMode_WithFiles_UnreadableIgnoreRulesDemoteReadinessForChan { if (originalMode.HasValue && File.Exists(ignorePath)) SetUnixPermissions(ignorePath, originalMode.Value); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4390,7 +4341,6 @@ public void Run_UpdateMode_WhenIgnoreFileChanges_FullScanPurgesAndRestoresMember } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4447,7 +4397,6 @@ public void Run_UpdateMode_WhenPatternConfigIsAddedOrEdited_FallsBackToFullScan_ finally { ExtractorPluginRegistry.ReloadForTests(); - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4489,7 +4438,6 @@ public void Run_UpdateMode_WithFiles_SubdirectoryProjectRoot_RespectsAncestorGit } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -4523,7 +4471,6 @@ public void Run_UpdateMode_WithFiles_SubdirectoryProjectRoot_RespectsAncestorDir } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -4557,7 +4504,6 @@ public void Run_UpdateMode_WithFiles_SubdirectoryProjectRoot_FallsBackToFullScan } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -4589,7 +4535,6 @@ public void Run_UpdateMode_WithFiles_SubdirectoryProjectRoot_FallsBackToFullScan } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -4620,7 +4565,6 @@ public void Run_UpdateMode_WithFiles_ProjectRootNamedNodeModules_UpdatesIndexedF } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(tempRoot); } } @@ -4659,7 +4603,6 @@ public void Run_UpdateMode_WithCommits_SubdirectoryProjectRoot_UsesRepositoryRel } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -4697,7 +4640,6 @@ public void Run_UpdateMode_WithCommits_SubdirectoryProjectRoot_FallsBackToFullSc } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(repoRoot); } } @@ -4894,7 +4836,6 @@ public void Run_UpdateMode_WithChangedBetween_StaleCsharpContractOutsideRangeRef } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -4971,7 +4912,6 @@ public void Run_UpdateMode_WithChangedBetween_CleanupPathReappearingAfterScanIsD finally { IndexCommandRunner.UpdateCSharpPrepassForTesting = previousPrepassHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5059,7 +4999,6 @@ public void Run_UpdateMode_WithChangedBetween_ExpandedExactCleanupPathReappearan { IndexCommandRunner.UpdateCSharpExpansionScanStartingForTesting = previousExpansionHook; IndexCommandRunner.UpdateFileContentLoadForTesting = previousContentLoadHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5121,7 +5060,6 @@ public void Run_UpdateCommits_ExactCleanupPathReappearingAfterSnapshotBarrierPre finally { IndexCommandRunner.UpdateScanInputSnapshotBarrierForTesting = previousBarrierHook; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5788,7 +5726,6 @@ public void Run_UpdateMode_DegradedIssuesKeepsLastRunReferenceCapSnapshotUnavail } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5851,7 +5788,6 @@ public void Run_UpdateMode_PreservesGraphAndIssuesOnPre86Db_WithoutStampingFold( } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5927,7 +5863,6 @@ public void Run_UpdateMode_DoesNotRestampFoldReadyWhenFoldKeyVersionMismatches() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -5980,7 +5915,6 @@ public void Run_UpdateMode_DoesNotRestampFoldReadyWhenSymbolExtractorVersionMism } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6013,7 +5947,6 @@ public void Run_UpdateMode_RestampsHotspotFamilyTrustOnOversizedFileSkip() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6050,7 +5983,6 @@ public void Run_UpdateMode_DoesNotRestampHotspotFamilyReadyWhenMarkerFingerprint } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6109,7 +6041,6 @@ public void Run_Update_WhenHotspotFamilyMetadataCannotBeRestamped_KeepsReference } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6148,7 +6079,6 @@ public void Run_Update_RollsBackHotspotFamilyRestampWhenCommitIsInterrupted() finally { IndexCommandRunner.HotspotFamilyUpdateRestampReadyForCommitForTesting = null; - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6204,7 +6134,6 @@ public void Run_UpdateMode_DoesNotRestampFoldReadyWhenFoldFingerprintMismatches( } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } @@ -6243,7 +6172,6 @@ public void Run_UpdateMode_DoesNotOverwriteIndexedHeadCommit() } finally { - SqliteConnection.ClearAllPools(); DeleteDirectory(projectRoot); } } diff --git a/tests/CodeIndex.Tests/QueryCommandRunnerReferencesTests.cs b/tests/CodeIndex.Tests/QueryCommandRunnerReferencesTests.cs index 6d7f3e24c..ba0b0183f 100644 --- a/tests/CodeIndex.Tests/QueryCommandRunnerReferencesTests.cs +++ b/tests/CodeIndex.Tests/QueryCommandRunnerReferencesTests.cs @@ -865,146 +865,126 @@ public void RunReferences_CountOnlyJson_WithMissingGraphTable_ReturnsNonAuthorit } [Fact] - public void RunReferences_ExactJson_CSharpInterpolatedRawStringPreservesCallSite() + public void RunReferences_Json_CSharpInterpolationBoundariesShareIndexedWorkspace() { - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_references_csharp_interpolated_raw"); + var projectRoot = TestProjectHelper.CreateTempProject("cdidx_references_csharp_interpolation_workspace"); try { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "app.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/interpolated-raw.cs", """" - public class App + public class RawApp { - private string Run() => "ok"; + private string RunRaw() => "ok"; public string Render() { return $""" - value = {Run()} + value = {RunRaw()} literal = function main() """; } } """"); - - var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db"); - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunReferences( - ["Run", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("Run", json.GetProperty("symbol_name").GetString()); - Assert.Equal("src/app.cs", json.GetProperty("path").GetString()); - Assert.Equal("call", json.GetProperty("reference_kind").GetString()); - Assert.Equal("Render", json.GetProperty("container_name").GetString()); - Assert.Equal(8, json.GetProperty("line").GetInt32()); - Assert.True(json.GetProperty("exact_index_available").GetBoolean()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunReferences_ExactJson_CSharpNestedInterpolatedStringInsideRawInterpolationPreservesCallSite() - { - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_references_csharp_nested_interpolated_raw"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "app.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/nested-interpolated-raw.cs", """" - public class App + public class NestedApp { - private string Run() => "ok"; + private string RunNested() => "ok"; public string Render() { return $""" - value = {$"{Run()}"} + value = {$"{RunNested()}"} literal = function main() """; } } """"); - - var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db"); - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunReferences( - ["Run", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("Run", json.GetProperty("symbol_name").GetString()); - Assert.Equal("src/app.cs", json.GetProperty("path").GetString()); - Assert.Equal("call", json.GetProperty("reference_kind").GetString()); - Assert.Equal("Render", json.GetProperty("container_name").GetString()); - Assert.Equal(8, json.GetProperty("line").GetInt32()); - Assert.True(json.GetProperty("exact_index_available").GetBoolean()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunReferences_Json_CSharpInterpolatedVerbatimStringEscapedBracesDoNotCreatePhantomReference() - { - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_references_csharp_escaped_verbatim_braces"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "app.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/escaped-verbatim-braces.cs", """ - public class App + public class EscapedApp { public string Render() { - return $@"{{Run()}}"; + return $@"{{EscapedOnlyRun()}}"; } } """); + TestProjectHelper.WriteTextFile( + projectRoot, + "src/nested-raw-fixture.cs", + """" + public class NestedRawApp + { + private int RunNestedRaw() => 1; + private string IdNestedRaw(string value) => value; + + public int Render() + { + return $""" + value = {IdNestedRaw(""" + ExecuteNestedRaw(); + public class PhantomNestedRaw + { + public void Go() { } + } + """) + RunNestedRaw()} + """.Length; + } + } + """"); var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db"); var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( [projectRoot, "--json", "--quiet"], _jsonOptions)); - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunReferences( - ["Run", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; Assert.Equal(CommandExitCodes.Success, indexExitCode); Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal(0, json.GetProperty("count").GetInt32()); - Assert.Equal(0, json.GetProperty("references").GetArrayLength()); - Assert.True(json.GetProperty("exact_index_available").GetBoolean()); + + void AssertSingleCall(string query, string path) + { + var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunReferences( + [query, "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], + _jsonOptions)); + using var document = ParseJsonOutput(stdout); + var json = document.RootElement; + + Assert.Equal(CommandExitCodes.Success, exitCode); + Assert.Equal(string.Empty, stderr); + Assert.Equal(query, json.GetProperty("symbol_name").GetString()); + Assert.Equal(path, json.GetProperty("path").GetString()); + Assert.Equal("call", json.GetProperty("reference_kind").GetString()); + Assert.Equal("Render", json.GetProperty("container_name").GetString()); + Assert.Equal(8, json.GetProperty("line").GetInt32()); + Assert.True(json.GetProperty("exact_index_available").GetBoolean()); + } + + void AssertNoReferences(string query) + { + var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunReferences( + [query, "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], + _jsonOptions)); + using var document = ParseJsonOutput(stdout); + var json = document.RootElement; + + Assert.Equal(CommandExitCodes.Success, exitCode); + Assert.Equal(string.Empty, stderr); + Assert.Equal(0, json.GetProperty("count").GetInt32()); + Assert.Equal(0, json.GetProperty("references").GetArrayLength()); + Assert.True(json.GetProperty("exact_index_available").GetBoolean()); + } + + AssertSingleCall("RunRaw", "src/interpolated-raw.cs"); + AssertSingleCall("RunNested", "src/nested-interpolated-raw.cs"); + AssertNoReferences("EscapedOnlyRun"); + AssertNoReferences("ExecuteNestedRaw"); } finally { @@ -1177,14 +1157,14 @@ public void RunCallers_JsonZeroResults_WithMissingGraphTable_ReturnsDegradedPayl } [Fact] - public void RunCallers_ExactJson_FindsTernaryContinuationCallSite() + public void RunCallers_ExactJson_CSharpScopeVariantsShareIndexedWorkspace() { - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_ternary"); + var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_scope_workspace"); try { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "dispatcher.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/ternary.cs", """ public class Dispatcher { @@ -1197,281 +1177,89 @@ private string Select(bool isUpdate) private string RunFullScan() => "full"; } """); - - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( - ["RunUpdateMode", "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("src/dispatcher.cs", json.GetProperty("path").GetString()); - // With #233 fixed, the expression-bodied `Select` method spans its declaration - // through the terminating ';' (multi-line ternary on the RHS of `=>`), so the - // RunUpdateMode call at line 5 attributes to Select, not the enclosing class. - // #233 修正により、`=>` で始まる式本体メソッド `Select` の範囲が宣言行から - // 末尾 `;` までに広がり、line 5 の RunUpdateMode 呼び出しは外側クラスではなく - // Select に帰属する。 - Assert.Equal("function", json.GetProperty("caller_kind").GetString()); - Assert.Equal("Select", json.GetProperty("caller_name").GetString()); - Assert.Equal("RunUpdateMode", json.GetProperty("callee_name").GetString()); - Assert.Equal(5, json.GetProperty("first_line").GetInt32()); - Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); - Assert.True(json.GetProperty("exact_index_available").GetBoolean()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunCallers_ExactJson_FindsCallerInsideAllmanStyleBlockBodyProperty() - { - // issue #233 review follow-up: Allman-style (next-line `{`) block-bodied C# - // properties were not extracted as symbols, so accessor-internal calls fell - // through to the enclosing class. End-to-end verify that `callers` attributes - // the call to the property itself once the extraction regex handles this shape. - // issue #233 のレビュー指摘: Allman スタイル(次行 `{`)の block-bodied プロパティが - // 抽出されておらず、accessor 内部の呼び出しが外側クラスに帰属していた。抽出 regex が - // この形を扱えるようになった後、`callers` が property に帰属することを end-to-end で確認する。 - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_allman_prop"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "calc.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/allman-block-property.cs", """ - public class Calc + public class AllmanCalc { - public int Compute() => 42; + public int ComputeAllman() => 42; - public int Wrap + public int WrapAllman { - get { return Compute(); } + get { return ComputeAllman(); } } } """); - - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( - ["Compute", "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("src/calc.cs", json.GetProperty("path").GetString()); - Assert.Equal("property", json.GetProperty("caller_kind").GetString()); - Assert.Equal("Wrap", json.GetProperty("caller_name").GetString()); - Assert.Equal("Compute", json.GetProperty("callee_name").GetString()); - Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunCallers_ExactJson_FindsCallerInsideMultiLineExpressionBodiedProperty() - { - // issue #233 second review follow-up: expression-bodied properties split across - // two lines (declaration + `=> expr;` continuation) must still attribute - // accessor-internal calls to the property through the CLI `callers` command. - // issue #233 の再レビュー指摘: 宣言行の次行に `=> expr;` が続く multi-line 式本体 - // プロパティでも、CLI `callers` で accessor 内呼び出しが property に帰属すること。 - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_ml_exprprop"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "calc.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/multiline-expression-property.cs", """ - public class Calc + public class ExpressionCalc { - public int Compute() => 42; - public int Wrap - => Compute(); + public int ComputeExpression() => 42; + public int WrapExpression + => ComputeExpression(); } """); - - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( - ["Compute", "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("src/calc.cs", json.GetProperty("path").GetString()); - Assert.Equal("property", json.GetProperty("caller_kind").GetString()); - Assert.Equal("Wrap", json.GetProperty("caller_name").GetString()); - Assert.Equal("Compute", json.GetProperty("callee_name").GetString()); - Assert.Equal(5, json.GetProperty("first_line").GetInt32()); - Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunCallers_ExactJson_FindsCallerInsideBraceSameLineAccessorNextLineProperty() - { - // issue #233 fifth review follow-up: the common Microsoft-style block-bodied - // property (`{` on the header line, accessor on the following line) must have - // CLI `callers` attribute the accessor call to the property itself. - // issue #233 第5次レビュー指摘: `{` が宣言行末にあり、accessor が次行にある - // 標準的な block-bodied property でも、CLI `callers` は accessor 内部の呼び出しを - // property に帰属させなければならない。 - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_brace_same_line"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "calc.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/same-line-brace-property.cs", """ - public class Calc + public class BraceCalc { - public int Compute() => 42; + public int ComputeBrace() => 42; - public int Wrap { - get { return Compute(); } + public int WrapBrace { + get { return ComputeBrace(); } } } """); - - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( - ["Compute", "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("src/calc.cs", json.GetProperty("path").GetString()); - Assert.Equal("property", json.GetProperty("caller_kind").GetString()); - Assert.Equal("Wrap", json.GetProperty("caller_name").GetString()); - Assert.Equal("Compute", json.GetProperty("callee_name").GetString()); - Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunCallers_ExactJson_FindsCallerInsideAllmanPropertyWithBlockComment() - { - // issue #233 fourth review follow-up: a multi-line /* ... */ block comment - // between the property header line and its `{` must not prevent CLI `callers` - // from attributing accessor-internal calls to the property itself. - // issue #233 の 4 回目レビュー指摘: property のヘッダ行と `{` の間に複数行の - // /* ... */ ブロックコメントが入っていても、CLI `callers` は accessor 内部の - // 呼び出しを外側クラスではなく property に帰属させなければならない。 - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_allman_prop_cmt"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "calc.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/allman-comment-property.cs", """ - public class Calc + public class CommentBlockCalc { - public int Compute() => 42; + public int ComputeCommentBlock() => 42; - public int Wrap + public int WrapCommentBlock /* some multi-line block comment */ { - get { return Compute(); } + get { return ComputeCommentBlock(); } } } """); - - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( - ["Compute", "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("src/calc.cs", json.GetProperty("path").GetString()); - Assert.Equal("property", json.GetProperty("caller_kind").GetString()); - Assert.Equal("Wrap", json.GetProperty("caller_name").GetString()); - Assert.Equal("Compute", json.GetProperty("callee_name").GetString()); - Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunCallers_ExactJson_FindsCallerInsideMultiLineExpressionPropertyWithBlockComment() - { - // issue #233 fourth review follow-up: a multi-line /* ... */ block comment - // between the property header line and its `=>` continuation must not prevent - // CLI `callers` from attributing the expression-body call to the property itself. - // issue #233 の 4 回目レビュー指摘: property のヘッダ行と `=>` 継続行の間に - // 複数行の /* ... */ ブロックコメントが入っていても、CLI `callers` は式本体の - // 呼び出しを外側クラスではなく property に帰属させなければならない。 - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_ml_exprprop_cmt"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "calc.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/expression-comment-property.cs", """ - public class Calc + public class CommentExpressionCalc { - public int Compute() => 42; + public int ComputeCommentExpression() => 42; - public int Wrap + public int WrapCommentExpression /* multi-line comment */ - => Compute(); + => ComputeCommentExpression(); + } + """); + TestProjectHelper.WriteTextFile( + projectRoot, + "src/multiline-switch-arm.cs", + """ + public class SwitchArm + { + public string Read(object value) + { + return value switch + { + string text + => text.Trim(), + _ => "" + }; + } } """); @@ -1479,22 +1267,47 @@ public int Wrap [projectRoot, "--json", "--quiet"], _jsonOptions)); - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( - ["Compute", "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - Assert.Equal(CommandExitCodes.Success, indexExitCode); Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("src/calc.cs", json.GetProperty("path").GetString()); - Assert.Equal("property", json.GetProperty("caller_kind").GetString()); - Assert.Equal("Wrap", json.GetProperty("caller_name").GetString()); - Assert.Equal("Compute", json.GetProperty("callee_name").GetString()); - Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); + + void AssertCaller( + string query, + string path, + string callerKind, + string callerName, + int? firstLine = null) + { + var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( + [query, "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact-name", "--lang", "csharp"], + _jsonOptions)); + using var document = ParseJsonOutput(stdout); + var json = document.RootElement; + + Assert.Equal(CommandExitCodes.Success, exitCode); + Assert.Equal(string.Empty, stderr); + Assert.Equal(path, json.GetProperty("path").GetString()); + Assert.Equal(callerKind, json.GetProperty("caller_kind").GetString()); + Assert.Equal(callerName, json.GetProperty("caller_name").GetString()); + Assert.Equal(query, json.GetProperty("callee_name").GetString()); + Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); + Assert.True(json.GetProperty("exact_index_available").GetBoolean()); + if (firstLine is not null) + { + Assert.Equal(firstLine.Value, json.GetProperty("first_line").GetInt32()); + } + } + + // These issue #233 regression shapes exercise one immutable graph. Unique callees + // keep every assertion isolated while avoiding a full CLI indexing pass per shape. + // issue #233 の各回帰形状は同一の不変 graph で検証する。callee を固有名にして + // アサーションを分離しつつ、形状ごとの CLI full index を避ける。 + AssertCaller("RunUpdateMode", "src/ternary.cs", "function", "Select", firstLine: 5); + AssertCaller("ComputeAllman", "src/allman-block-property.cs", "property", "WrapAllman"); + AssertCaller("ComputeExpression", "src/multiline-expression-property.cs", "property", "WrapExpression", firstLine: 5); + AssertCaller("ComputeBrace", "src/same-line-brace-property.cs", "property", "WrapBrace"); + AssertCaller("ComputeCommentBlock", "src/allman-comment-property.cs", "property", "WrapCommentBlock"); + AssertCaller("ComputeCommentExpression", "src/expression-comment-property.cs", "property", "WrapCommentExpression"); + AssertCaller("Trim", "src/multiline-switch-arm.cs", "function", "Read"); } finally { @@ -1536,66 +1349,6 @@ class C { int N => 0; void M() { var x = global::N.Color.Red; } } } } - [Fact] - public void RunCallers_ExactJson_MultiLineSwitchArm_AttributesToEnclosingFunction() - { - // issue #233 third review follow-up: inside a switch expression whose `=>` is - // placed on a continuation line, calls from the arm body must still attribute to - // the enclosing function. If the switch-expression guard does not cover the - // continuation `=>`, the pattern variable is emitted as a phantom property and - // `callers Trim` would return caller_kind=property, caller_name=text. - // issue #233 第3次レビュー指摘: switch expression arm の `=>` が継続行にある場合でも、 - // arm 本体の呼び出しは外側関数に帰属しなければならない。継続 `=>` まで switch-expression - // ガードを広げないと、パターン変数が phantom property になり、`callers Trim` が - // caller_kind=property, caller_name=text を返してしまう。 - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_callers_csharp_ml_switch_arm"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "sample.cs"), - """ - class C - { - string M(object o) - { - return o switch - { - string text - => text.Trim(), - _ => "" - }; - } - } - """); - - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunCallers( - ["Trim", "--db", Path.Combine(projectRoot, ".cdidx", "codeindex.db"), "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal("src/sample.cs", json.GetProperty("path").GetString()); - Assert.Equal("function", json.GetProperty("caller_kind").GetString()); - Assert.Equal("M", json.GetProperty("caller_name").GetString()); - Assert.Equal("Trim", json.GetProperty("callee_name").GetString()); - Assert.Equal(1, json.GetProperty("reference_count").GetInt32()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - [Fact] public void RunCallees_JsonZeroResults_WithMissingGraphTable_ReturnsDegradedPayload() { @@ -11185,61 +10938,6 @@ module com.example.app { } } - [Fact] - public void RunReferences_Json_CSharpNestedRawStringInsideInterpolationDoesNotCreatePhantomReference() - { - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_references_csharp_nested_raw_fixture"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "app.cs"), - """" - public class App - { - private int Run() => 1; - private string Id(string value) => value; - - public int Render() - { - return $""" - value = {Id(""" - Execute(); - public class Phantom - { - public void Go() { } - } - """) + Run()} - """.Length; - } - } - """"); - - var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db"); - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunReferences( - ["Execute", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - using var document = ParseJsonOutput(stdout); - var json = document.RootElement; - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stderr); - Assert.Equal(0, json.GetProperty("count").GetInt32()); - Assert.Equal(0, json.GetProperty("references").GetArrayLength()); - Assert.True(json.GetProperty("exact_index_available").GetBoolean()); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - [Fact] public void RunReferences_ExactJson_CSharpUsingStaticSingleLineConstantPattern_StaysSuppressed() { diff --git a/tests/CodeIndex.Tests/QueryCommandRunnerSearchGuardTests.cs b/tests/CodeIndex.Tests/QueryCommandRunnerSearchGuardTests.cs index bdc29aaca..90d9972cd 100644 --- a/tests/CodeIndex.Tests/QueryCommandRunnerSearchGuardTests.cs +++ b/tests/CodeIndex.Tests/QueryCommandRunnerSearchGuardTests.cs @@ -391,11 +391,12 @@ public void RunSearch_GuardFiltersKeepRequestedBudgetWithOriginFilters_Issue3423 try { var dbPath = TestProjectHelper.CreateProjectDb(projectRoot); - TestProjectHelper.InsertIndexedFile( - dbPath, - "src/GuardBudgetNeedle.cs", - "csharp", - """ + var fixtures = new[] + { + new TestProjectHelper.IndexedFileFixture( + "src/GuardBudgetNeedle.cs", + "csharp", + """ public class Guarded { public void Run() @@ -405,17 +406,17 @@ public void Run() } } """, - modified: new DateTime(2025, 2, 1, 0, 0, 0, DateTimeKind.Utc)); + Modified: new DateTime(2025, 2, 1, 0, 0, 0, DateTimeKind.Utc)), + }; const int expectedCandidateLimit = 200; - for (var i = 0; i < expectedCandidateLimit; i++) - { - TestProjectHelper.InsertIndexedFile( - dbPath, - $"src/zzz_noise_{i:000}.cs", - "csharp", - "public class Noise { public void Run() { GuardBudgetNeedle(); } }\n"); - } + TestProjectHelper.InsertIndexedFiles( + dbPath, + fixtures.Concat(Enumerable.Range(0, expectedCandidateLimit) + .Select(i => new TestProjectHelper.IndexedFileFixture( + $"src/zzz_noise_{i:000}.cs", + "csharp", + "public class Noise { public void Run() { GuardBudgetNeedle(); } }\n")))); var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunSearch( ["GuardBudgetNeedle", "--db", dbPath, "--exact-substring", "--require-before", "GuardMarker", "--guard-window", "2", "--exclude-strings", "--limit", "1", "--json=array"], diff --git a/tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs b/tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs index e35688dad..bed35e491 100644 --- a/tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs +++ b/tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs @@ -216,17 +216,16 @@ public void RunSearch_RecipeJsonRetainsClassifiedChildFailureAsInvalid_Issue4907 { var dbPath = TestProjectHelper.CreateProjectDb(projectRoot); const int minimumGuardedCandidates = 200; - for (var i = 0; i <= minimumGuardedCandidates; i++) - { - var path = $"src/C{i:D4}.cs"; - var content = $"public sealed class C{i:D4} {{ private string token = \"value\"; }}\n"; - TestProjectHelper.WriteTextFile(projectRoot, path, content); - TestProjectHelper.InsertIndexedFile( - dbPath, - path, - "csharp", - content); - } + var fixtures = Enumerable.Range(0, minimumGuardedCandidates + 1) + .Select(i => + { + var path = $"src/C{i:D4}.cs"; + var content = $"public sealed class C{i:D4} {{ private string token = \"value\"; }}\n"; + TestProjectHelper.WriteTextFile(projectRoot, path, content); + return new TestProjectHelper.IndexedFileFixture(path, "csharp", content); + }) + .ToArray(); + TestProjectHelper.InsertIndexedFiles(dbPath, fixtures); using var env = EnvironmentVariableScope.Capture(SearchAuditRecipes.RecipePathsEnvironmentVariable); env.Set(SearchAuditRecipes.RecipePathsEnvironmentVariable, null); @@ -1984,14 +1983,12 @@ public void RunSearch_FormatSarifReportsCompletionForLimitedCompleteEmptyAndMerg resultLimit: 20, truncated: false); - for (var i = 1; i < 126; i++) - { - TestProjectHelper.InsertIndexedFile( - dbPath, + TestProjectHelper.InsertIndexedFiles( + dbPath, + Enumerable.Range(1, 125).Select(i => new TestProjectHelper.IndexedFileFixture( $"src/app{i:D3}.cs", "csharp", - $"public class App{i:D3} {{ void Run() {{ Authenticate(); }} }}"); - } + $"public class App{i:D3} {{ void Run() {{ Authenticate(); }} }}"))); var (limitedExitCode, limitedStdout, limitedStderr) = CaptureConsole(() => QueryCommandRunner.RunSearch( [ @@ -2083,14 +2080,13 @@ public void RunSearch_FormatSarifReportsCompletionForLimitedCompleteEmptyAndMerg .GetString(); Assert.Contains("search --query -TODO", dashReplayCommand, StringComparison.Ordinal); - for (var i = 0; i <= DbReader.MaxGuardedSearchCandidates; i++) - { - TestProjectHelper.InsertIndexedFile( - dbPath, - $"src/guard{i:D4}.cs", - "csharp", - $"public class GuardFixture{i:D4} {{ void Run() {{ GuardNeedle(); Continue(); }} }}"); - } + TestProjectHelper.InsertIndexedFiles( + dbPath, + Enumerable.Range(0, DbReader.MaxGuardedSearchCandidates + 1) + .Select(i => new TestProjectHelper.IndexedFileFixture( + $"src/guard{i:D4}.cs", + "csharp", + $"public class GuardFixture{i:D4} {{ void Run() {{ GuardNeedle(); Continue(); }} }}"))); var (guardedExitCode, guardedStdout, guardedStderr) = CaptureConsole(() => QueryCommandRunner.RunSearch( ["GuardNeedle", "--db", dbPath, "--format", "sarif", "--exact-substring", "--reject-after", "NeverPresent", "--limit", "1"], _jsonOptions)); @@ -3311,8 +3307,14 @@ public void RunSearch_JsonTrustBoundaryClassifiesPrivateWritersAndUntrustedParse try { var dbPath = TestProjectHelper.CreateProjectDb(projectRoot); - TestProjectHelper.InsertIndexedFile( - dbPath, + var fixtures = new List(); + + void AddFixture(string path, string lang, string content) + { + fixtures.Add(new TestProjectHelper.IndexedFileFixture(path, lang, content)); + } + + AddFixture( "src/private-writer.cs", "csharp", """ @@ -3327,8 +3329,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/public-writer.cs", "csharp", """ @@ -3343,8 +3344,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/review-required-public-writer.cs", "csharp", """ @@ -3359,8 +3359,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/mixed-boundary-writer.cs", "csharp", """ @@ -3378,8 +3377,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/annotation-bleed-writer.cs", "csharp", """ @@ -3396,8 +3394,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/same-line-intervening-writer.cs", "csharp", """ @@ -3412,8 +3409,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/same-line-block-intervening-writer.cs", "csharp", """ @@ -3428,8 +3424,7 @@ public static JavaScriptEncoder Create(bool skip) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/same-line-multiple-writer.cs", "csharp", """ @@ -3444,8 +3439,7 @@ public static object Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/same-line-prior-getter-writer.cs", "csharp", """ @@ -3465,8 +3459,7 @@ public static object Create(Source source) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/first-named-argument-writer.cs", "csharp", """ @@ -3483,8 +3476,7 @@ public static JavaScriptEncoder Create() private static JavaScriptEncoder Consume(JavaScriptEncoder encoder) => encoder; } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/nullable-declaration-writer.cs", "csharp", """ @@ -3500,8 +3492,7 @@ public static class NullableDeclarationWriter } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/comparison-operand-writer.cs", "csharp", """ @@ -3516,8 +3507,7 @@ public static bool Create(object existing) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/nested-generic-first-argument-writer.cs", "csharp", """ @@ -3535,8 +3525,7 @@ public static JavaScriptEncoder Create() private static JavaScriptEncoder Consume(JavaScriptEncoder encoder) => encoder; } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/indexer-target-writer.cs", "csharp", """ @@ -3557,8 +3546,7 @@ public static JavaScriptEncoder Create(JavaScriptEncoder[] sink, IndexHolder hol } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/property-receiver-writer.cs", "csharp", """ @@ -3583,8 +3571,7 @@ public static void Create(ReceiverSink sink) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/single-hop-property-receiver-writer.cs", "csharp", """ @@ -3606,8 +3593,7 @@ public static void Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/private-looking-writer.cs", "csharp", """ @@ -3623,8 +3609,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/direction-mismatch-writer.cs", "csharp", """ @@ -3639,8 +3624,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/invalid-annotation-writer.cs", "csharp", """ @@ -3655,8 +3639,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/raw-string-writer.cs", "csharp", """" @@ -3673,8 +3656,7 @@ public static JavaScriptEncoder Create() } } """"); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/verbatim-string-writer.cs", "csharp", """ @@ -3691,8 +3673,7 @@ public static JavaScriptEncoder Create() } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/nested-serializer-writer.cs", "csharp", """ @@ -3707,8 +3688,7 @@ public static string Create(object payload) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/cast-serializer-writer.cs", "csharp", """ @@ -3723,8 +3703,7 @@ public static object Create(object payload) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/multiline-serializer-writer.cs", "csharp", """ @@ -3741,8 +3720,7 @@ public static string Create(object payload) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/conditional-assignment-serializer-writer.cs", "csharp", """ @@ -3757,8 +3735,7 @@ public static string Create(bool condition, object payload, ref string result) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/default-parameter-expression-bodied-serializer-writer.cs", "csharp", """ @@ -3770,8 +3747,7 @@ public static class DefaultParameterExpressionBodiedSerializerWriter public static string Create(object payload, int count = 1) => JsonSerializer.Serialize(payload); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/property-invocation-receiver-writer.cs", "csharp", """ @@ -3796,8 +3772,7 @@ public static string Create(PropertyInvocationSource source, object payload) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/direct-invocation-receiver-writer.cs", "csharp", """ @@ -3815,8 +3790,7 @@ public static string Create(DirectInvocationSource source, object payload) => source.Build(JsonSerializer.Serialize(payload)); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/bare-property-invocation-receiver-writer.cs", "csharp", """ @@ -3836,8 +3810,7 @@ public static string Create(object payload) => Factory.Build(JsonSerializer.Serialize(payload)); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/conditional-compilation-writer.cs", "csharp", """ @@ -3851,8 +3824,7 @@ public static class ConditionalCompilationWriter public static string Create(object payload) => JsonSerializer.Serialize(payload); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/parenthesized-conditional-compilation-writer.cs", "csharp", """ @@ -3866,8 +3838,7 @@ public static class ParenthesizedConditionalCompilationWriter public static string Create(object payload) => JsonSerializer.Serialize(payload); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/negated-conditional-compilation-writer.cs", "csharp", """ @@ -3881,8 +3852,7 @@ public static class NegatedConditionalCompilationWriter public static string Create(object payload) => JsonSerializer.Serialize(payload); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/repeated-assignment-property-receiver-writer.cs", "csharp", """ @@ -3904,8 +3874,7 @@ public static void Create(object payload) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/explicit-utf8-writer.cs", "csharp", """ @@ -3921,8 +3890,7 @@ public static void Create(Stream stream) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/multiline-explicit-utf8-writer.cs", "csharp", """ @@ -3939,8 +3907,7 @@ public static void Create(Stream stream) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/long-multiline-utf8-writer.cs", "csharp", """ @@ -3961,8 +3928,7 @@ public static void Create(Stream stream) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/expression-bodied-utf8-writer.cs", "csharp", """ @@ -3975,8 +3941,7 @@ public static class ExpressionBodiedUtf8Writer public static Utf8JsonWriter Create(Stream stream) => new Utf8JsonWriter(stream); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/generic-return-utf8-writer.cs", "csharp", """ @@ -3991,8 +3956,7 @@ public static class GenericReturnUtf8Writer public static GenericWriterHolder Create(Stream stream) => new(new Utf8JsonWriter(stream)); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/outer-generic-return-utf8-writer.cs", "csharp", """ @@ -4006,8 +3970,7 @@ public static class OuterGenericReturnUtf8Writer public static Tuple Create(Stream stream) => new(new Utf8JsonWriter(stream), 1); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/split-generic-return-utf8-writer.cs", "csharp", """ @@ -4024,8 +3987,7 @@ public static SplitGenericWriterHolder< new(new Utf8JsonWriter(stream)); } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/qualified-utf8-writer.cs", "csharp", """ @@ -4040,8 +4002,7 @@ public static void Create(Stream stream) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/cross-query-writer.cs", "csharp", """ @@ -4057,8 +4018,7 @@ public static string Create(Stream stream) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/overlapping-parser.cs", "csharp", """ @@ -4074,8 +4034,7 @@ public static object Parse(Stream stream) } } """); - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( "src/composite-cast-parser.cs", "csharp", """ @@ -4099,8 +4058,7 @@ public static class CompositeCastParser ("src/file-parser.cs", "file", "user_selected_file"), }) { - TestProjectHelper.InsertIndexedFile( - dbPath, + AddFixture( path, "csharp", $$""" @@ -4117,6 +4075,8 @@ public static JsonDocument Parse(string payload) """); } + TestProjectHelper.InsertIndexedFiles(dbPath, fixtures); + var (writerExitCode, writerStdout, writerStderr) = CaptureConsole(() => QueryCommandRunner.RunSearch( ["--recipe", "dogfood-risk-patterns/relaxed-json-encoder", "--db", dbPath, "--json", "--limit", "30", "--snippet-lines", "1"], _jsonOptions)); @@ -5937,8 +5897,12 @@ public void RunSearch_GuardLimitErrorIncludesCandidateStats_Issue3940() try { var dbPath = TestProjectHelper.CreateProjectDb(projectRoot); - for (var i = 0; i < 201; i++) - TestProjectHelper.InsertIndexedFile(dbPath, $"src/guard-budget-{i:0000}.cs", "csharp", "public void Run() { GuardStatsNeedle(); }\n"); + TestProjectHelper.InsertIndexedFiles( + dbPath, + Enumerable.Range(0, 201).Select(i => new TestProjectHelper.IndexedFileFixture( + $"src/guard-budget-{i:0000}.cs", + "csharp", + "public void Run() { GuardStatsNeedle(); }\n"))); var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunSearch( ["GuardStatsNeedle", "--db", dbPath, "--require-before", "MissingGuardMarker", "--guard-window", "1", "--limit", "1"], @@ -12294,15 +12258,13 @@ public void RunSearch_AdHocIssueDraftsPreserveSourceTotalsSelectorsAndReplay_Iss try { var dbPath = TestProjectHelper.CreateProjectDb(projectRoot); - for (var i = 0; i < 126; i++) - { - TestProjectHelper.InsertIndexedFile( - dbPath, + TestProjectHelper.InsertIndexedFiles( + dbPath, + Enumerable.Range(0, 126).Select(i => new TestProjectHelper.IndexedFileFixture( $"src/replay's/match{i:D3}.cs", "csharp", $"public sealed class Issue4838Needle{i:D3} {{ }}\n", - isGenerated: true); - } + IsGenerated: true))); var args = new[] { @@ -12785,8 +12747,12 @@ public void RunSearch_NormalizesLanguageAliasesAcrossSharedIndex() Aliases: new[] { "fs" }), }; - foreach (var testCase in cases) - TestProjectHelper.InsertIndexedFile(dbPath, testCase.Path, testCase.Lang, testCase.Query); + TestProjectHelper.InsertIndexedFiles( + dbPath, + cases.Select(testCase => new TestProjectHelper.IndexedFileFixture( + testCase.Path, + testCase.Lang, + testCase.Query))); foreach (var testCase in cases) { @@ -14659,32 +14625,33 @@ public void ExactCanonicalFixtureCoversCSharpJavaAndKotlinForms() try { var dbPath = TestProjectHelper.CreateProjectDb(projectRoot); - TestProjectHelper.InsertIndexedFile( + TestProjectHelper.InsertIndexedFiles( dbPath, - "src/app.cs", - "csharp", - """ + [ + new TestProjectHelper.IndexedFileFixture( + "src/app.cs", + "csharp", + """ namespace Demo; using @Foo.@Bar; - """); - TestProjectHelper.InsertIndexedFile( - dbPath, - "src/App.kt", - "kotlin", - """ + """), + new TestProjectHelper.IndexedFileFixture( + "src/App.kt", + "kotlin", + """ fun `when`() {} - """); - TestProjectHelper.InsertIndexedFile( - dbPath, - "src/App.java", - "java", - """ + """), + new TestProjectHelper.IndexedFileFixture( + "src/App.java", + "java", + """ public class \u0046oo { void match() {} } - """); + """), + ]); var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunSearch( ["Foo.Bar", "--db", dbPath, "--path", "src/app.cs", "--json", "--exact-substring", "--count"], @@ -17341,14 +17308,12 @@ void Run() Assert.Contains(nextSteps, step => step.GetProperty("command").GetString()!.Contains("cdidx inspect", StringComparison.Ordinal)); Assert.Contains(nextSteps, step => step.GetProperty("command").GetString()!.Contains("cdidx excerpt", StringComparison.Ordinal)); - for (var i = 0; i < 11; i++) - { - TestProjectHelper.InsertIndexedFile( - dbPath, + TestProjectHelper.InsertIndexedFiles( + dbPath, + Enumerable.Range(0, 11).Select(i => new TestProjectHelper.IndexedFileFixture( $"src/many{i}.cs", "csharp", - $"public class Many{i} {{ void Run() {{ ManyNextStepNeedle(); }} }}\n"); - } + $"public class Many{i} {{ void Run() {{ ManyNextStepNeedle(); }} }}\n"))); var (manyExitCode, manyStdout, manyStderr) = CaptureConsole(() => QueryCommandRunner.RunSearch( ["ManyNextStepNeedle", "--db", dbPath, "--exact-substring", "--json=array", "--next-steps", "--limit", "20"], diff --git a/tests/CodeIndex.Tests/QueryCommandRunnerSymbolTests.cs b/tests/CodeIndex.Tests/QueryCommandRunnerSymbolTests.cs index 56a1e476a..1030c5615 100644 --- a/tests/CodeIndex.Tests/QueryCommandRunnerSymbolTests.cs +++ b/tests/CodeIndex.Tests/QueryCommandRunnerSymbolTests.cs @@ -7286,16 +7286,16 @@ public void RunSymbols_BlankPositionalQueryReturnsDistinctUsageError() } [Fact] - public void RunSymbols_Json_CSharpNestedRawStringInsideInterpolationDoesNotCreatePhantomSymbols() + public void RunSymbols_Json_CSharpStringLiteralBoundariesShareIndexedWorkspace() { - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_symbols_csharp_nested_raw_fixture"); + var projectRoot = TestProjectHelper.CreateTempProject("cdidx_symbols_csharp_string_literal_workspace"); try { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "app.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/nested-raw.cs", """" - public class App + public class NestedRawSymbolsApp { private int Run() => 1; private string Id(string value) => value; @@ -7304,7 +7304,7 @@ public int Render() { return $""" value = {Id(""" - public class Phantom + public class NestedRawPhantom { public void Go() { } } @@ -7313,43 +7313,16 @@ public void Go() { } } } """"); - - var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db"); - var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( - [projectRoot, "--json", "--quiet"], - _jsonOptions)); - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunSymbols( - ["Phantom", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); - - Assert.Equal(CommandExitCodes.Success, indexExitCode); - Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stdout); - Assert.Equal(string.Empty, stderr); - } - finally - { - TestProjectHelper.DeleteDirectory(projectRoot); - } - } - - [Fact] - public void RunSymbols_Json_CSharpInterpolatedVerbatimStringEscapedBracesDoNotCreatePhantomSymbols() - { - var projectRoot = TestProjectHelper.CreateTempProject("cdidx_symbols_csharp_escaped_verbatim_braces"); - try - { - Directory.CreateDirectory(Path.Combine(projectRoot, "src")); - File.WriteAllText( - Path.Combine(projectRoot, "src", "app.cs"), + TestProjectHelper.WriteTextFile( + projectRoot, + "src/escaped-verbatim.cs", """ - public class App + public class EscapedVerbatimSymbolsApp { public string Render() { return $@"{{ - public class Phantom + public class EscapedVerbatimPhantom }}"; } } @@ -7359,15 +7332,23 @@ public class Phantom var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run( [projectRoot, "--json", "--quiet"], _jsonOptions)); - var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunSymbols( - ["Phantom", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], - _jsonOptions)); Assert.Equal(CommandExitCodes.Success, indexExitCode); Assert.Equal(string.Empty, indexStderr); - Assert.Equal(CommandExitCodes.Success, exitCode); - Assert.Equal(string.Empty, stdout); - Assert.Equal(string.Empty, stderr); + + void AssertNoSymbols(string query) + { + var (exitCode, stdout, stderr) = CaptureConsole(() => QueryCommandRunner.RunSymbols( + [query, "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"], + _jsonOptions)); + + Assert.Equal(CommandExitCodes.Success, exitCode); + Assert.Equal(string.Empty, stdout); + Assert.Equal(string.Empty, stderr); + } + + AssertNoSymbols("NestedRawPhantom"); + AssertNoSymbols("EscapedVerbatimPhantom"); } finally { diff --git a/tests/CodeIndex.Tests/TestProjectHelper.cs b/tests/CodeIndex.Tests/TestProjectHelper.cs index 396bdb81d..498e1d56b 100644 --- a/tests/CodeIndex.Tests/TestProjectHelper.cs +++ b/tests/CodeIndex.Tests/TestProjectHelper.cs @@ -16,6 +16,13 @@ namespace CodeIndex.Tests; internal static class TestProjectHelper { + internal sealed record IndexedFileFixture( + string Path, + string Lang, + string Content, + DateTime? Modified = null, + bool IsGenerated = false); + internal const string TrustedTestRootEnvironmentVariable = "CDIDX_TEST_TRUSTED_TEMP_ROOT"; internal static string RepeatCsvEntry(string value, int count) @@ -263,44 +270,91 @@ internal static void InsertIndexedFile( bool isGenerated = false, bool releasePoolForFileAccess = false) { + using var db = new DbContext(DbOpenIntent.WriteIndex, dbPath); + db.InitializeSchema(); + + var writer = new DbWriter(db.Connection); + InsertIndexedFile( + writer, + new IndexedFileFixture(path, lang, content, modified, isGenerated), + deferReferenceRefresh: false); + + if (releasePoolForFileAccess) + SqliteConnection.ClearPool(db.Connection); + } + + internal static void InsertIndexedFiles( + string dbPath, + IEnumerable files, + bool releasePoolForFileAccess = false) + { + using var db = new DbContext(DbOpenIntent.WriteIndex, dbPath); + db.InitializeSchema(); + + var writer = new DbWriter(db.Connection); + using var hotspotAggregateRefresh = writer.BeginDeferredHotspotReferenceAggregateRefresh(); + using var transaction = writer.BeginTransaction(); + var hasReferences = false; + foreach (var file in files) + hasReferences |= InsertIndexedFile(writer, file, deferReferenceRefresh: true); + if (hasReferences) + writer.RefreshMutualRecursionFlags(); + hotspotAggregateRefresh.Complete(CancellationToken.None); + transaction.Commit(); + + if (releasePoolForFileAccess) + SqliteConnection.ClearPool(db.Connection); + } + + private static bool InsertIndexedFile( + DbWriter writer, + IndexedFileFixture file, + bool deferReferenceRefresh) + { + var path = file.Path; + var lang = file.Lang; + var content = file.Content; var normalized = content.Replace("\r\n", "\n"); var lines = normalized.Split('\n'); var lineCount = FileIndexer.CountPhysicalLines(content); - using (var db = new DbContext(DbOpenIntent.WriteIndex, dbPath)) + var fileId = writer.UpsertFile(new FileRecord { - db.InitializeSchema(); - - var writer = new DbWriter(db.Connection); - var fileId = writer.UpsertFile(new FileRecord + Path = path, + Lang = lang, + Size = normalized.Length, + Lines = lineCount, + Modified = file.Modified ?? new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc), + Checksum = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(normalized))).ToLowerInvariant(), + Generated = file.IsGenerated, + }); + + writer.InsertChunks([ + new ChunkRecord { - Path = path, - Lang = lang, - Size = normalized.Length, - Lines = lineCount, - Modified = modified ?? new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc), - Checksum = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(normalized))).ToLowerInvariant(), - Generated = isGenerated, - }); - - writer.InsertChunks([ - new ChunkRecord - { - FileId = fileId, - ChunkIndex = 0, - StartLine = 1, - EndLine = lines.Length, - Content = normalized, - } - ]); - - var symbols = SymbolExtractor.Extract(fileId, lang, normalized, path); - writer.InsertSymbols(symbols); - writer.InsertReferences(ReferenceExtractor.Extract(fileId, lang, normalized, symbols, path)); - - if (releasePoolForFileAccess) - SqliteConnection.ClearPool(db.Connection); + FileId = fileId, + ChunkIndex = 0, + StartLine = 1, + EndLine = lines.Length, + Content = normalized, + } + ]); + + var symbols = SymbolExtractor.Extract(fileId, lang, normalized, path); + writer.InsertSymbols(symbols); + var references = ReferenceExtractor.Extract(fileId, lang, normalized, symbols, path); + if (deferReferenceRefresh) + { + writer.InsertReferencesInAtomicFileScope( + references, + refreshMutualRecursionFlags: false, + CancellationToken.None); + } + else + { + writer.InsertReferences(references); } + return references.Count > 0; } internal static void InsertFreshIndexedFile( diff --git a/tests/CodeIndex.Tests/TestProjectHelperTests.cs b/tests/CodeIndex.Tests/TestProjectHelperTests.cs index c440320c9..683f63009 100644 --- a/tests/CodeIndex.Tests/TestProjectHelperTests.cs +++ b/tests/CodeIndex.Tests/TestProjectHelperTests.cs @@ -86,6 +86,65 @@ public void InsertIndexedFileAndDeleteDatabaseFiles_UseFailureDrivenPoolRelease( Assert.False(File.Exists(dbPath + "-shm")); } + [Fact] + public void InsertIndexedFiles_CommitsMixedLanguageBatchAndRollsBackFailedEnumeration() + { + using var project = TestProjectHelper.CreateTempProjectScope("cdidx_fixture_database_batch"); + var dbPath = TestProjectHelper.CreateProjectDb(project.Root); + TestProjectHelper.InsertIndexedFiles( + dbPath, + [ + new TestProjectHelper.IndexedFileFixture( + "src/App.cs", + "csharp", + "public class App { public void Run() { Target(); } }\n"), + new TestProjectHelper.IndexedFileFixture( + "src/app.py", + "python", + "def target():\n pass\n\ntarget()\n"), + new TestProjectHelper.IndexedFileFixture( + "src/app.ts", + "typescript", + "export function target(): void {}\ntarget();\n"), + ]); + + var exception = Assert.Throws(() => + TestProjectHelper.InsertIndexedFiles(dbPath, FailingBatch())); + Assert.Equal("fixture enumeration failed", exception.Message); + + var connectionString = new SqliteConnectionStringBuilder + { + DataSource = dbPath, + Mode = SqliteOpenMode.ReadOnly, + Pooling = false, + }.ToString(); + using var connection = new SqliteConnection(connectionString); + connection.Open(); + + Assert.Equal(3, ExecuteScalar("SELECT COUNT(*) FROM files")); + Assert.Equal(3, ExecuteScalar("SELECT COUNT(DISTINCT lang) FROM files")); + Assert.Equal(3, ExecuteScalar("SELECT COUNT(*) FROM chunks")); + Assert.True(ExecuteScalar("SELECT COUNT(*) FROM symbols") >= 3); + Assert.True(ExecuteScalar("SELECT COUNT(*) FROM symbol_references") >= 3); + Assert.Equal(0, ExecuteScalar("SELECT COUNT(*) FROM files WHERE path = 'src/rolled-back.rb'")); + + long ExecuteScalar(string sql) + { + using var command = connection.CreateCommand(); + command.CommandText = sql; + return Assert.IsType(command.ExecuteScalar()); + } + + static IEnumerable FailingBatch() + { + yield return new TestProjectHelper.IndexedFileFixture( + "src/rolled-back.rb", + "ruby", + "def target\nend\ntarget\n"); + throw new InvalidOperationException("fixture enumeration failed"); + } + } + [Fact] public void InsertIndexedFile_ReleasePoolForFileAccess_AllowsStandaloneRawRead() {