大家好,本文介绍了3D引擎的测试方法,搭建了本地的测试环境。

上一篇博文

从0开发3D引擎(三):搭建开发环境

下一篇博文

从0开发3D引擎(五):函数式编程及其在引擎中的应用

了解自动化测试

对于引擎开发这种复杂、长期的项目,为了减少bug,提升长期的开发效率,自动化测试必不可少。在我们的Wonder.js引擎中,包括了本节介绍的3种自动化测试,测试覆盖率达到了95%。

本系列为了节省篇幅,不进行自动化测试。因此本节只进行简单的介绍,不给出实际的案例,读者可以到Wonder.js->test/目录下查看自动化测试实例。

单元测试

我们需要写测试用例对单个函数进行单元测试。

搭建环境

使用jest作为测试框架,sinon进行stub。

如果读者想了解stub的概念,可以参考我对Stub和Mock的理解

因为不能直接使用js库,需要写对应的FFI(类似于typescript的d.ts文件)才能在Reason中被调用,所以我们可以使用bs-jest和Wonder的Wonder-bs-sinon作为FFI

集成测试

相对于单元测试,集成测试的测试目标变为某个特性,该特性跨越多个函数或多个模块。

搭建环境

与单元测试的环境一样。

目录结构

可以在test/unit/目录下写单元测试用例,而在test/integration/目录下写集成测试用例。

端对端测试

也称为e2e测试,包括了“渲染测试”和“性能测试”。它们都需要安装puppeteer,通过chrome内核渲染3D场景来进行测试。

  • 渲染测试

渲染测试是针对特定的场景(如只有一个模型的场景,或者只有一个光源的场景)进行测试,从而保证渲染的正确性。

测试步骤为:

1、预先渲染一张正确图片

2、使用引擎渲染一张当前图片

3、逐个像素地比较两者,如果95%以上的像素都相同,则测试通过;否则测试失败

  • 性能测试

性能测试是针对极端场景(如5000个box)进行测试,从而保证花费的时间占用的内存大小符合要求。

测试步骤为:

1、预先准备基准数据

使用引擎运行场景多次,取平均值,记录到json文件中

2、使用引擎运行场景多次(少于“准备基准数据”的运行次数),取平均值,得到当前数据

3、比较两者的花费的时间占用的内存大小,如果在误差范围内,则测试通过;否则测试失败

注意事项:

只有在本地测试时,保持基准数据不变。如果在云端(如在push到Github仓库时使用CI工具-travis进行测试)或者其它环境(如换一台电脑进行测试)进行性能测试,需要在每次测试时更新基准数据(因为不同的环境,性能不一样,所以对应的基准数据也不一样)。

通过打印日志来调试

有以下的原因使得可以在单元测试和集成测试中通过打印日志来进行调试:

  • 因为在自动化测试中打开watch,代码修改后能够立即刷新,所以能够即时看到打印的结果,测试很方便
  • 因为在函数式编程中,函数为纯函数(Reason也允许非纯),没有状态,所以我们可以通过打印函数的输入和输出,来验证该函数是否正确

了解运行测试

本系列通过在Chrome浏览器中进行运行测试来验证程序的正确性。

通过以下的方式进行运行测试:

断点调试

因为浏览器运行的是Reason编译后的js代码,所以我们可以在浏览器的控制台->Sources中通过断点来调试js代码

具体可以参考:

使用断点暂停代码

通过Spector.js测试WebGL

Spector.js调试预览:

Spector.js能查看一帧中WebGL的调用情况、shader代码和WebGL的状态,它支持WebGL 1.0或WebGL 2.0,也支持WebGL 1.0的VAO等扩展。

另外,Spector.js支持多个canvas,能查看指定的canvas对应的WebGL信息。这对于调试编辑器(如我们的Wonder-Editor在线编辑器)很有用。因为编辑器有多个canvas(如一个canvas进行主场景绘制,另一个canvas以材质球的方式显示单个material资产的效果),而我们希望分别调试从每个canvas中取得上下文的WebGL。

Spector.js可以在Chrome的扩展中安装,详情请见官方Github

通过log调试Shader

本系列通过在fragment shader中,将变量作为输出的颜色,来调试Shader的变量值。

更多可以参考:

调试OpenGL -> 调试着色器输出

OpenGL shader如何调试?

OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式

移动端测试

我们通过下面两种方法进行测试:

  • 模拟测试

