#csv #compare #diff #difference #image #config-file #json-text

bin+lib havocompare

一个灵活的基于规则的文件和文件夹比较工具和 crate,包括美观的 HTML 报告。比较 CSV、JSON、文本文件、pdf-texts 和图像。

28 个版本

0.6.1 2024 年 6 月 5 日
0.5.4 2024 年 3 月 18 日
0.5.2 2023 年 12 月 15 日
0.5.1 2023 年 11 月 12 日
0.1.4-RC22022 年 11 月 25 日

#60文件系统

Download history 184/week @ 2024-05-01 90/week @ 2024-05-08 111/week @ 2024-05-15 157/week @ 2024-05-22 219/week @ 2024-05-29 416/week @ 2024-06-05 148/week @ 2024-06-12 143/week @ 2024-06-19 200/week @ 2024-06-26 39/week @ 2024-07-03 17/week @ 2024-07-10 51/week @ 2024-07-17 42/week @ 2024-07-24 199/week @ 2024-07-31 217/week @ 2024-08-07 251/week @ 2024-08-14

714 每月下载量

MIT 许可证

205KB
5.5K SLoC

Havocompare - 文件夹比较工具

Crates.io Documentation CI Coverage Status License

贡献者

Contributors

快速入门

0. 安装 havocompare

你有 rust 吗?太棒了!尝试: cargo install havocompare

你只想获取二进制文件:请检查我们的 GitHub 页面的二进制下载

1. 创建配置文件

Havocompare 是基于几个设计目标开发的。我们希望有一个易于阅读和组合的配置文件格式。经过几次尝试,我们最终得到了当前的格式,即 yaml 文件中的一系列规则。请看以下示例 config.yaml

rules:
  - name: "Numerical results csv"
    # you can have multiple includes and excludes
    pattern_include:
      - "**/export_*.csv"
    # excludes are optional
    pattern_exclude:
      - "**/export_1337.csv"
    CSV:
      comparison_modes:
        - Relative: 0.1
        - Absolute: 1.0

它创建一个名为 rule 的新规则,包括所有匹配 "export_*.csv" 的所有子文件夹中的文件,但排除 " export_1337.csv"。字符串单元格将被检查为完全相同,数字(包括带单位的数字)将被检查为相对偏差小于 0.1 AND 绝对偏差小于 1.0

比较规则

  • 相对意味着有效性检查如下: |nominal - actual| / |nominal| < 容差
  • 绝对意味着有效性检查如下: |nominal - actual| < 容差
  • "nan" 和 "nan" 相等
  • 00 名义值的差对于任何相对差异都是有效的

2. 运行比较

运行比较非常简单,只需提供标称值、实际值和配置文件:./havocompare compare nominal_dir actual_dir config.yaml 比较报告将写入 ./report 文件夹内。差异也会打印到终端。此外,如果发现差异,返回码将是 1,如果没有发现差异,将是 0,这使得将 havocompare 集成到 CI 系统变得非常容易。

关于配置的详细信息

验证方案

在不使用自动完成的情况下编写有效的配置文件可能会出错。我们建议使用 json schema 来验证您的 yaml,甚至可以在 pycharm 等 IDE 中启用自动完成。要生成 schema,可以调用:./havocompare schema > config_scheme.json 并将生成的 schema 导入您的 IDE。

比较选项

CSV

比较模式 comparison_modes 是必需的,类型为 'list'。它可以包含相对数值 ('Relative') 最大偏差或最大偏差 ('Absolute')。您可以指定小数分隔符和字段分隔符。如果没有指定,havocompare 将尝试从每个 csv 文件中猜测它。注意:如果分隔符未指定,即使标称值和实际值之间的分隔符不同,只要所有偏差都在范围内,也接受不同的分隔符。要忽略特定单元格,可以指定一个排除的正则表达式。

预处理步骤是在使用给定的分隔符(或猜测)解析文件之后以及执行其他任何操作之前完成的。处理顺序按列表中编写。在下面的示例中,将从 csv 输入文件中提取标题,然后删除标题为 "Column to delete" 的列。如果任何预处理步骤失败,havocompare 将立即退出并显示错误,因此请谨慎使用。

以下是一个设置所有可选参数的示例

