ESP-IDF 的目标端单元测试可以直接运行在 ESP32 上。测试代码与组件放在一起,通过 Unity 断言检查结果,再由 ESP-IDF 注册和调度测试。对于自己的业务工程,一个实用组织方式是保留正常应用,同时另建只运行测试的应用:组件实现共享,入口和构建目标分开。
本文以 Espressif Systems 的 ESP-IDF v6.1《ESP32 中的单元测试》为主线,结合该版本的 system/unit_test 官方示例整理。原文未见个人署名。已核对指南全文、示例 README、组件实现、头文件、测试文件、两个入口及 CMake 配置;本文没有安装 SDK、连接硬件、编译或执行任何测试。
指南还介绍 Linux 主机测试,其中硬件依赖可以由 Mock 替代,但 v6.1 文档明确说明支持范围有限,并非所有 ESP-IDF 组件都能在主机上模拟。本文先把目标端自定义组件测试讲完整,再说明多设备、多阶段和 Mock 等扩展边界。

一个组件,两种应用入口
官方示例的顶层 unit_test 是正常应用,unit_test/test 是独立测试应用。组件自己的 test 目录又是另一层含义:它存放组件测试用例。不要把这两个同名目录混淆。
unit_test/
├── CMakeLists.txt
├── main/
│ ├── CMakeLists.txt
│ └── example_unit_test_main.c
├── components/
│ └── testable/
│ ├── CMakeLists.txt
│ ├── mean.c
│ ├── include/testable.h
│ └── test/
│ ├── CMakeLists.txt
│ └── test_mean.c
└── test/
├── CMakeLists.txt
├── sdkconfig.defaults
└── main/
├── CMakeLists.txt
└── example_unit_test_test.c
正常应用调用组件 API,打印随机数与均值;测试应用负责注册、执行和汇总测试。普通应用构建不包含组件测试,测试工程用 TEST_COMPONENTS 指定要带入测试的组件。
README 仍出现 Makefile 和 component.mk,这是历史叙述。本文锁定 v6.1 的实际 CMake 文件,不再把旧 Make 构建说明当作当前操作步骤。普通应用源码注释中引用的旧测试入口名也与当前文件名不一致,以上目录使用已读取的真实文件名。
实现与头文件:先明确被测函数的行为
组件 include/testable.h 声明整数均值函数:
#pragma once
int testable_mean(const int* values, int count);
该版本 mean.c 的实现如下。它是官方用于演示测试的原始代码,保留了后面故意暴露的问题:
#include "testable.h"
int testable_mean(const int* values, int count)
{
if (count == 0) {
return 0;
}
int sum = 0;
for (int i = 0; i < count; ++i) {
sum += values[i];
}
return sum / count;
}
count == 0 返回 0;正常情况下使用整数除法计算均值。这里的 int sum 可能在累加时溢出。有符号整数溢出在 C 中属于未定义行为,不能把某个平台上恰好出现的回绕值视为语言保证。原函数还没有验证负长度、空指针与实际数组长度的关系;调用者必须满足接口前提,或在产品代码中另行定义并实现错误处理。
在 ESP32 的 32 位 int、正的 int count 且数组确有相应长度这一边界下,可以考虑使用 int64_t 累加来避免这个示例中的 32 位加法溢出,但这仍不能代替指针和长度契约检查。本文没有把这种修改悄悄写回“原代码”,也没有提供未经编译的修复通过结论。
组件的构建定义很小:
# components/testable/CMakeLists.txt
idf_component_register(SRCS "mean.c"
INCLUDE_DIRS "include")
编写和注册三个测试
测试写在组件的 test 子目录,文件名以 test 开头。一个 C 文件可放多个用例,包含 unity.h 和被测模块头文件后,用 TEST_CASE 注册。第一个参数是可读名称,第二个参数是方括号标签;标签可组合,用来按模块或属性分组。
#include <limits.h>
#include "unity.h"
#include "testable.h"
#define countof(x) (sizeof(x) / sizeof(x[0]))
TEST_CASE("Mean of an empty array is zero", "[mean]")
{
const int values[] = { 0 };
TEST_ASSERT_EQUAL(0, testable_mean(values, 0));
}
TEST_CASE("Mean of a test vector", "[mean]")
{
const int v[] = {1, 3, 5, 7, 9};
TEST_ASSERT_EQUAL(5, testable_mean(v, countof(v)));
}
TEST_CASE("Another test case which fails", "[mean][fails]")
{
const int v1[] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX};
TEST_ASSERT_EQUAL(INT_MAX, testable_mean(v1, countof(v1)));
}
前两个用例分别检查空输入约定与正常均值。第三个用例在数学上应得到 INT_MAX,但原实现先用 int 相加,暴露了累加溢出。[fails] 只是开发者加的标签,测试运行器不会因为这个名字而自动跳过、忽略或把失败变成成功。
countof 是该示例对局部数组使用的宏,不应拿它计算指针指向多少个元素。真实代码中的数组边界需要独立保存。本文保留原始用例以说明测试目的,没有实际执行这个溢出示例。
每个测试用例内部不需要重复添加 UNITY_BEGIN() 和 UNITY_END();指南说明平台测试调度层会处理测试执行边界。独立测试应用则可以在一组手动调度调用外包一对宏,以输出这组测试的统计。
让测试工程找到组件和测试依赖
指南给出的最小测试组件写法是:
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES unity)
常规组件通常建议明确列出源文件,而组件测试可以使用 SRC_DIRS。当前官方示例的 components/testable/test/CMakeLists.txt 实际写法是:
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES cmock testable)
这不是漏掉 Unity 后仍然神奇地可用:本次已核对 v6.1 的 components/cmock/CMakeLists.txt,其中有 REQUIRES unity。示例通过 cmock 带入 Unity,并明确依赖被测的 testable。自己写测试时,应根据实际使用的组件声明依赖,不要从示例推断任何测试都必须使用 Mock。
测试应用入口又有自己的依赖:
# unit_test/test/main/CMakeLists.txt
idf_component_register(SRCS "example_unit_test_test.c"
PRIV_REQUIRES unity
INCLUDE_DIRS ".")
独立测试工程的顶层配置将正常应用的组件目录加入搜索范围,并指定待测试组件:
# unit_test/test/CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
set(EXTRA_COMPONENT_DIRS "../components")
set(TEST_COMPONENTS "testable" CACHE STRING "List of components to test")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
idf_build_set_property(MINIMAL_BUILD ON)
project(unit_test_test)
EXTRA_COMPONENT_DIRS 指向共享的组件实现;TEST_COMPONENTS 选择测试对象;MINIMAL_BUILD 让构建包含最小组件集合、main 及其依赖。正常应用的工程名为 unit_test,同样启用最小构建,但不设置上述测试组件选择。正常入口的 main/CMakeLists.txt 使用 PRIV_REQUIRES testable。
按名称、标签、排除标签或菜单运行
官方测试入口展示四种调度方式,实际工程通常只需选择其中一种。下面省略只负责打印分隔标题的 print_banner,保留原始调度顺序:
#include "unity.h"
void app_main(void)
{
UNITY_BEGIN();
unity_run_test_by_name("Mean of an empty array is zero");
UNITY_END();
UNITY_BEGIN();
unity_run_tests_by_tag("[mean]", false);
UNITY_END();
UNITY_BEGIN();
unity_run_tests_by_tag("[fails]", true);
UNITY_END();
UNITY_BEGIN();
unity_run_all_tests();
UNITY_END();
unity_run_menu();
}
unity_run_tests_by_tag("[mean]", false) 选择带该标签的用例;第二个参数为 true 时,选择不带指定标签的用例。所以 unity_run_tests_by_tag("[fails]", true) 是显式排除故意失败用例的步骤。
原始入口对 unity_run_menu() 有重要注释:它不会返回,并且忙等 UART 输入,使用时要关闭任务看门狗。已核对测试工程的 sdkconfig.defaults,其中是 CONFIG_ESP_TASK_WDT_EN=n。该设置只针对这个交互测试应用,不应作为正常产品固件的默认可靠性策略。
串口菜单可按以下方式选择测试,输入后按 Enter;示例 README 提醒菜单不会回显输入字符:
| 输入 | 选择范围 |
|---|---|
1 等序号 |
菜单中对应的单个测试。 |
"Mean of an empty array is zero" |
指定名称的测试,保留引号。 |
[mean] |
带 mean 标签的所有测试。 |
![fails] |
排除 fails 标签。 |
* |
运行所有已注册测试,包括未被排除的故意失败用例。 |
构建位置和输出应该怎样理解
示例不要求额外外设,一块 ESP32 开发板和串口即可进行这些均值测试。先按 v6.1 的安装说明准备 SDK,确保 IDF_PATH 指向对应版本,再进入官方示例目录下的 unit_test/test。以下命令是待操作者在自己的测试设备上执行的步骤:
# 当前目录必须是 examples/system/unit_test/test
idf.py menuconfig
idf.py build
idf.py -p PORT flash monitor
把 PORT 替换为已经确认的开发板串口。flash 会写入目标设备固件,应先确认串口对应测试板,不能误用生产设备。退出串口监视器使用 Ctrl-]。如果在 unit_test 顶层执行烧录,得到的是正常应用,而不是测试入口。
指南还介绍 ESP-IDF 自身的中央测试应用:中文 v6.1 页指定 tools/unit-test-app 目录,在那里执行 idf.py menuconfig 与 idf.py build,再按构建提示烧录。这是 SDK 组件测试的入口,不应与本文的自定义组件测试子工程混为一谈。
官方 README 的示例输出中,单独运行空数组用例显示 1 Tests 0 Failures;选择 [mean] 或运行全部用例时包含故意失败测试,示例显示 3 Tests 1 Failures;排除 [fails] 后是 2 Tests 0 Failures。这些数字来自原文说明,不是本次执行结果。
README 的溢出示例打印了“Expected 2147483647 Was -1”,但 C 的有符号溢出结果没有可移植保证。判断教学例子时,应看到预期失败揭示的实现缺陷,不能将它写成环境安装失败,也不能宣称整个样例“全部通过”。README 中菜单文字和行号还带有历史痕迹,实际名称以当前 test_mean.c 与板端菜单为准。
多设备与多阶段测试
均值函数不需要额外设备;GPIO、SPI 等通信测试可能需要多块 DUT(被测设备)。指南使用 TEST_CASE_MULTIPLE_DEVICES 将最多 5 个测试入口注册为一个多设备用例:
TEST_CASE_MULTIPLE_DEVICES(
"gpio multiple devices test example",
"[driver]",
gpio_master_test,
gpio_slave_test);
原文例子中,gpio_master_test 配置输入端,调用 unity_wait_for_signal("output high level") 等待,再断言读取到高电平;gpio_slave_test 配置输出端、设置高电平,再调用 unity_send_signal("output high level")。终端同步需要人工交互:看到输出板发送信号后,在等待板终端按 Enter,等待调用才解除阻塞。
编者校核:以示例代码为准,解除等待后执行的是读取和断言,不是再次改变 GPIO 电平。中文原页这一段的主从叙述容易混淆。原文还用 1 << GPIO_PIN 构造位掩码;对较大 GPIO 编号,必须核对移位类型和合法范围,不能机械使用默认 int。适配时可考虑 64 位无符号掩码并检查引脚范围、配置返回值及板间连线。这里不提供脱离实际板卡定义的可烧录 GPIO 工程,也未进行引脚操作。
如果测试需要跨越深度睡眠等复位阶段,可使用多阶段用例。原文的核心例子如下,须放在具备相应 ESP-IDF 头文件与目标支持的测试环境中:
static void trigger_deepsleep(void)
{
esp_sleep_enable_timer_wakeup(2000);
esp_deep_sleep_start();
}
void check_deepsleep_reset_reason(void)
{
soc_reset_reason_t reason = esp_rom_get_reset_reason(0);
TEST_ASSERT(reason == RESET_REASON_CORE_DEEP_SLEEP);
}
TEST_CASE_MULTIPLE_STAGES(
"reset reason check for deepsleep",
"[esp32]",
trigger_deepsleep,
check_deepsleep_reset_reason);
首次选择阶段 1 触发深度睡眠,设备恢复运行后重新选择同一用例,再选择阶段 2 检查复位原因。只有最后阶段通过、此前阶段都按预期触发复位,才算整个用例通过。多设备与多阶段宏会分别自动添加 [multi_device]、[multi_stage] 标签,菜单会显示对应子菜单;这些宏生成的功能标签与人为命名的 [fails] 不应混为一谈。
让测试适配不同芯片
硬件能力不同的芯片不一定运行相同用例。指南建议用 DISABLED_FOR_TARGETS 或 TEMPORARY_DISABLED_FOR_TARGETS 表达永久与临时禁用,并优先依据 soc_caps.h 中的能力宏决定哪些功能可测。测试文件仍应进入编译流程,而不是被目录规则悄悄排除。
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32, ESP8266)
TEST_CASE("a test that is not ready for esp32 and esp8266 yet", "[]")
{
/* 原文用于展示条件编译的空示例,不是有效业务测试。 */
}
#endif
若芯片根本没有所需外设,使用永久禁用;若只是测试尚未调通、缺少 runner 等暂时原因,使用临时禁用,后续恢复。能力判断与临时禁用可以组合。原文能力宏示例中的 ESP64 是示意标记,不能据此认定它是本文可用的实际开发目标。
不要再通过 test/esp32 一类目录加 CMake 选择来表达测试项目的禁用,也不要用 CONFIG_IDF_TARGET_xxx 白名单把测试项目限定死。否则引入新芯片时,测试可能被静默跳过。
“测试项目是否存在”和“同一测试如何实现”是两件事。例如不带 SD Host 的目标无需运行 SD 1-bit、SD 4-bit 用例,但仍可有 SDSPI 用例;而读取某项寄存器时,各芯片可采用不同实现。后者允许用目标宏选择实现。指南建议避免兜底 #else,让尚未适配的新芯片在构建阶段暴露问题,提醒维护者明确选择实现。
从手动菜单到自动化测试
反复在串口输入菜单适合初步探索。需要连续运行多个用例或接入 CI 时,可以继续使用官方推荐的 pytest-embedded 与 ESP-IDF pytest 工作流。指南链接的 ESP Test Template 展示应用组织和仿真、真实硬件的 CI 方向;更复杂的工程可以拆成多个 test-app,再用 IDF Build Apps 发现并构建它们。
这些是主指南提供的后续入口。本文未复制它们的 CI 配置,也未验证这些仓库在本机或特定硬件上的运行,不能把引用它们当成完成了自动化测试环境。
微基准中的缓存补偿计时
从 SPI flash、SPI RAM 等外部存储器取得指令或数据,需要经过缓存。命中时快,未命中时处理器可能等待外部取数。不同构建中的代码与数据布局会改变命中情况,因此微基准在不同运行或不同构建之间可能出现时间差。
一种办法是将被测代码与数据放入 IRAM、DRAM,减少缓存因素,但可用容量有限。指南提供缓存补偿定时器作为另一种选择:利用处理器内部事件计数器估计因缓存缺失而等待的时间,再从记录的墙钟时间中减去。
ccomp_timer_start();
func_code_to_time();
int64_t t = ccomp_timer_stop(); /* 返回相对开始时刻的微秒数 */
这是 API 使用片段,func_code_to_time 代表待测函数。关键限制是把执行该测量的任务固定在同一个 CPU 核上:各核事件计数器独立,如果在一核开始、经调度后到另一核停止,测量就无效。补偿后的时间也不能不加说明地当作用户实际感受到的端到端延迟;本文没有产生任何性能数字。
用 CMock 隔离硬件依赖
上层组件依赖大量底层代码或硬件状态时,边界条件难以重现,失败原因也难定位。Mock 让测试用软件替代依赖,并明确控制交互。ESP-IDF 集成 CMock,通过构建函数为整个组件或部分函数生成模拟实现。
理想情况下可以替换被测组件的所有依赖,但若需要模拟的调用太多,也可保留更多真实代码,或重新拆分组件、减少依赖。保留真实代码会增加耦合,测试失败时也更难判断来自被测逻辑还是底层实现;改进组件设计通常能同时改善测试和后续维护。
v6.1 主指南的这部分仍局限于受支持的 Linux 目标和部分可模拟组件。需准备 ESP-IDF 及其依赖、足够新的 Linux 或 macOS 与 GCC、libbsd/libbsd-dev 等系统要求,应用依赖必须受 Linux/POSIX 目标支持或可模拟。同版本英文页还明确要求主机安装 Ruby 以生成 CMock;中文页未列出,本稿据英文页补充。
Linux 目标应用可在根 CMakeLists.txt 中设置 set(COMPONENTS main),避免为了方便而自动带入 ESP-IDF 的所有组件。已有组件模拟可以按支持情况直接使用;没有时,需要创建与原组件同名的模拟组件,并让构建系统在原组件之后发现它,从而覆盖原实现。
模拟组件要指定生成 Mock 所需的头文件、包含路径,以及头文件依赖的其他组件。指南给出的接口形式如下;其中尖括号名称及头文件名是待替换的示意内容,不是可直接构建的完整配置:
idf_component_get_property(
original_component_dir
<original-component-name>
COMPONENT_OVERRIDEN_DIR
)
idf_component_mock(
INCLUDE_DIRS "${original_component_dir}/include"
REQUIRES freertos
MOCK_HEADER_FILES
${original_component_dir}/include/header_containing_functions_to_mock.h
)
组件模拟另需一个 mock 目录,在 mock_config.yaml 中配置 CMock,例如:
:cmock:
:plugins:
- expect
- expect_any_args
并非每次都要模拟整个组件;只要满足测试和其他引用方所需接口,部分 Mock 也成立。官方 tools/mocks 中很多模拟就是这种方式。要让测试工程使用模拟,可把它放在项目的 components 目录,或在工程根配置中追加实际模拟目录:
list(APPEND EXTRA_COMPONENT_DIRS "<mock_component_dir>")
同名组件覆盖会改变实际链接的实现,因此要确认 Mock 只进入测试工程,不能因为共享路径配置而混入生产构建。本文未运行 Ruby、Mock 生成器或任何主机测试。
静态审查中还发现了什么
除了 testable_mean 的有符号累加溢出、输入契约缺失和 GPIO 位移范围,普通应用 example_unit_test_main.c 还有两个不应照搬到产品代码的细节:calloc 返回值未检查就被解引用;它打印“1 到 100”,但实际表达式 1 + esp_random() % (max - 1) 在 max = 100 时只生成 1 到 99。取模也不应被当作无偏随机分布的通用实现。这些都是对已读源码的静态分析,没有执行应用。
所核对的源码与命令片段未发现硬编码秘密或动态命令拼接。真正会改变环境的操作包括构建产物写入、设备烧录、GPIO 输出和深度睡眠;本文只展示必要步骤与边界,全部未执行。静态审查不能证明代码没有其他漏洞,也不能替代编译器检查、硬件验证和测试运行。
来源与许可证
主要来源:ESP-IDF v6.1 中文单元测试指南、同版本英文页和 v6.1 system/unit_test 示例。原作者未单独署名,维护及发布方为 Espressif Systems。全文读取和代码核对日期为 2026-10-05。
ESP-IDF 的代码总体许可与各文件头可能不同;本文下方保留仓库默认 Apache License 2.0 许可原文。本稿引用的示例 C 文件及头文件明确写为公共领域,或由使用者选择 CC0。原始声明全文保留在下方。Unity、CMock 属于各自的 MIT 许可组件,本文只调用其接口,不复制其库实现。指南 HTML 主题的 MIT 许可不被当作文章正文的转载许可,详见官方版权页。
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
本文为经核查的中文整理稿,保留技术内容并按自定义组件工程重组顺序;编者修正和未实测边界已就近标明。原创配图可随本文使用。
原始版权与许可
指南原始版权:© 版权所有 2016 – 2026 乐鑫信息科技(上海)股份有限公司。官方版权说明所列源代码总体版权为2015–2023乐鑫科技,具体文件头优先。本例C源码公共领域/CC0声明已在上文完整保留;以下为仓库默认Apache2条款,不覆盖改写其文件级例外。
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.












暂无评论内容