我们可以在Chrome浏览器上,点击控制台->Toggle Device Toolbar,打开用于模拟移动设备视口的界面。

本系列主要用该方法测试引擎对于移动端touch事件的支持。

详细的介绍参考:

使用 Chrome DevTools 中的 Device Mode 模拟移动设备

  • 真机测试

具体步骤如下:

1、在测试html页面中引入vConsole库

从而可以通过打印日志的方式,在手机上查看错误和日志信息

vConsole介绍参考:

前端开发 - 在手机上调试利器vConsole

2、把测试页面push到测试环境的服务器上(如使用Github Pages搭建的服务器)

3、把测试页面的访问地址转换为二维码

如使用草料二维码在线转换

4、用测试手机的微信扫该二维码,运行测试页面,验证渲染结果,查看错误和日志信息

了解性能测试

因为本系列开发的引擎重视性能,所以会通过手动的性能测试,来指导引擎优化。

性能测试的指标包括时间开销和内存开销,下面分别分析:

测试时间开销

  • 使用Chrome DevTools的Javascript Profiler

通过在测试页面记录profile,查看每个函数的时间开销,从而定位到热点函数进行优化。

相关资料可参考:

加速执行 JavaScript

Chrome DevTools 之 Profiles,深度性能优化必备

  • 使用Chrome DevTools的Performance

通过时间线Timeline,可以查看CPU端各个线程和GPU的执行顺序和热点函数的时间开销。

本系列主要用该方法测试在多线程中,每个线程的执行顺序和性能开销

相关资料可参考:

如何使用 Timeline 工具

Chrome DevTools 之 Timeline,快捷性能优化工具

  • 使用Chrome DevTools的NetWork

查看各个资源的加载时间和顺序。

本系列用该方法测试在“使用函数式反应式编程的流来异步加载 js、二进制文件等资源”时,各个资源的加载顺序是否正确。

  • 使用Performance.now

使用该方法打印某段逻辑的时间开销,多用于自动化测试->端对端测试—>性能测试。

本系列在使用profile定位到热点函数后,会使用该方法确定具体代码的时间开销。通过比较优化前和优化后的时间开销,来评估优化的效果。

示例代码为:

var n1 = performance.now();

执行某些逻辑

var n2 = performance.now();

//打印逻辑的时间开销(ms为单位)
console.log(n2 - n1);
  • 使用Console.profile

相对于Chrome DevTools的Javascript Profiler,该方法可以测试某一段逻辑的profile,而不是整个页面的profile,粒度更小,更加可控。