rules:
  - name: "CSV - Demo all options"
    # what files to include - use as many as make sense to reduce duplication in your rules
    pattern_include:
      - "**/*.csv"
    # optional: of all included files, remove the ones matching any exclude pattern
    pattern_exclude:
      - "**/ignored.csv"
    CSV:
      # delimiters are optional, if not given, they will be auto-detected.
      # auto-detection allows different delimiters for nominal and actual
      decimal_separator: '.'
      field_delimiter: ';'
      # can have Absolute or Relative or both
      comparison_modes:
        - Absolute: 1.0
        - Relative: 0.1
      # optional: exclude fields matching the regex from comparison
      exclude_field_regex: "Excluded"
      # optional: preprocessing of the csv files
      preprocessing:
        # extracts the headers to the header-fields, makes reports more legible and allows for further processing "ByName".
        # While it may fail, there's no penalty for it, as long as you don't rely on it.
        - ExtractHeaders
        # Sort the table by column 0, beware that the column must only contain numbers / quantities
        - SortByColumnNumber: 0
        # Delete a column by name, needs `ExtractHeaders` first - delete sets all values to 'DELETED'
        - DeleteColumnByName: "Vertex_Position_Y"
        - DeleteColumnByNumber: 1
        # Sorts are stable, so a second sort will keep the first sort as sub-order.
        - SortByColumnName: "Vertex_Position_X"
        # Deletes the first row by setting all values to 'DELETED' - meaning that numbering stays constant 
        - DeleteRowByNumber: 0
        # Deletes rows having any element matching the given regex (may delete different lines in nom / act)!
        - DeleteRowByRegex: "Vertex_Count"
        # Deletes the cell (column, row) by setting the value to 'DELETED'
        - DeleteCellByNumber:
            column: 0
            row: 0
        # Deletes the cell (column name, row) by setting the value to 'DELETED'. This needs `ExtractHeaders`
        - DeleteCellByName:
            column: "Column to delete"
            row: 0

图像比较

图像比较使用 image compare crate 完成。在这里指定许多选项,然后根据阈值进行过滤。

rules:
  - name: "JPG comparison"
    pattern_include:
      - "**/*.jpg"
    # exclude can of course also be specified!
    Image:
      # Compare images in RGBA-mode, can also be RGB and Gray
      # Comparison mode set to Hybrid means we want MSSIM on the Y channel and 2 dim vec diff on UV for color information
      RGBA: Hybrid
      threshold: 0.9

纯文本比较

对于纯文本比较,文件将逐行读取和比较。对于每一行,使用 strsim crate 的归一化 Damerau-Levenshtein 距离。您可以指定任意数量的忽略行,以忽略已知不同的行。

rules:
  - name: "HTML-Compare strict"
    pattern_exclude:
      - "**/*_changed.html"
    pattern_include:
      - "**/*.html"
    PlainText:
      # Normalized Damerau-Levenshtein distance
      threshold: 1.0
      # All lines matching any regex below will be ignored
      ignore_lines:
        - "stylesheet"
        - "next_ignore"
        - "[A-Z]*[0-9]"

PDF 文本比较

对于 PDF 文本比较,将提取文本并写入临时文件。然后使用纯文本比较比较这些文件。

rules:
  - name: "PDF-Text-Compare"
    pattern_exclude:
      - "**/*_changed.pdf"
    pattern_include:
      - "**/*.pdf"
    PDFText:
      # Normalized Damerau-Levenshtein distance
      threshold: 1.0
      # All lines matching any regex below will be ignored
      ignore_lines:
        - "stylesheet"
        - "next_ignore"
        - "[A-Z]*[0-9]"

哈希比较

对于无法以其他方式检查的二进制文件,我们还可以执行简单的哈希比较。目前,我们仅支持 SHA-256,但可以轻松添加更多检查。

rules:
  - name: "Hash comparison strict"
    pattern_exclude:
      - "**/*.bin"
    Hash:
      # Currently we only have Sha256
      function: Sha256

文件元数据比较

对于仅存在或某些元数据已经足够的情况。

rules:
  - name: "Metadata comparison"
    pattern_exclude:
      - "**/*.bin"
    FileProperties:
      # nom/act file paths must not contain whitespace
      forbid_name_regex: "[\\s]"
      # files must have their modification timestamp within 3600 seconds
      modification_date_tolerance_secs: 3600
      # files sizes must be within 1 kb 
      file_size_tolerance_bytes: 1024

运行外部比较工具

如果您想运行外部比较工具,可以使用此选项

rules:
  - name: "External checker"
    pattern_include:
      - "*.pdf"
    External:
      # this config will call `/usr/bin/pdf-diff --only-images nominal.pdf actual.pdf`
      # return code will decide on comparison result
      executable: "/usr/bin/pdf-diff"
      # optional: add as many extra params as 
      extra_params:
        - "--only-images"

JSON 比较器

比较 JSON 文件中的不同键和值中的不匹配。 ignore_keys 是一个正则表达式列表,它将匹配单个键名称,如果正则表达式匹配,则排除键值对。值不受此影响。

