我在之前的文章中介绍过SQLite,这是个嵌入式数据库,也是世界上最流行的数据库。

打开网易新闻 查看精彩图片

(详情参见这篇文章:)

有一天我突发奇想:既然是嵌入式数据库,那应该不复杂,能不能用AI写一个?

说干就干,我把SQLite的架构文档发给了AI,让它根据现有架构来实现。

你别说,AI实现得相当不错,把Tokenizer,Parser,Code Generator,Bytecode Engine这些核心的概念都考虑到了,代码写得也不错,着实是让我吃惊。

打开网易新闻 查看精彩图片

但是,当我看到SQLite的测试的时候,我就知道,现在的AI,想写出SQLite级别的代码,恐怕是做梦。

因为SQLite简直不是一个软件,而是个怪物

截至 3.42.0 版本(2023-05-16),SQLite 库本身大约包含15.58万行C代码。

而测试代码和测试脚本脚本加起来一共有9205万行,是核心代码的 590 倍!

不是5倍,50倍,而是590倍

测试的目标不但要覆盖“功能”,还要覆盖分支,覆盖异常,覆盖现实世界的灾难。

SQLite设立了4套“互不信任”的测试体系,避免同一思路的盲区。

0 1

“保命”测试

首先,它用TCL写了一套“日常保命测试”,测试脚本有1390个文件,一共23.2M,包含了51,445 个不同的测试用例,参数化执行,会跑出数百万测试。

开发者日常主要依赖一个非常快的、几分钟就能跑完的子集,用于每次提交前自己检查。

0 2

“彻底”测试

SQLite有一套私有的C语言测试系统(名称为TH3),为 SQLite 核心库提供 100% 分支覆盖率。

这个数字是非常恐怖的,大家都知道覆盖所有分支很麻烦,例如这行代码:

}

想要达到100%分支覆盖,至少需要3个测试用例

(1) a<=b

(2) a>b 且 c==25

(3) a>b 且 c!=25

除了分支覆盖,SQLite还是是实现了100% MC/DC 覆盖率。

MC/DC(Modified Condition/Decision Coverage)是航空、核工业级别的测试标准。也就是说SQLite 把一个嵌入式数据库,按飞控软件的标准来测。

TH3有个非常重要的特点,就是不靠“后门”,只是用SQLite公开的API接口,完全模拟用户的行为。

TH3 包含大约 76.9MB、100万行C 代码,实现了 50,362 个不同测试用例。

在发布之前的一个长时间压力测试中,会执行2.485亿次测试。

0 3

对标测试

这是一个裁判性测试,它将大量 SQL 语句同时运行在 SQLite 和其他4个数据库引擎(PostgreSQL、MySQL、SQL Server、Oracle 10g )上,并比较结果是否一致。

打开网易新闻 查看精彩图片

换句话说,SQLite 不只要自洽,还要和“行业共识”对齐。

这个测试共运行 720 万条查询,测试数据总量 1.12GB。

0 4

模糊测试

模糊测试通过向SQLite提供无效、意外或者随机数据来测试它的健壮性。

例如这样的SQL语言:

  CAST('999999999999999999999999' AS INTEGER) < 0;

它完全符合SQL语法,但是几乎不可能是人写的,因为它精准命中了“超大内存申请”,“整数溢出”,“负索引”等极端情况。

更残酷的是,模糊测试还会破坏数据库文件(.db),对它做随机位翻转、截断、重复、拼接。

例如,这是原始的数据文件(简化):

[Header][Page1][Page2][Page3]

模糊测试工具把它改成:

[Header][Page1][Page1][乱码][Page3][Page3]

然后尝试去打开它:

sqlite3_open("mutated.db", &db);

SQLite要做到不崩溃,不越界,不死循环,要么报错,要么优雅失败。

最后,模糊测试还会开关所有的宏定义,对不同的编译器设置不同的编译优化级别,让同一条SQL在不同的情况下疯狂运行,疯狂地鞭打SQLite,折磨SQLite,直到它漏出所有的破绽。

由于SQLite在嵌入式领域非常流行,手机、路由器、车载、工业设备等在大量使用它,所以它特别注重对异常情况的处理。

内存不足测试

在服务器和工作站上,内存分配malloc()几乎不会失败,在嵌入式设备上,OOM就太常见了,SQLite必须得能优雅的处理OOM错误。

SQLite的测试框架(自己写的)可以插入自定义的可控的malloc函数,设置成在调用一定次数后内存分配失败,这样就可以在循环中执行SQLite操作,确保能处理OOM错误。

I/O错误测试

磁盘空间不足、磁盘硬件故障、网络中断、SQL操作过程中发生的系统配置或权限更改,或其他硬件或操作系统故障......无论原因如何,SQLite 必须能够正确响应这些错误。

测试的方法和OOM测试类似,都是模拟I/O错误,然后检查SQLite能否正确响应

崩溃测试

应用系统崩溃,操作系统崩溃,数据库更新过程中断电,SQLite都不能发生故障。

这也是SQLite“原子提交”能被信任的原因。

复合失效测试

先发生I/O错误,然后出现OOM,再搞一个崩溃..... 保证程序还能正常运行。

SQLite的这些测试,它的数量、完善程度和复杂程度,看起来真是让人脊背发凉。

这得付出多年的努力,经历多少事故才能写出来啊!

被这样测试保护的软件,代码质量可以说是固若金汤了。

AI能写出这样高质量的SQLite吗?

写个玩具版是可以的,但是想要复刻是万万不行的。

SQLite这种“高质量”不是体现在代码本身,而是体现在几十年积累的测试体系、失败经验和工程纪律上。

AI 可以帮助我们写实现、补测试、找 bug,但无法凭空生成那些来自真实事故、长期演化出来的异常场景和验证策略。

换句话说,AI 能参与写 SQLite,但还不能“独立孕育”一个 SQLite。

SQLite只是一个嵌入式数据库,就已经恐怖如斯了,那些更加复杂的数据库会怎么样呢?

欢迎了解详情的朋友在评论区留言!