(图来自Chrome 控制台console的用法(学了之后对于调试js可是大大有用的哦)

测试内存开销

  • 使用Chrome DevTools的Memory

本系列对这个工具的应用:

使用Allocation sampling,定位内存占用的热点函数;

使用Allocation instrumentation on timeline来比较每一帧内存开销的增长情况,从而确定是否有内存泄漏。如果有内存泄漏,则通过Heap snapshot,记录多个内存快照并进行比较,定位到具体是哪些地方增加了内存。

相关资料可参考:

解决内存问题

搭建本地测试环境

运行测试和性能测试不仅需要使用Chrome浏览器的控制台功能,还需要:

1、安装Spector.js

Spector.js可以在Chrome的扩展中安装

2、进行移动端测试时,需要在测试页面中引入vConsole库,并使用Github Pages搭建测试服务器

从0开发3D引擎(四):搭建测试环境的更多相关文章

  1. 从0开发3D引擎(三):搭建开发环境

    本系列使用Reason语言,因此需要搭建它的开发环境. 上一篇博文 从0开发3D引擎(二):准备预备知识 搭建开发环境 建议使用VSCode编辑器来开发Reason,因为它的插件支持得最好. 具体搭建 ...

  2. 从0开发3D引擎(八):准备“搭建引擎雏形”

    大家好,现在开始本系列的第三部分,按照以下几个步骤来搭建引擎雏形: 1.分析引擎的需求 2.实现最小的3D程序 3.从中提炼引擎原型 4.一步一步地对引擎进行改进,使其具备良好的架构 5.实现与架构相 ...

  3. 从0开发3D引擎(一):开篇

    介绍 大家好,本系列带你踏上Web 3D编程之旅- 本系列是实战类型,从0开始带领读者写出"良好架构.良好扩展性.最小功能集合(MVP)" 的3D引擎. 本系列的素材来自我们的产品 ...

  4. 从0开发3D引擎(五):函数式编程及其在引擎中的应用

    目录 上一篇博文 函数式编程的优点与缺点 优点 缺点 为什么使用Reason语言 函数式编程学习资料 引擎中相关的函数式编程知识点 数据 不可变数据 可变数据 函数 纯函数 高阶函数 柯西化 参考资料 ...

  5. 从0开发3D引擎:目录

    介绍 大家好,本系列带你踏上Web 3D编程之旅- 本系列是实战类型,从0开始带领读者写出"良好架构.良好扩展性.优秀的性能.最小功能集合(MVP)" 的3D引擎. 本系列的素材来 ...

  6. 从0开发3D引擎(九):实现最小的3D程序-“绘制三角形”

    目录 上一篇博文 运行测试截图 需求分析 目标 特性 头脑风暴 确定需求 总体设计 具体实现 新建Engine3D项目 实现上下文 实现_init 实现"获得WebGL上下文" 实 ...

  7. 用函数式编程,从0开发3D引擎和编辑器(一)

    介绍 大家好,欢迎你踏上3D编程之旅- 本系列的素材来自我们的产品:Wonder-WebGL 3D引擎和编辑器 的整个开发过程,探讨了在从0开始构建3D引擎和编辑器的过程中,每一个重要的功能点.设计方 ...

  8. 从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(上)

    目录 上一篇博文 下一篇博文 前置知识 回顾上文 最小3D程序完整代码地址 通用语言 将会在本文解决的不足之处 本文流程 解释本文使用的领域驱动设计的一些概念 本文的领域驱动设计选型 设计 引擎名 识 ...

  9. 从0开发3D引擎(补充):介绍领域驱动设计

    我们使用领域驱动设计(英文缩写为DDD)的方法来设计引擎,在引擎开发的过程中,领域模型会不断地演化. 本文介绍本系列使用的领域驱动设计思想的相关概念和知识点,给出了相关的资料. 上一篇博文 从0开发3 ...

随机推荐

  1. behavior planning——13. implement a cost function in C++

    In the previous quizzes, you designed a cost function to choose a lane when trying to reach a goal i ...

  2. oracle自动选择索引

    如果表中有两个以上(包括两个)索引,其中有一个唯一性索引,而其他是非唯一性. 在这种情况下,ORACLE将使用唯一性索引而完全忽略非唯一性索引. 举例: SELECT ENAME FROM EMP W ...

  3. jqLite

    一.关于DOM导航的jqLite方法 children() 返回一组子元素.这个方法的jqLite实现不支持jQuery所提供的选择器特性 eq(index) 从一个元素集合中返回指定索引下的元素 f ...

  4. 只要是使用函数file_get_contents访问 https 的网站都要开启

    使用file_get_contents();报错failed to open stream: Unable to find the socket transport "ssl" - ...

  5. SQLSTATE[HY000] [2002] 错误

    http://www.thinkphp.cn/topic/36194.html 使用tp框架 3.2.3 ,在windows上跑的时候没有任何问题,但是部署到linux系统和是哪个,就会报这个错,不知 ...

  6. THINKPHP框架的优秀开源系统推荐

    THINKPHP框架的优秀开源系统推荐 众所周知,国内众多优秀的开源框架,ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性.并且拥有众多的原创功能 ...

  7. Ultra-QuickSort——[归并排序、分治求逆序对]

    Description In this problem, you have to analyze a particular sorting algorithm. The algorithm proce ...

  8. ASP.NET MVC4.0+EF+LINQ+bui+bootstrap+网站+角色权限管理系统(1)

    本系列的的角色权限管理主要采用Dotnet MVC4工程内置的权限管理模块Simplemembership实现,主要有关文件是InitializeSimpleMembershipAttribute.c ...

  9. TOJ5705动态序列操作(STL or treap)

    传送门:动态序列操作 在一个动态变化的序列中,完成以下基本操作: (1)插入一个整数 (2)删除一个整数 (3)查找序列中最大的数 (4)查找序列中最小的数 (5)求x的前驱(前驱定义为不大于x的序列 ...

  10. 微软软件开发技术二十年回顾-.NET框架篇

    六. .NET框架篇 .NET是微软自从发布Windows 3.0以来最为激动人心的新技术,是微软战略上为下一个十年对服务器和桌面软件工程的第一步,是微软的一场世纪大豪赌.对于.NET,微软的定义是, ...