第一章:Go测试工程化的重要意义
在现代软件开发中,测试不再是开发完成后的附加环节,而是贯穿整个研发流程的核心实践。Go语言以其简洁的语法和强大的标准库,为测试工程化提供了天然支持。将测试纳入工程化体系,不仅能提升代码质量,还能显著增强团队协作效率与系统可维护性。
测试驱动开发提升代码质量
Go语言内置 testing 包,支持单元测试、基准测试和覆盖率分析,使开发者能够快速编写可执行的测试用例。通过测试先行的方式,开发者在实现功能前先定义行为预期,从而确保代码符合设计规范。
自动化集成保障持续交付
将测试嵌入CI/CD流水线,是实现持续集成的关键步骤。例如,使用GitHub Actions执行以下流程:
# .github/workflows/test.yml
name: Run Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.21'
- name: Run tests
run: go test -v ./... # 执行所有测试,-v 参数输出详细日志
该配置在每次代码推送时自动运行测试,确保新提交不破坏现有功能。
测试工程化带来的核心收益
| 优势 | 说明 |
|---|---|
| 故障前置 | 在开发阶段暴露问题,降低修复成本 |
| 文档价值 | 测试用例可作为API的行为文档 |
| 重构保障 | 提供安全网,支持代码重构与优化 |
通过将测试作为工程实践的核心组成部分,团队能够构建更可靠、更易维护的Go应用系统。
第二章:go test -c 基本原理与工作机制
2.1 理解 go test 的默认行为与测试生命周期
Go 的 go test 命令在无额外参数时会自动识别当前目录下以 _test.go 结尾的文件,仅运行测试函数。其默认行为包括:编译测试文件、执行测试主函数、输出结果并返回退出码。
测试函数的执行流程
每个测试函数以 Test 开头,接收 *testing.T 参数。Go 按字母顺序执行这些函数,确保可重复性。
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("期望 5,实际 %d", result)
}
}
上述代码定义了一个基础测试用例。t.Errorf 在断言失败时记录错误并标记测试为失败,但不会立即停止,允许后续逻辑继续执行。
测试生命周期阶段
使用 testing 包可管理测试前后的资源:
TestMain可自定义测试入口,控制 setup/teardown;t.Cleanup()注册清理函数,按后进先出顺序执行。
| 阶段 | 执行内容 |
|---|---|
| 初始化 | 加载测试包,解析标志 |
| Setup | 执行 TestMain 或 init 函数 |
| 运行测试 | 依次执行 TestXxx 函数 |
| 清理 | 触发 t.Cleanup 注册的函数 |
执行流程示意
graph TD
A[执行 go test] --> B[发现 *_test.go 文件]
B --> C[编译测试包]
C --> D[运行 init 函数]
D --> E[调用 TestMain 或直接执行测试]
E --> F[按序运行 TestXxx]
F --> G[执行 Cleanup 函数]
G --> H[输出结果并退出]
2.2 go test -c 的作用:从测试到可执行文件的转变
在Go语言的测试体系中,go test -c 是一个常被低估但极具实用价值的命令。它允许开发者将测试代码编译为独立的可执行二进制文件,而无需立即运行。
编译测试为可执行文件
使用以下命令:
go test -c -o myapp.test
该命令会将当前包的测试代码编译成名为 myapp.test 的可执行文件。参数说明:
-c:指示go test仅编译不运行;-o:指定输出文件名,便于后续手动执行或分发。
生成的二进制文件包含了所有测试逻辑,可在不同环境中运行,适用于CI/CD流水线中的隔离测试阶段。
典型应用场景
- 离线部署测试:在网络受限环境运行预编译测试;
- 性能分析:结合
pprof对测试二进制进行深度剖析; - 权限控制:在安全沙箱中运行经审计的测试程序。
工作流程示意
graph TD
A[源码 *_test.go] --> B(go test -c)
B --> C[可执行文件]
C --> D[本地运行]
C --> E[远程部署]
这一机制强化了测试的可移植性与复用性,使测试从开发辅助转变为可交付产物。
2.3 编译生成的测试二进制文件结构解析
在C++单元测试中,编译器将测试源码与测试框架(如Google Test)链接后生成可执行的测试二进制文件。该文件不仅包含用户编写的测试逻辑,还嵌入了测试运行时环境所需的核心组件。
核心组成部分
- 测试用例元数据:记录每个TEST宏注册的测试名称、所属套件
- 初始化与清理逻辑:全局SetUp/TearDown、断言处理器
- 主入口函数main:默认或自定义的执行起点,启动测试运行器
典型链接结构
g++ -o test_example \
example_test.cpp \
-lgtest -lgtest_main -lpthread
需显式链接gtest和pthread库,确保测试框架功能完整。
gtest_main提供默认main实现,自动执行所有注册测试。
符号表布局(部分)
| 符号类型 | 示例 | 说明 |
|---|---|---|
| 函数符号 | TEST_F(MathTest, Addition) |
编码后的测试函数 |
| 全局对象 | testing::internal::GetTestSuiteRegistry() |
测试套件注册器单例 |
加载执行流程
graph TD
A[操作系统加载ELF] --> B[运行时初始化]
B --> C[全局构造函数注册测试]
C --> D[gtest_main调用RUN_ALL_TESTS]
D --> E[遍历执行测试用例]
2.4 对比直接运行与编译后运行的差异
执行方式的本质区别
直接运行脚本(如 Python)依赖解释器逐行翻译,而编译后运行(如 C++)需先将源码转换为机器码。前者启动快但执行效率低,后者编译耗时但运行速度快。
性能对比示例
以计算斐波那契数列为例:
# 直接运行(Python)
def fib(n):
return n if n <= 1 else fib(n-1) + fib(n-2)
print(fib(35)) # 输出结果较慢
解释型语言每次执行都需要解析语法树,函数调用开销大,适合开发调试。
// 编译后运行(C++)
#include <iostream>
int fib(int n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}
int main() {
std::cout << fib(35) << std::endl; // 响应迅速
return 0;
}
编译型语言生成优化后的二进制文件,CPU 直接执行指令,性能显著提升。
运行机制对比表
| 维度 | 直接运行 | 编译后运行 |
|---|---|---|
| 启动速度 | 快 | 较慢(含编译) |
| 执行效率 | 低 | 高 |
| 跨平台性 | 依赖解释器 | 依赖目标平台 |
| 调试便利性 | 高 | 中等 |
执行流程差异可视化
graph TD
A[源代码] --> B{执行方式}
B --> C[直接运行: 解释器逐行解析]
B --> D[编译运行: 源码→机器码→执行]
C --> E[运行时动态翻译]
D --> F[静态生成可执行文件]
2.5 实践:使用 go test -c 生成并手动执行测试二进制
Go 提供了 go test -c 命令,用于将测试代码编译为独立的可执行二进制文件,而无需立即运行。这一特性在 CI/CD 环境或需要复用测试程序时尤为有用。
生成测试二进制
go test -c -o math_test binary
该命令会基于当前包中的 _test.go 文件生成名为 math_test binary 的二进制文件。参数说明:
-c:仅编译测试,不执行;-o:指定输出文件名。
手动执行测试
生成后可直接运行:
./math_test binary
支持传递测试过滤器:
./math_test binary -test.run=TestAdd -test.v
其中 -test.run 指定要运行的测试函数,-test.v 启用详细输出。
典型应用场景对比
| 场景 | 是否适用 go test -c |
说明 |
|---|---|---|
| 本地快速验证 | 否 | 直接 go test 更高效 |
| 容器内分阶段测试 | 是 | 编译与执行分离,利于镜像分层 |
| 分布式环境批量部署 | 是 | 一次编译,多节点运行 |
工作流程示意
graph TD
A[编写 *_test.go] --> B[go test -c]
B --> C{生成测试二进制}
C --> D[传输至目标环境]
D --> E[手动执行并收集结果]
这种方式增强了测试的灵活性和可移植性。
第三章:测试二进制在CI/CD中的典型应用场景
3.1 将测试二进制集成到流水线中的优势分析
将测试二进制文件提前构建并集成到CI/CD流水线中,可显著提升反馈速度与发布可靠性。传统模式下,测试代码随每次流水线触发重新编译,造成资源浪费和延迟。
快速反馈机制
通过独立构建测试二进制包,并在流水线中直接拉取执行,避免重复编译。例如:
# 构建测试二进制
go test -c -o ./bin/integration.test ./test/integration
该命令将测试套件编译为独立可执行文件 integration.test,可在任意环境运行,无需Go工具链支持。
环境一致性保障
使用统一的测试二进制确保开发、预发、生产环境验证逻辑一致,减少“在我机器上能跑”的问题。
| 优势维度 | 传统方式 | 集成二进制方式 |
|---|---|---|
| 执行效率 | 每次需编译 | 直接运行,秒级启动 |
| 资源消耗 | 高(CPU/内存) | 低 |
| 版本可追溯性 | 弱 | 强(二进制带版本标签) |
流水线优化示意
graph TD
A[提交代码] --> B{是否首次构建?}
B -->|是| C[编译测试二进制并缓存]
B -->|否| D[拉取缓存二进制]
D --> E[执行测试]
C --> E
该模式实现编译与执行解耦,提升整体流水线稳定性与可观测性。
3.2 实践:在Docker镜像中分阶段构建与运行测试
在现代CI/CD流程中,使用Docker多阶段构建可有效分离编译与运行环境,同时嵌入测试环节保障代码质量。
构建与测试分离策略
通过多阶段构建,可在中间阶段执行单元测试,仅当测试通过后才生成最终镜像:
# 阶段1:构建与测试
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go test -v ./... # 执行全部单元测试,失败则中断构建
# 阶段2:生产镜像
FROM golang:1.21-alpine AS production
COPY --from=builder /app/bin/server /usr/local/bin/
CMD ["server"]
上述go test命令会在构建时运行所有测试用例,确保只有通过验证的代码才能进入下一阶段。--from=builder精确指定来源阶段,避免携带测试依赖。
阶段流转控制
| 阶段 | 用途 | 输出产物 |
|---|---|---|
| builder | 编译与测试 | 可执行文件、测试报告 |
| production | 运行服务 | 轻量级运行镜像 |
构建流程可视化
graph TD
A[开始构建] --> B[第一阶段: 编译并运行测试]
B --> C{测试是否通过?}
C -->|是| D[第二阶段: 构建运行镜像]
C -->|否| E[终止构建, 返回错误]
D --> F[输出最终镜像]
3.3 跨平台测试二进制的生成与验证策略
在持续集成流程中,跨平台测试二进制的生成是保障软件兼容性的关键环节。通过统一构建脚本,可针对不同目标平台(如 Linux、Windows、macOS)交叉编译出对应的测试可执行文件。
构建流程自动化
#!/bin/bash
# build_test_bin.sh - 生成多平台测试二进制
GOOS=linux GOARCH=amd64 go build -o bin/test_linux ./cmd/tester
GOOS=windows GOARCH=amd64 go build -o bin/test_windows.exe ./cmd/tester
GOOS=darwin GOARCH=amd64 go build -o bin/test_darwin ./cmd/tester
上述脚本利用 Go 语言的交叉编译能力,通过设置 GOOS 和 GOARCH 环境变量生成对应平台的二进制文件,确保测试程序能在目标环境中运行。
验证策略设计
为确保生成的二进制有效性,需实施分层验证:
- 校验文件是否存在及权限是否正确
- 执行版本输出命令(如
--version)确认基础可运行性 - 在目标平台或模拟器中启动轻量级冒烟测试
| 平台 | 二进制名称 | 验证方式 |
|---|---|---|
| Linux | test_linux | 容器内执行健康检查 |
| Windows | test_windows.exe | CI 中启动 PowerShell 测试 |
| macOS | test_darwin | 使用 qemu 模拟验证 |
可信发布流程
graph TD
A[源码提交] --> B[触发CI流水线]
B --> C[生成多平台测试二进制]
C --> D[签名并上传制品库]
D --> E[各平台执行验证测试]
E --> F[全部通过则标记为可信版本]
第四章:高级技巧与性能优化建议
4.1 使用自定义标志与配置提升测试灵活性
在复杂系统测试中,硬编码配置难以适应多环境、多场景需求。通过引入自定义标志(flags)与外部化配置,可显著增强测试的可定制性与复用性。
配置驱动的测试行为控制
使用命令行标志动态调整测试参数,例如:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--env', choices=['dev', 'staging', 'prod'], default='dev')
parser.add_argument('--slow-test', action='store_true') # 是否运行耗时测试
args = parser.parse_args()
# 根据标志决定是否执行性能敏感测试
if not args.slow_test:
skip_performance_tests()
上述代码通过 --env 指定测试目标环境,--slow-test 控制测试范围,实现按需执行。
多维度配置管理策略
| 配置项 | 用途 | 来源 |
|---|---|---|
--browser |
指定UI测试浏览器类型 | 命令行标志 |
config.json |
存储数据库连接信息 | 外部JSON文件 |
| 环境变量 | 注入密钥或CI/CD上下文 | 运行时环境 |
动态测试流程调度
graph TD
A[启动测试] --> B{解析自定义标志}
B --> C[加载对应环境配置]
C --> D{是否启用集成测试?}
D -->|是| E[执行全量测试套件]
D -->|否| F[仅运行单元测试]
该机制支持在不同部署阶段灵活切换测试深度,提升反馈效率。
4.2 静态链接与依赖管理对部署的影响
静态链接在编译时将所有依赖库直接嵌入可执行文件,生成的二进制文件不依赖外部库环境。这种方式显著提升了部署的可移植性,尤其适用于目标系统无法保证依赖版本一致的场景。
部署优势与权衡
-
优点:
- 无需在目标机器安装额外库
- 避免“依赖地狱”(Dependency Hell)
- 启动更快,无动态查找开销
-
缺点:
- 可执行文件体积增大
- 更新库需重新编译整个程序
- 内存无法共享相同库实例
链接方式对比
| 类型 | 文件大小 | 启动速度 | 维护成本 | 依赖管理 |
|---|---|---|---|---|
| 静态链接 | 大 | 快 | 高 | 简单 |
| 动态链接 | 小 | 较慢 | 低 | 复杂 |
// 示例:使用静态链接编译程序
gcc -static main.c -o server
上述命令强制将
libc等基础库静态打包进server。虽然提升部署稳定性,但输出文件可能从几MB增至数十MB,适合容器镜像或嵌入式系统等对运行时环境控制较弱的场景。
构建流程影响
graph TD
A[源代码] --> B(编译为目标文件)
C[静态库.a] --> D[链接器]
B --> D
D --> E[单一可执行文件]
E --> F[部署到任意Linux系统]
静态链接将多模块整合为独立单元,极大简化CI/CD流程中的部署环节,尤其适配不可变基础设施理念。
4.3 减小测试二进制体积的编译优化方案
在持续集成环境中,测试二进制文件的体积直接影响构建速度与资源消耗。通过编译器优化手段可显著减小其大小。
启用链接时优化(LTO)
gcc -flto -O2 test.c -o test_binary
-flto 启用链接时优化,使编译器在链接阶段重新分析和优化整个程序代码,消除未使用的静态函数与冗余指令,通常可减少15%-20%的二进制体积。
剥离调试符号
使用 strip 工具移除不必要的调试信息:
strip --strip-unneeded test_binary
该命令移除所有非必需符号表项,可进一步压缩体积达30%以上,适用于无需现场调试的CI环境。
优化策略对比
| 优化方式 | 体积缩减率 | 是否影响调试 |
|---|---|---|
| LTO | ~20% | 轻微影响 |
| Strip符号 | ~30% | 完全不可调试 |
| 静态库裁剪 | ~15% | 无影响 |
流程整合
graph TD
A[源码编译] --> B{启用LTO?}
B -->|是| C[生成中间优化对象]
B -->|否| D[普通对象]
C --> E[全局优化链接]
D --> E
E --> F[生成二进制]
F --> G[执行strip]
G --> H[最终测试二进制]
4.4 并行生成与缓存机制加速大规模项目测试
在大规模项目中,测试执行效率直接影响开发迭代速度。通过并行生成测试用例与引入结果缓存机制,可显著缩短整体测试周期。
并行测试用例生成
利用多核资源并行执行测试任务,大幅提升吞吐量。例如,在 Python 中使用 pytest-xdist 插件实现分布式运行:
pytest -n auto --dist=loadfile
该命令根据文件粒度自动分配测试用例到多个进程,-n auto 表示使用 CPU 核心数作为进程数,有效避免资源争抢。
缓存机制优化重复执行
将已执行且结果稳定的测试用例输出缓存至本地或共享存储,下次运行时比对输入依赖,若未变更则直接复用结果。
| 缓存键 | 内容 | 有效期 |
|---|---|---|
| 源码哈希 | 文件内容的 SHA-256 | 变更时失效 |
| 依赖版本 | 第三方库列表 | 升级时失效 |
| 环境标识 | OS/Python 版本 | 环境变化失效 |
执行流程优化
graph TD
A[开始测试] --> B{是否首次运行?}
B -->|是| C[执行并缓存结果]
B -->|否| D[计算输入指纹]
D --> E{缓存命中?}
E -->|是| F[复用结果]
E -->|否| C
C --> G[输出测试报告]
F --> G
在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 在 復 復 復 復 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 微 미 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 다 피 다 다 다 피 다 다 피 다 피 다 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피 피피피파피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피피