rules:
  - name: "Compare JSON files"
    pattern_include:
      - "**/*.json"
    Json:
      ignore_keys:
        # drop "ignore_this_key" and "ignore_this_keys" with this regex :)
        - "ignore_this_key(s?)"

在单元测试中使用 HavoCompare

  1. 将 havocompare 添加到您的 dev-dependencies
    [dev-dependencies]
    havocompare = "0.5"
    
  2. 在单元测试中使用它,例如
    #[test]
    // works in 0.5 series
    fn integ_test_dirs() {
      let result_dir = process_whatever_test_data();
      // just write the usual yaml file
      let result = havocompare::compare_folders("../tests/data/nominal/integ_test/case", &result_dir, "../tests/data/config.yaml", "../tests/out_report").unwrap;
      assert!(result);
    }
    #[test]
    // works starting with 0.5.3 only
    fn integ_test_file() {
      let result_file = process_generate_image();
      // see docs for all options
      let compare_mode = ComparisonMode::Image(ImageCompareConfig{threshold: 0.97});
      let result = havocompare::compare_files("../tests/data/nominal.png", &result_file, &compare_mode).unwrap;
      assert!(result);
    }
    

变更日志

0.6.1

  • 添加 json-diff 的新版本,具有更好的 API 和更好的过滤选项

0.6.0

  • 为图像比较模块添加新选项(很多选项!)
  • 将 json-compare 升级到新版本,修复正则表达式字段排除和排序的错误

0.5.4

  • 添加从 CLI 运行单文件模式的选项

0.5.3

  • 添加排序JSON数组的功能(包括深度排序)
  • 将单个文件比较函数改为公开API
  • 更新依赖项,修复与空格相关的损坏PDF导入

0.5.2

  • 在CSV和PDF报告中保留空格,而不是由HTML折叠
  • 在报告索引中添加加号和减号符号
  • 如果比较的文件名不同,则在报告索引中显示组合文件名

0.5.1

  • 修复JSON检查中的潜在崩溃

0.5.0

  • 添加JSON检查

0.4.0

  • 将报告逻辑与比较逻辑分开
  • 实现机器可读的JSON报告

0.3.2

  • 在比较后通过--open直接打开报告
  • 运行compare时的解析失败现在会传播到终端

0.3.1

  • 修复哈希和文本比较中的实际和名义值交换

0.3.0

  • 允许RGBA图像比较
  • 添加文件元数据比较
  • 添加外部检查
  • 为文件元数据比较和外部检查添加和调整报告
  • 添加CSV标题比较。之前,报告仅标记差异但不返回任何错误。
  • 添加配置yaml验证命令,用于检查文件是否可以加载

0.2.4

  • 检查两个比较的CSV文件的行数,如果不相等则抛出错误
  • 添加按单元格删除
  • 简化报告子文件夹创建:子文件夹现在在临时文件夹中临时创建,而不是在当前工作文件夹中
  • 将报告行编号更改为始终从0开始,以便行删除更容易理解
  • 修复不可显示的diff值的浮点值比较

0.2.3

  • 将pdf-extract crate升级到0.6.4以修复"尝试离开未初始化的类型linked_hash_map::Node<alloc::vec::Vec<u8>, object::Object>"

0.2.2

  • 将包含错误且无法进行比较的文件包含在报告中
  • 修复了一个导致程序提前退出规则循环的bug,并且没有处理所有

0.2.0

  • 删除列将不再真正删除它们,而是将每个值替换为"已删除"
  • 将配置结构公开到库API
  • 修复了关于多个空行处理不当的bug
  • 重构CSV报告,使其具有交错和更紧凑的视图
    • 在报告索引.html中显示比较文件的相对路径而不是文件名
    • 使标题提取成为可失败的但不重要的操作 - 现在始终可以启用
  • 编写了一个全新的csv解析器
    • 尊重转义
    • 允许包含未转义的字段分隔符的字符串字面量(field1, "field2, but as literal", field3)
    • 允许包含引号的多行字符串字面量
  • 现在将非矩形格式的CSV失败

0.1.4

  • 添加多个包含和排除项 - 警告,这将破坏0.1.3及更早版本的rules-files
  • 在库代码中移除所有unwrapexpect,以换取正确的错误传播
  • 为CSV文件添加预处理选项
  • 精炼了readme.md
  • 修复报告生成中的唯一键创建
  • 添加PDF-Text比较

0.1.3:

  • 添加可选的cli参数以配置存储报告的文件夹

0.1.2:

  • 添加SHA-256比较模式
  • 修复Windows上的CSV比较BOM

0.1.1:

  • 在找不到文件夹时提供更好的错误消息
  • 更好的测试覆盖率
  • 修复Windows终端上的颜色
  • 扩展CI到Windows和mac

依赖项

~35–48MB
~712K SLoC