Testing Files Generator
繁體中文

文件

工具的全部功能,依人們實際會問的問題來組織。儲存庫中的 README 是完整參考,並且始終與你下載的版本一致。

有哪些指令?

每個指令只做一件事:

tfg generate    依配方或參數產生檔案
tfg validate    檢查配方,不寫入任何內容
tfg verify      對照清單檢查目錄
tfg cleanup     刪除清單中列出的檔案
tfg recipe fmt  以標準形式列印配方
tfg preset      依具名的測試問題建立一組檔案
tfg formats     列出此版本支援的格式
tfg damage      列出此版本可以刻意破壞檔案的方式
tfg tool        處理現有檔案的小工具
tfg version     列印工具版本
tfg license     列印授權條款及其對產生檔案的意義

如何產生一個大小精確的檔案?

指定格式、大小與輸出位置。大小以 1024 進位計算,所以 2mb 是 2097152 位元組。直接寫位元組數也可以,所以 --size 10485761 要求的就是恰好這麼多。

tfg generate --format png --size 2mb --out ./out

generate 常用的參數:

參數作用
--format <id>檔案格式,例如 txt
--size <size>每個檔案的精確大小,例如 10mb 或直接寫位元組數
--size-range <a-b>從範圍內為每個檔案抽取一個大小,例如 1kb-8kb。抽取結果來自種子
--boundary <size>圍繞一個限制的三個檔案:小一位元組、恰好等於限制、大一位元組
--count <n>產生多少個檔案。預設 1
--name <template>檔名範本,例如 invoice_{index:04}.txt
--out <dir>寫入的目錄
--seed <n>本次執行的種子。相同的種子得到相同的位元組
--set <k>=<v>一項格式設定,可重複使用
--damage <name>刻意破壞檔案,可重複使用,並依序套用。執行 tfg damage 查看清單
--expected <outcome>accept、reject、sanitize 或 unspecified
--dry-run只統計並顯示,完全不寫入
--json將清單寫到標準輸出

如何做出刻意損壞的檔案?

本工具寫出的其他所有檔案在構造上都是正確的,這回答了上傳驗證器會問的三個問題中的兩個。--damage 回答第三個,也就是檔案到底能不能開啟。檔案先正常產生,再被破壞,因此仍然維持你要求的大小。

tfg generate --format png --size 2mb --damage zero-head --out ./out
tfg generate --format png --size 2mb --damage zero-head:bytes=16 --out ./out

設定寫在冒號後面。該參數可以重複,寫入的順序就是套用的順序。tfg damage 會列出此版本能做什麼,以及每種破壞接受哪些設定。

在配方中,這個鍵是一個清單,內容是名稱或設定:

targets:
  - id: broken
    format: png
    count: 5
    size: 2mb
    damage:
      - zero-head
      - type: zero-head
        bytes: 16

受損檔案在清單中會得到 expected: reject,並在旁邊記錄所做的破壞。有兩種情況會在寫入任何內容之前被拒絕,因為各自都會在磁碟上留下清單描述有誤的檔案:

第三種無法事先得知。如果某個破壞執行後沒有改動任何位元組,該檔案會被捨棄而不是寫出,執行會繼續,指出是哪個檔案,並以部分完成的結束碼收尾。

一步一步來,附帶一個讀取清單的測試:如何製作用於測試的損毀檔案。

配方長什麼樣子?

配方是一個描述整次執行的 YAML 檔案。把它與測試放在一起提交,fixture 就不再是儲存庫裡的二進位檔,任何人都可以用一個只有幾百個字元的檔案逐位元組重建它們。

# fixtures.yaml
version: 1
seed: 7741

defaults:
  label: true

targets:
  - id: invoices
    format: pdf
    count: 25
    size: 300kb
    name: invoice_{index:04}.pdf
    properties:
      pages: 3
      page_size: a4
    expected: accept

  - id: over_the_limit
    format: png
    count: 2
    size: 12mb
    expected:
      outcome: reject
      reason: size_limit

  - id: bundle
    format: zip
    contains:
      - format: txt
        count: 200
        size: 4kb

output:
  dir: ./fixtures
  manifest: manifest.json
tfg validate fixtures.yaml
tfg generate fixtures.yaml

每個 target 必須恰好有 size、size-range、boundary 或 contains 其中一個。兩個是錯誤,一個都沒有也是錯誤。無效的配方不會寫入任何檔案,並且會一次回報所有問題,而不是只報第一個,每個問題都會指明所涉及的設定。

