1 Yocto 项目测试环境手册
1 欢迎
欢迎阅读 Yocto 项目测试环境手册。本手册介绍项目用于确保每个主要版本和维护版本 按预期工作的测试环境。所有测试基础设施与流程都公开可见, 社区可以随时了解正在执行哪些测试、如何执行,以及测试和项目当前的状态。 其他组织也可以借助这些流程和环境,在项目核心提供的基础上, 建立适合自己的自动化生产测试环境。
本手册仍在持续完善,最初的内容来自 README 文件与核心工程师的记录:
yocto-autobuilder2: README.md 详细介绍了 Yocto 项目 Autobuilder 的配置方法。 yocto-autobuilder2 仓库 提供项目的 Buildbot 控制台界面插件,以及让 Buildbot 执行项目所需测试的配置。
yocto-autobuilder-helper: 该仓库及其 README 包含 Autobuilder 辅助脚本和配置。 yocto-autobuilder-helper 定义了测试哪些内容以及如何执行测试的协调逻辑。 任意 CI 系统都可以利用它获取正确的源码修订、配置构建与层、执行构建并收集结果。 代码独立于具体 CI 平台,因此既可用于 Buildbot, 也可用于 Jenkins 等系统。 仓库为每个项目发布版本维护相应分支,分别定义该版本所需的测试。
1 Yocto 项目 Autobuilder 概览
Yocto 项目 Autobuilder 是项目用来定期、自动化地在支持的硬件上测试软件的 程序、工具、脚本与流程的总称。在版本开发过程中,它负责验证各项功能是否正常, 构建所有测试目标并运行相应测试。
项目使用标准上游 Buildbot 驱动集成和测试, 通过 yocto-autobuilder2 提供的控制台插件定制界面,使构建结果能够按项目需要展示。
yocto-autobuilder-helper 中的辅助脚本提供配置和任务管理功能。
这一 helper 层包含大部分构建配置,并按发布版本维护,
因此很容易为不同项目定制。它不依赖具体 CI 系统,
可用简单的 JSON 文件生成构建配置。
备注
项目使用 Buildbot 既有历史原因,也因为很多开发者熟悉 Python。 外层调度完全可以改用 Jenkins 等其他 CI 系统。
下图展示了由一个控制节点与一组工作节点组成的 Autobuilder:
1 Yocto 项目测试类型概览
Autobuilder 使用以下测试验证项目的不同部分:
构建测试: 改变 MACHINE、DISTRO、其他配置选项, 以及要构建的具体镜像或
world,检查这些配置能否构建。 测试覆盖不同架构、机器、发行版和初始化系统等大量组合。 Autobuilder 实际会测试数百种配置与目标。构建过程中的完整性检查: 由 insane 类发起, 用于验证构建输出是否正确。例如,生成二进制文件的 ELF 架构应与目标系统匹配, ARM 二进制文件无法在 MIPS 系统中工作。
构建性能测试: 验证常见构建步骤的效率,并避免性能回退。
oe-build-perf-test对常见场景计时。 测试运行在隔离机器上,避免其他进程干扰测量。 项目目前同时在 Fedora 和 Ubuntu 上测试, 既避免单点依赖,也确认不同发行版都能高效工作。eSDK 测试: 通过以下命令发起:
$ bitbake image -c testsdkext
使用 testsdk 类与
do_testsdkext任务。功能测试: 通过 OpenEmbedded 自测(oe-selftest) 运行各种场景测试。项目会在每个主要受支持发行版上执行 oe-selftest。
镜像测试: 通过以下命令发起:
$ bitbake image -c testimage
使用 testimage 类与 do_testimage 任务。 详情见 执行自动化运行时测试。
层测试: Autobuilder 能测试特定层是否与系统其余部分正常协作。 项目成员可以选择待测层,一些重要社区层也会定期测试。
软件包测试: ptest 在目标机器上测试 OpenEmbedded 构建系统生成的软件包。 详情见 使用 ptest 测试软件包 和 Ptest Wiki。
SDK 测试: 通过以下命令发起:
$ bitbake image -c testsdk
使用 testsdk 类与
do_testsdk任务。单元测试: 通过 bitbake-selftest 和 oe-selftest 对系统中的不同组件执行单元测试。
自动升级助手: 检查软件是否有新版本,以及能否自动升级。 如果可以,它会向维护者发送包含补丁的邮件,提示可进行升级。
1 测试与代码位置的对应关系
各类测试在代码库中的位置如下:
bitbake-selftest:
这些测试自成一体,验证 BitBake 及其 API,包括 fetcher。 测试位于
bitbake/lib/*/tests。部分测试会运行
bitbake,因此应先把bitbake/bin加入PATH。 在 BitBake 仓库中运行:$ export PATH=$PWD/bin:$PATH
然后运行自测脚本:
$ bitbake-selftest
默认只输出执行摘要。需要更多信息时,可使用详细输出选项:
$ bitbake-selftest -v
要跳过访问互联网的测试,在执行
bitbake-selftest时设置BB_SKIP_NETTESTS:$ BB_SKIP_NETTESTS=yes bitbake-selftest
需要网络访问的测试主要用于验证 fetcher 模块。 若只想运行某个测试模块,在命令后加模块名即可。 例如,运行
bb.tests.data.DataExpansions:$ bitbake-selftest bb.tests.data.DataExpansions
也可以通过完整的模块、类和测试名称运行单个测试,例如:
$ bitbake-selftest bb.tests.data.DataExpansions.test_one_var
这些测试基于 Python unittest。
oe-selftest:
使用 OE 验证工作流,包括特定功能、任务行为和 API 单元测试。
可通过
-j指定线程数并行执行。 同一个测试类中的测试总是在同一线程运行; 要提高大量测试的并行度,可以把一个大测试类拆成多个单元。测试基于 Python unittest。
测试代码位于
meta/lib/oeqa/selftest/cases/。运行全部测试:
$ oe-selftest -a
运行特定测试时使用以下形式,其中
testname为具体测试名称:$ oe-selftest -r <testname>
例如,运行
tinfoil的getVarAPI 测试:$ oe-selftest -r tinfoil.TinfoilTests.test_getvar
也可以运行一组测试,例如全部
tinfoil测试:$ oe-selftest -r tinfoil
testimage:
构建并启动镜像,然后针对镜像内容运行测试。
测试代码位于
meta/lib/oeqa/runtime/cases/。需要设置 IMAGE_CLASSES:
IMAGE_CLASSES += "testimage"
使用以下命令运行测试:
$ bitbake image -c testimage
testsdk:
构建并安装 SDK,再测试该 SDK。
测试代码位于
meta/lib/oeqa/sdk/cases/。使用以下命令运行:
$ bitbake image -c testsdk
testsdk_ext:
构建并安装可扩展 SDK(eSDK),再对其执行测试。
测试代码位于
meta/lib/oeqa/sdkext/cases/。使用以下命令运行:
$ bitbake image -c testsdkext
oe-build-perf-test:
运行常见使用场景,并测量操作耗时。
代码位于
meta/lib/oeqa/buildperf。使用以下命令运行:
$ oe-build-perf-test <options>
命令支持结果保存位置等多项选项。 Autobuilder 辅助脚本中的
build-perf-test-wrapper提供了从命令行调用 oe-build-perf-test 的示例。可用
oe-git-archive将测试结果存入 Git 仓库。使用
oe-build-perf-report可生成文本报告, 以及带性能图表的 HTML 报告。示例见 HTML 和 文本。测试用例位于
meta/lib/oeqa/buildperf/test_basic.py。
1 测试示例
本节为 测试与代码位置的对应关系 中的各类测试提供示例。
oe-selftest用例位于meta/lib/oeqa/selftest/cases。bitbake-selftest用例位于bitbake/lib/bb/tests/。
1.5.1 bitbake-selftest
bitbake/lib/bb/tests/data.py 中的一个简单示例如下:
class DataExpansions(unittest.TestCase):
def setUp(self):
self.d = bb.data.init()
self.d["foo"] = "value_of_foo"
self.d["bar"] = "value_of_bar"
self.d["value_of_foo"] = "value_of_'value_of_foo'"
def test_one_var(self):
val = self.d.expand("${foo}")
self.assertEqual(str(val), "value_of_foo")
此例创建了基于标准
Python unittest 的
DataExpansions 测试类。类中的公共 setUp 方法为所有测试准备环境,
随后用一个简单测试确认变量展开能够得到正确值。
BitBake 自测就是常规的 Python unittest 测试。 更多编写方法见 Python unittest 文档。
1.5.2 oe-selftest
这类测试背后需要准备完整构建环境,因此更复杂。 它们包装了大多数标准对象,而不是直接使用 Python unittest。 测试本身也可以很简单,例如在 OE 构建环境中执行一个命令:
class BitbakeLayers(OESelftestTestCase):
def test_bitbakelayers_showcrossdepends(self):
result = runCmd('bitbake-layers show-cross-depends')
self.assertTrue('aspell' in result.output, msg = "No dependencies were shown. bitbake-layers show-cross-depends output: %s"% result.output)
这个例子来自 meta/lib/oeqa/selftest/cases/bblayers.py。
测试类基于由 unittest.TestCase 派生的 OESelftestTestCase,
执行 bitbake-layers 并检查输出中是否包含预期内容。
oeqa.utils.commands 提供了处理常见任务的辅助函数:
读取 BitBake 变量: 使用
oeqa.utils.commands.get_bb_var(); 读取多个变量时使用oeqa.utils.commands.get_bb_vars()。调用 BitBake 构建: 使用
oeqa.utils.commands.bitbake()。运行命令: 使用
oeqa.utils.commands.runCmd()。
oeqa.utils.commands.runqemu() 可启动 runqemu,
以便在运行中的虚拟化镜像内测试。
这些测试可以按测试类并行运行。同一类中的测试应始终使用同一次构建,
不同类或模块则可能分配到不同构建中。
测试通过启动 bitbake 命令工作,位于其上下文之外,
因此没有可直接使用的数据存储,也不能直接使用通常的 BitBake 库函数 bb.\*。
1.5.3 testimage
这类测试在镜像已经启动后运行,目标可以是真实硬件,也可以是 QEMU。
测试假定自身面对目标镜像环境,而不是主机构建环境。
meta/lib/oeqa/runtime/cases/python.py 中有如下简单示例:
class PythonTest(OERuntimeTestCase):
@OETestDepends(['ssh.SSHTest.test_ssh'])
@OEHasPackage(['python3-core'])
def test_python3(self):
cmd = "python3 -c \\"import codecs; print(codecs.encode('Uryyb, jbeyq', 'rot13'))\""
status, output = self.target.run(cmd)
msg = 'Exit status was not 0. Output: %s' % output
self.assertEqual(status, 0, msg=msg)
这里 OERuntimeTestCase 包装了 unittest.TestCase。
self.target 表示目标系统,测试可以通过它的 run() 方法执行命令。
使用 OETestDepends 和 OEHasPackage 装饰器,
可以确保所需测试依赖或软件包存在。
例如,上述测试只有在镜像安装了 python3-core 时才有意义。
1.5.4 testsdk_ext
这些测试针对已经构建的可扩展 SDK 运行,
可以假定 eSDK 环境已经完成初始化。
meta/lib/oeqa/sdk/cases/devtool.py 中有如下示例:
class DevtoolTest(OESDKExtTestCase):
@classmethod def setUpClass(cls):
myapp_src = os.path.join(cls.tc.esdk_files_dir, "myapp")
cls.myapp_dst = os.path.join(cls.tc.sdk_dir, "myapp")
shutil.copytree(myapp_src, cls.myapp_dst)
subprocess.check_output(['git', 'init', '.'], cwd=cls.myapp_dst)
subprocess.check_output(['git', 'add', '.'], cwd=cls.myapp_dst)
subprocess.check_output(['git', 'commit', '-m', "'test commit'"], cwd=cls.myapp_dst)
@classmethod
def tearDownClass(cls):
shutil.rmtree(cls.myapp_dst)
def _test_devtool_build(self, directory):
self._run('devtool add myapp %s' % directory)
try:
self._run('devtool build myapp')
finally:
self._run('devtool reset myapp')
def test_devtool_build_make(self):
self._test_devtool_build(self.myapp_dst)
此例验证在 eSDK 中,devtool build 是否能够构建一个示例应用。
1.5.5 testsdk
这些测试针对已经构建的 SDK 运行,
可以假定 SDK 已解压且环境脚本已载入。
meta/lib/oeqa/sdk/cases/python.py 中有如下简单示例:
class Python3Test(OESDKTestCase):
def setUp(self):
if not (self.tc.hasHostPackage("nativesdk-python3-core") or
self.tc.hasHostPackage("python3-core-native")):
raise unittest.SkipTest("No python3 package in the SDK")
def test_python3(self):
cmd = "python3 -c \\"import codecs; print(codecs.encode('Uryyb, jbeyq', 'rot13'))\""
output = self._run(cmd)
self.assertEqual(output, "Hello, world\n")
如果 SDK 中安装了 nativesdk-python3-core,
测试会调用 python3 解释器运行一条基本命令,检查其是否正常。
只有 SDK 安装了 Python3 时才运行该测试。
1.5.6 oe-build-perf-test
性能测试通常测量操作耗时及过程中的资源使用情况。
以下例子来自 meta/lib/oeqa/buildperf/test_basic.py:
class Test3(BuildPerfTestCase):
def test3(self):
"""Bitbake parsing (bitbake -p)"""
# Drop all caches and parse
self.rm_cache()
oe.path.remove(os.path.join(self.bb_vars['TMPDIR'], 'cache'), True)
self.measure_cmd_resources(['bitbake', '-p'], 'parse_1',
'bitbake -p (no caches)')
# Drop tmp/cache
oe.path.remove(os.path.join(self.bb_vars['TMPDIR'], 'cache'), True)
self.measure_cmd_resources(['bitbake', '-p'], 'parse_2',
'bitbake -p (no tmp/cache)')
# Parse with fully cached data
self.measure_cmd_resources(['bitbake', '-p'], 'parse_3',
'bitbake -p (cached)')
它在使用或不使用不同缓存的情况下测量三种解析耗时, 用来观察 BitBake 解析性能随时间的变化。
1 编写测试时的注意事项
Autobuilder 上的资源会被多个工作节点并发访问, 因此编写测试时应注意以下约束:
不允许运行 cleanall。
该操作可能删除 DL_DIR 中的文件,破坏其他并行构建。 如果必须执行,需将 DL_DIR 指向隔离目录。
不允许运行 cleansstate。
该操作可能删除 SSTATE_DIR 中的文件,破坏其他并行构建。
如果必须执行,需将 SSTATE_DIR 指向隔离目录。
也可以对 bitbake 使用 -f,通过改变任务的共享状态校验和将任务标记为需要重做,
从而阻止复用对应缓存。
测试不应修改元数据。
这一点对并行运行的 oe-selftest 尤其重要。 修改元数据会改变校验和,干扰同时运行的 BitBake。 确有需要时,应把层复制到临时位置再修改。 例如 devtool 测试确实需要变更元数据,就必须先准备临时副本以保护原数据。