sync: collector/ 同步自 inkstone/arcrun-rag@4982b50(桌面小幫手 0.18.43)

This commit is contained in:
2026-08-28 01:48:25 +08:00
parent 3d1cb83110
commit ef5fe1df6f
16 changed files with 1026 additions and 45 deletions
+116
View File
@@ -0,0 +1,116 @@
package collector
// foldertree_live_test.go — 對**真實的監看資料夾**跑一次樹,用 leo 給的機械判準對帳
//`inkstone/Arcrun#180`leo 2026-08-28
// 「就在桌面,你為什麼沒看?最簡單的方式就像 terminal tree 一樣把被同步資料夾下的
// 所有 folder 都遍歷後印出來」)。
//
// 🔴 為什麼 fixture 不夠(同 sourcerepair_live_test.go 的理由):
// `foldertree_traversal_test.go` 用的是我們自己擺出來的形狀——它證明得了邏輯,
// 證明不了「**leo 桌面上那個資料夾**現在到底展不展得開」。
// 這張票的現象是他螢幕上的數字,對帳就要拿他螢幕上那個資料夾來對。
//
// 只讀磁碟、不碰網路、不寫 manifest,所以沒有「打錯實例」的風險;
// 仍然預設 skip,因為路徑是機器專屬的。
//
// LIVE_TREE_ROOTS="/Users/x/Desktop/youlinhsieh-test1,/Users/x/Desktop/youlinhsieh-test2" \
// go test -run TestLiveFolderTreeMatchesDisk -v ./
//
// 判準(leo 原話):**`find <root> -type d` 數出幾個目錄,雲端的樹就該有幾個節點**
//(收不收是另一回事,`skip_reason` 照標)。
// 例外只有一種且必須說得出口:**真的沒走進去**的那幾類(gitignore/工具產物/
// 建置目錄/worktree/巢狀 repo)——它們仍然佔一個節點,但底下不生子節點,
// 所以本測試把它們的子樹從地端這一側也扣掉,兩邊才是同一個問題的兩個答案。
import (
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"time"
)
// diskDirsExcludingPrunedSubtrees=地端目錄清單,但「真的沒走進去」的那幾類只算它自己。
func diskDirsExcludingPrunedSubtrees(t *testing.T, root string, plan IngestPlan) []string {
t.Helper()
var out []string
err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
if err != nil || !d.IsDir() {
return err //nolint:nilerr // 讀不到的目錄交給 WalkDir 自己回報
}
if p == root {
out = append(out, "")
return nil
}
if strings.HasPrefix(d.Name(), ".") {
return filepath.SkipDir
}
rel, _ := filepath.Rel(root, p)
rel = filepath.ToSlash(rel)
out = append(out, rel)
if skip, _ := plan.SkipsDirWhy(rel, p); skip {
return filepath.SkipDir // 節點留著(上一行已經 append),子樹不算
}
return nil
})
if err != nil {
t.Fatal(err)
}
sort.Strings(out)
return out
}
func TestLiveFolderTreeMatchesDisk(t *testing.T) {
raw := strings.TrimSpace(os.Getenv("LIVE_TREE_ROOTS"))
if raw == "" {
t.Skip("設 LIVE_TREE_ROOTS=<資料夾,資料夾> 才跑(路徑是機器專屬的)")
}
for _, root := range strings.Split(raw, ",") {
root = strings.TrimSpace(root)
if root == "" {
continue
}
t.Run(filepath.Base(root), func(t *testing.T) {
plan := PlanIngest(root)
m := &Manifest{Entries: map[string]*ManifestEntry{}}
p, err := Scan(root, m, ScanOptions{Plan: plan})
if err != nil {
t.Fatal(err)
}
tree := BuildFolderTree(root, "live", p.DirStats, m.Entries, p.AllExcludedDirs, plan, time.Now())
onTree := map[string]FolderNode{}
for _, n := range tree.Nodes {
onTree[n.Path] = n
}
want := diskDirsExcludingPrunedSubtrees(t, root, plan)
t.Logf("收檔策略:%s — %s", plan.Mode, plan.Reason)
t.Logf("地端目錄 %d(扣掉沒走進去的子樹)/樹上節點 %dtruncated=%v",
len(want), tree.TotalNodes, tree.Truncated)
for _, n := range tree.Nodes {
t.Logf(" %-46s tot=%-4d sync=%-4d excl=%-4d skipped=%v %s",
"'"+n.Path+"'", n.TotalFiles, n.SyncedFiles, n.ExcludedFiles, n.Skipped, n.SkipReason)
}
var missing []string
for _, w := range want {
if _, ok := onTree[w]; !ok {
missing = append(missing, w)
}
}
if len(missing) > 0 {
t.Fatalf("地端有、樹上沒有的資料夾(「不收」又被實作成「不走進去」了):%v", missing)
}
if tree.TotalNodes != len(want) {
t.Fatalf("樹上節點 %d ≠ 地端資料夾 %d", tree.TotalNodes, len(want))
}
if tree.Truncated {
t.Fatalf("被截斷了——MaxFolderTreeNodes=%d 不夠這個資料夾用,不准靜默截斷",
MaxFolderTreeNodes)
}
})
}
}