如何宣告我的系統應如何處理某個檔案?

只需結果時用簡短寫法,原因重要時用完整寫法:

expected: accept
expected:
  outcome: reject
  reason: size_limit

結果有 accept、reject、sanitize 與 unspecified。原因是封閉清單,方便報告依原因分組:content_malformed、count_limit、dimensions_limit、duplicate、encoding_invalid、extension_rule、filename_invalid、filename_too_long、filename_traversal、malware_signature、mime_mismatch、nesting_depth、none、size_limit 與 size_zero。

原因指明的是起作用的規則,而不是裁決。所以同一個原因可以出現在兩種結果之下:比限制小一位元組的檔案是 accept,而它所涉及的規則仍然是 size_limit。

清單裡有什麼?

每次執行結束時,包括被中斷的執行,它都會寫在檔案旁邊。每個檔案一項:

{
  "manifest_version": "1.0",
  "tool": { "name": "testing-files-generator", "version": "0.4.0" },
  "run": {
    "id": "run_b359aa8d94",
    "seed": 0,
    "command": "tfg generate --format png --size 2mb --out ./out",
    "platform": { "os": "windows", "arch": "amd64" },
    "complete": true
  },
  "summary": {
    "file_count": 1,
    "total_bytes": 2097152,
    "by_format": { "png": 1 },
    "by_expected": { "unspecified": 1 }
  },
  "files": [
    {
      "path": "files_0001.png",
      "bytes": 2097152,
      "format": "png",
      "fidelity": "full",
      "determinism": "byte",
      "seed": "8dc2d18c",
      "hashes": { "sha256": "1a1f7c..." },
      "properties": { "width": 640, "height": 480 },
      "expected": {
        "outcome": "unspecified",
        "detail": "No expectation was declared for this file.",
        "confidence": "policy_dependent"
      }
    }
  ]
}

執行來自配方時會加上 recipe_hash,來自預設集時會加上 preset 與 overrides,因此清單總能追溯到產生它的來源。

每一項還帶有 target_id,也就是配方中產生該檔案的 target 的 id,summary.by_target 則統計每個 target 產生的檔案數。因此有多個 target 的配方可以逐個 target 檢查,無需閱讀檔名。

什麼是預設集?

預設集是回答常見測試問題的現成檔案組,你不必自己設計。預設集底層就是普通配方,eject 會把配方列印出來,供你從那裡開始編輯。每個預設集都有自己的頁面,說明它通常能發現什麼、組合裡有什麼,以及它接受的每項設定。

tfg preset list
tfg preset show size-boundaries
tfg generate --preset size-boundaries --limit 10mb --out ./edges
tfg preset eject size-boundaries > my.yaml

show 會在你建置之前告訴你這個組合的開銷,並且在某個數字只是我們的預留值而不是你的限制時直接說明。

結束碼是什麼意思?

每種結束方式都有自己的代碼,機器可讀的輸出寫到標準輸出,失敗的執行不會在那裡列印任何內容。這張表是凍結的約定,改變某個代碼的意義需要提升主版本號。

代碼 意義
0 一切正常。
1 工具內部發生非預期的錯誤。
2 指令或參數有誤。
3 配方無效。
4 該格式無法完成所要求的操作。
5 讀取或寫入失敗。
6 磁碟空間不足。
7 verify 發現不一致。
8 執行已結束,但並非所有檔案都已產生。
130 被 Ctrl+C 中斷。
143 被訊號終止,CI 逾時就是這個樣子。
- name: build the fixtures
  run: tfg generate fixtures.yaml --out ./fixtures

- name: run the tests
  run: pytest tests/

- name: nothing moved
  run: tfg verify ./fixtures/manifest.json

被 Ctrl+C 停止的執行仍會留下清單,也絕不會留下寫了一半的檔案,所以被取消的工作仍可由下一次執行清理。

適用於 GitHub Actions 和 GitLab CI 的現成工作流程:如何在 CI 流程中產生測試檔案。

有桌面視窗嗎?

有,它就是在同一個引擎上加了一個視窗,用於不走腳本的測試。它不是縮水版:有測試逐項比對這兩種介面,只有其中一方能做的事必須被宣告並說明理由,而不是悄悄地漸行漸遠。

畫面有單批產生、預設集、同時多批與關於。它會在寫入任何內容之前顯示一次執行的開銷,執行時回報進度,並且可以在中途取消而不會留下寫了一半的檔案。它目前還不能開啟配方檔,配方暫時只屬於命令列,視窗透過表單來建立批次。