├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── README-EN.md ├── README.md ├── cgit.bat ├── cgit.sh ├── doc ├── img │ ├── idea-proerties-ascii.png │ └── junitperf-report-html.png ├── todo.md ├── 创作因由.md └── 发布流程.md ├── pom.xml ├── release.bat ├── release.sh ├── release_rm.sh └── src ├── main ├── java │ └── com │ │ └── github │ │ └── houbb │ │ └── junitperf │ │ ├── constant │ │ ├── VersionConstant.java │ │ └── enums │ │ │ └── StatusEnum.java │ │ ├── core │ │ ├── annotation │ │ │ ├── JunitPerfConfig.java │ │ │ └── JunitPerfRequire.java │ │ ├── jupiter │ │ │ ├── context │ │ │ │ └── PerfConfigContext.java │ │ │ └── provider │ │ │ │ └── PerfConfigProvider.java │ │ ├── report │ │ │ ├── Reporter.java │ │ │ └── impl │ │ │ │ ├── ConsoleReporter.java │ │ │ │ └── HtmlReporter.java │ │ └── statistics │ │ │ ├── StatisticsCalculator.java │ │ │ └── impl │ │ │ └── DefaultStatisticsCalculator.java │ │ ├── model │ │ ├── BaseModel.java │ │ ├── evaluation │ │ │ ├── EvaluationContext.java │ │ │ └── component │ │ │ │ ├── EvaluationConfig.java │ │ │ │ ├── EvaluationRequire.java │ │ │ │ └── EvaluationResult.java │ │ └── vo │ │ │ └── I18nVo.java │ │ ├── support │ │ ├── builder │ │ │ ├── EvaluationConfigBuilder.java │ │ │ ├── EvaluationRequireBuilder.java │ │ │ └── EvaluationResultBuilder.java │ │ ├── exception │ │ │ ├── JunitPerfException.java │ │ │ └── JunitPerfRuntimeException.java │ │ ├── i18n │ │ │ └── I18N.java │ │ ├── statements │ │ │ └── PerformanceEvaluationStatement.java │ │ └── task │ │ │ └── PerformanceEvaluationTask.java │ │ └── util │ │ └── FreemarkerUtil.java └── resources │ ├── htmls │ ├── chart.html │ └── hello-better.html │ ├── i18n │ ├── JunitPerfMessages.properties │ ├── JunitPerfMessages_en.properties │ └── JunitPerfMessages_zh_CN.properties │ └── templates │ ├── report.ftl │ └── report_old.ftl └── test └── java └── com └── github └── houbb └── junitperf └── examples ├── HelloWorldMultiTest.java ├── HelloWorldTest.java ├── LongNameHtmlTest.java ├── OrderedHtmlTest.java └── report ├── DefaultReporterTest.java ├── DefineReporterTest.java ├── HtmlReporterTest.java ├── MultiReporterTest.java └── support └── MyReporter.java /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven # 2 | target/ 3 | _site/ 4 | 5 | # IDEA # 6 | .git/ 7 | .idea/ 8 | *.iml 9 | src/log/ 10 | 11 | # Eclipse # 12 | .settings/ 13 | .metadata/ 14 | .classpath 15 | .project 16 | Servers/ 17 | 18 | # ignore generator files 19 | temp 20 | ftp.properties 21 | mdm.txt 22 | mdm_files.txt 23 | v5.txt 24 | v5_files.txt 25 | 26 | 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true 5 | script: mvn test 6 | after_success: 7 | - mvn clean cobertura:cobertura coveralls:report 8 | env: 9 | global: 10 | secure: iPE3WhBDDUoyhVQWHNUfSXpd7EH/HAOkC4kMeC+cP2w9/aGA35jmmlcEOoNRKbnuBFpWF51Td1Iq2/19sScSlOmUMR8hYmBcGcy219UraqL88KaM8ToGUKMQStaSvexy5Xv8WO8tEilu5n4vTBM7pQws+8wnQbJWGS4IfXCjpA4kf/5XB8jnHzGuTO/q22g7UpFvHnO3ABlHs1CKf5m0Xmhen9V25Km7G0eYK4kVFMnrylo0snulRAapxfhIwTpNfuJSM8cC+6Sh6znSW18bxhPunMUHdZKcdp7pKVUMC5eKeZbyyUKzbo9PUZg6lsNDDFbRR5Kx6oAS4PVKY4njxST2PtAafQh7qOD6fmy5BBlNDNtsC/aAu2Grz9teB6jzfDNz1UiFlJ5yKgJz3g9N0rsj6W8LJVghhURZDtoV0dwylCjbhEFYY+Jid0CdOZtRdLKEv/3uvddjXbm4KL8OrqaTE8Bpbx3K8oFJhcwWzsEOfLNWW3vnvMaH5SoXVhYQoi88kk9dPtYCaL1AK8d6FBopf6OS6bFHMx+bSPtSdSv2RiaNPwU7zk3HtlZmCgylCWgFFpfiESDDSiVRqBMB2wNpe9+LPEUYEACUvrlbSU2FcC7Wfzkqu6pABx8It7KJxVlo4+AMHzPvhi/Jc72+s/MTCeBlmnkctHmO5BbMOWE= 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 变更日志 2 | 3 | 用于记录各个版本的主要变化 4 | 5 | ## 类型说明 6 | 7 | | 类型 | 说明 | 8 | |:----|:----| 9 | | A | 新增 | 10 | | U | 更新 | 11 | | D | 删除 | 12 | | T | 测试 | 13 | | O | 优化 | 14 | | F | 修复BUG | 15 | 16 | # release_1.0.0 17 | 18 | 基础功能 19 | 20 | # release_1.0.1 21 | 22 | - 2018-1-15 13:31:11 23 | 24 | add junit test 25 | 26 | - 2018-01-15 22:44:49 27 | 28 | 1. 修正 TimeUtil 方法名称 29 | 30 | 2. 调整 EvaluationContext 31 | 32 | # release_1.0.2 33 | 34 | | 变更类型 | 具体 | 说明 | 时间 | 备注 | 35 | |:---|:---|:---|:--|:---| 36 | | O | 代码优化 | | 2018-1-29 09:17:15 | 解决 sonar 相关 BUGS 和坏味道 | 37 | | A | 新增 I18N 支持 | | 2018-05-05 00:41:58 | | 38 | | O | HTML 报告页面优化 | | 2018-05-05 00:41:58 | | 39 | | F | 修复是否成功判断BUG | | 2018-05-05 00:41:58 | | 40 | | U | 使用文档更新 | | 2018-05-05 00:53:20 | | 41 | 42 | # release_2.0.0 43 | 44 | | 序号 | 变更类型 | 具体 | 说明 | 时间 | 备注 | 45 | |:---|:---|:---|:---|:--|:---| 46 | | 1 | U | 调整为 [junit5](https://junit.org/junit5/) 实现 | | 2018-07-02 10:54:09 | | 47 | 48 | # release_2.0.2 49 | 50 | | 序号 | 变更类型 | 具体 | 说明 | 时间 | 备注 | 51 | |:---|:---|:---|:---|:--|:---| 52 | | 1 | O | 优化二方库依赖 | | 2019-5-14 17:31:28 | | 53 | 54 | # release_2.0.2 55 | 56 | | 序号 | 变更类型 | 具体 | 说明 | 时间 | 备注 | 57 | |:---|:---|:---|:---|:--|:---| 58 | | 1 | O | 代码优化 | | 2020-1-22 09:05:41 | | 59 | | 2 | O | 说明文档优化 | | 2020-1-22 09:05:41 | | 60 | | 3 | O | 二方库优化 | | 2020-1-22 09:05:41 | | 61 | 62 | # release_2.0.4 63 | 64 | | 序号 | 变更类型 | 具体 | 说明 | 时间 | 备注 | 65 | |:---|:---|:---|:---|:--|:---| 66 | | 1 | U | 移除对于 log-integration 的依赖 | | 2020-6-15 20:10:40 | | 67 | | 2 | O | 文档优化 | | 2020-6-15 20:10:40 | | 68 | 69 | # release_2.0.5 70 | 71 | | 序号 | 变更类型 | 具体 | 说明 | 时间 | 备注 | 72 | |:---|:---|:---|:---|:--|:---| 73 | | 1 | F | 新增内存消耗特性 | | 2020-6-16 13:53:54 | | 74 | | 2 | U | 更新 guava 版本 | | 2020-6-16 14:02:54 | | 75 | 76 | # release_2.0.6 77 | 78 | | 序号 | 变更类型 | 具体 | 说明 | 时间 | 备注 | 79 | |:---|:---|:---|:---|:--|:---| 80 | | 1 | U | 更新 junit5 版本 | | 2020-6-16 19:59:35 | 支持 Order,升级到 5.4 以后 | 81 | | 2 | O | 优化页面显示 | | 2020-6-16 14:02:54 | FIXED #10 | 82 | | 3 | F | 支持顺序显示 | | 2020-6-16 14:02:54 | FIXED #10 | 83 | | 4 | O | 日志输出优化 | | 2020-6-16 14:02:54 | FIXED #10 | 84 | 85 | # release_2.0.7 86 | 87 | | 序号 | 变更类型 | 具体 | 说明 | 时间 | 备注 | 88 | |:---|:---|:---|:---|:--|:---| 89 | | 1 | U | heaven 依赖更新 | | 2020-6-17 11:11:48 | 移除公用代码 | 90 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2016 ShenHuaJie iBase4J@163.com 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | ====================================================================== 205 | 206 | Apache许可证 207 | 版本 2.0,2004年1月 208 | http://www.apache.org/licenses/ 209 | 210 | 使用、重生成及分发的术语和条件: 211 | 212 | 1.定义 213 | 214 | "许可证"是指根据本文档第1到第9部分关于使用、重生成和分发的术语和条件。 215 | 216 | "许可证颁发者"是指版权所有者或者由版权所有者批准的授权许可证的实体。 217 | 218 | "法律实体"是指实施实体和进行控制的所有其它实体受该实体控制,或者受该实体集中控制。 219 | 根据此定义,"控制"是指(i)让无论是否签订协议的上述实体,进行指导或管理的直接权利或间接权利, 220 | 或者(ii)拥有百分之五十(50%)或以上已发行股票的所有者,或者(iii)上述实体的实权所有者。 221 | 222 | "用户"(或"用户的")是指行使本许可证所授予权限的个人或法律实体。 223 | 224 | "源程序"形式是指对包含但不限制软件源代码、文档源程序和配置文件进行修改的首选形式。 225 | 226 | "目标"形式是指对源程序形式进行机械转换或翻译的任何形式,包括但不限于对编译的目标代码, 227 | 生成的文件以及转换为其它媒体类型。 228 | 229 | "作品"是指根据本许可证所制作的源程序形式或目标形式的著作,在著作中包含的或附加的版权通知 230 | (在下面附录中提供了一个示例)。 231 | 232 | "衍生作品"是指基于作品(或从作品衍生而来)的源程序形式或目标形式的任何作品,以及编辑修订、 233 | 注释、详细描述或其它修订等构成原创著作作品的整体。根据本许可证,衍生作品不得包括与作品及其 234 | 衍生作品分离之作品,或仅与作品及其衍生作品的接口相链接(或按名称结合)之作品。 235 | 236 | "贡献"是指任何著作作品,包括作品的原始版本和对该作品或衍生作品所做的任何修订或补充, 237 | 意在提交给许可证颁发者以让版权所有者或代表版权所有者的授权个人或法律实体包含在其作品中。 238 | 根据此定义,"提交"一词表示发送给许可证颁发者或其代表人,任何电子的、口头的或书面的交流信息形式, 239 | 包括但不限于在由许可证颁发者或者代表其管理的电子邮件清单、源代码控制系统、以及发布跟踪系统上为 240 | 讨论和提高作品的交流,但不包括由版权所有者以书面形式明显标注或指定为"非贡献"的交流活动。 241 | 242 | "贡献者"是指许可证颁发者和代表从许可证颁发者接受之贡献的并随后包含在作品之贡献中的任何个人或法律实体。 243 | 244 | 2.版权许可证的授予 245 | 246 | 根据本许可证的条款,每个贡献者授予用户永久性的、全球性的、非专有性的、免费的、无版权费的、 247 | 不可撤销的版权许可证以源程序形式或目标形式复制、准备衍生作品、公开显示、公开执行、 248 | 授予分许可证、以及分发作品和这样的衍生作品。 249 | 250 | 3.专利许可证的授予 251 | 252 | 根据本许可证的条款,每个贡献者授予用户永久性的、全球性的、非专有性的、免费的、无版权费的、 253 | 不可撤销的(除在本部分进行说明)专利许可证对作品进行制作、让人制作、使用、提供销售、销售、 254 | 进口和其它转让,且这样的许可证仅适用于在所递交作品的贡献中因可由单一的或多个这样的贡献者 255 | 授予而必须侵犯的申请专利。如果用户对任何实体针对作品或作品中所涉及贡献提出因直接性或贡献性 256 | 专利侵权而提起专利法律诉讼(包括交互诉讼请求或反索赔),那么根据本许可证,授予用户针对作品 257 | 的任何专利许可证将在提起上述诉讼之日起终止。 258 | 259 | 4.重新分发 260 | 261 | 用户可在任何媒介中复制和分发作品或衍生作品之副本,无论是否修订,还是以源程序形式或目标形式, 262 | 条件是用户需满足下列条款: 263 | 264 | a) 用户必须为作品或衍生作品的任何其他接收者提供本许可证的副本;并且 265 | 266 | b) 用户必须让任何修改过的文件附带明显的通知,声明用户已更改文件;并且 267 | 268 | c) 用户必须从作品的源程序形式中保留衍生作品源程序形式的用户所分发的所有版权、专利、 269 | 商标和属性通知,但不包括不属于衍生作品任何部分的类似通知;并且 270 | 271 | d) 如果作品将"通知"文本文件包括为其分发作品的一部分,那么用户分发的任何衍生作品中须至少 272 | 在下列地方之一包括,在这样的通知文件中所包含的属性通知的可读副本,但不包括那些不属于衍生 273 | 作品任何部分的通知:在作为衍生作品一部分而分发的通知文本文件中;如果与衍生作品一起提供则 274 | 在源程序形式或文件中;或者通常作为第三方通知出现的时候和地方,在衍生作品中产生的画面中。 275 | 通知文件的内容仅供信息提供,并未对许可证进行修改。用户可在其分发的衍生作品中在作品的通知 276 | 文本后或作为附录添加自己的属性通知,条件是附加的属性通知不得构成修改本许可证。 277 | 278 | 用户可以为自身所做出的修订添加自己的版权声明并可对自身所做出修订内容或为这样的衍生作品作为 279 | 整体的使用、复制或分发提供附加或不同的条款,条件是用户对作品的使用、复制和分发必须符合本许 280 | 可证中声明的条款。 281 | 282 | 5.贡献的提交。 283 | 284 | 除非用户明确声明,在作品中由用户向许可证颁发者的提交若要包含在贡献中,必须在无任何附加条款下 285 | 符合本许可证的条款。尽管上面如此规定,执行许可证颁发者有关贡献的条款时,任何情况下均不得替代 286 | 或修改任何单独许可证协议的条款。 287 | 288 | 6.商标。本许可证并未授予用户使用许可证颁发者的商号、商标、服务标记或产品名称,除非将这些名称 289 | 用于合理性和惯例性描述作品起源和复制通知文件的内容时。 290 | 291 | 7.保证否认条款。除非因适用法律需要或书面同意,许可证颁发者以"按原样"基础提供作品(并且每个 292 | 贡献者提供其贡献),无任何明示的或暗示的保证或条件,包括但不限于关于所有权、不侵权、 293 | 商品适销性、或适用性的保证或条件。用户仅对使用或重新分发作品的正确性负责,并需承担根据本 294 | 许可证行使权限时的任何风险。 295 | 296 | 8.责任限制条款。在任何情况下并根据任何法律,无论是因侵权(包括过失)或根据合同,还是其它原因, 297 | 除非根据适用法律需要(例如故意行为和重大过失行为)或经书面同意,即使贡献者事先已被告知发生 298 | 损害的可能性,任何贡献者不就用户因使用本许可证或不能使用或无法使用作品(包括但不限于商誉损失、 299 | 停工、计算机失效或故障,或任何商业损坏或损失)而造成的损失,包括直接的、非直接的、特殊的、意外 300 | 的或间接的字符损坏而负责。 301 | 302 | 9.接受保证或附加责任。重新分发作品或及其衍生作品时,用户可选择提供或为符合本许可证承担之支持、 303 | 担保、赔偿或其它职责义务和/或权利而收取费用。但是,在承担上述义务时,用户只可代表用户本身和 304 | 用户本身责任来执行,无需代表任何其它贡献者,并且用户仅可保证、防护并保持每个贡献者不受任何 305 | 因此而产生的责任或对因用户自身承担这样的保证或附加责任而对这样的贡献者所提出的索赔。 306 | 307 | 条款结束 308 | 309 | 附录:如何向用户作品中应用Apache许可证。 310 | 311 | 若要向用户作品应用Apache许可证,请附加下列样本通知,将括号"[]"中的字段以用户自身的 312 | 区分信息来替换(但不包括括号)。文本必须以文件格式适当的注释句法包含在其中。 313 | 另外建议将文件名或类别名以及目的说明包含在相同的"打印页"上作为版权通知,以更加容易的区分出第三方档案。 314 | 315 | 版权所有 2016 ShenHuaJie iBase4J@163.com 根据2.0版本Apache许可证("许可证")授权; 316 | 根据本许可证,用户可以不使用此文件。 317 | 318 | 用户可从下列网址获得许可证副本:http://www.apache.org/licenses/LICENSE-2.0 319 | 除非因适用法律需要或书面同意,根据许可证分发的软件是基于"按原样"基础提供, 320 | 无任何明示的或暗示的保证或条件。详见根据许可证许可下,特定语言的管辖权限和限制。 321 | 322 | ======================================================= 323 | 324 | 简要解释: 325 | 326 | 1.需要给代码的用户一份Apache Licence 327 | 2.如果你修改了代码,需要在被修改的文件中说明。 328 | 3.在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标, 329 | 专利声明和其他原来作者规定需要包含的说明。 330 | 4.如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有 Apache Licence。 331 | 你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改。 -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | # junitperf 2 | 3 | > [使用说明](README.md) 4 | 5 | [junitperf](https://github.com/houbb/junitperf) is a performance testing framework designed for java developers. 6 | 7 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.houbb/junitperf/badge.svg)](http://mvnrepository.com/artifact/com.github.houbb/junitperf) 8 | [![Build Status](https://www.travis-ci.org/houbb/junitperf.svg)](https://www.travis-ci.org/houbb/junitperf) 9 | [![](https://img.shields.io/badge/license-Apache2-FF0080.svg)](https://github.com/houbb/junitperf/blob/master/LICENSE.txt) 10 | [![Open Source Love](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/houbb/junitperf) 11 | 12 | ## Why use it? 13 | 14 | - It fits perfectly with Junit5. 15 | 16 | - Simple to use, convenient for practical testing during project development. 17 | 18 | - Provide expansion, users can customize development. 19 | 20 | ## Features 21 | 22 | - Support I18N 23 | 24 | - Support multiple report generation methods, support custom 25 | 26 | - Junt5 perfect support, easy for Java developers to use 27 | 28 | ## CHANGELOG 29 | 30 | [CHANGELOG](CHANGELOG.md) 31 | 32 | ### v2.0.5 major change 33 | 34 | 1. new feature for memory cost 35 | 36 | # Quick Start 37 | 38 | ## Required 39 | 40 | - jdk1.8 or later 41 | 42 | - [Junit5](https://junit.org/junit5/) 43 | 44 | ## maven 45 | 46 | ```xml 47 | 48 | com.github.houbb 49 | junitperf 50 | 2.0.5 51 | 52 | ``` 53 | 54 | ## hello world 55 | 56 | > [hello world](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/HelloWorldTest.java) 57 | 58 | - demo 59 | 60 | ```java 61 | public class HelloWorldTest { 62 | 63 | @JunitPerfConfig(duration = 1000) 64 | public void helloTest() throws InterruptedException { 65 | Thread.sleep(100); 66 | System.out.println("Hello Junit5"); 67 | } 68 | 69 | } 70 | ``` 71 | 72 | # Config 73 | 74 | ## Annotation 75 | 76 | ### @JunitPerfConfig 77 | 78 | config for test 79 | 80 | | Attr | Desc | Type | Default | Remark | 81 | |:----|:----|:----|:----|:----| 82 | | threads | How many threads are used to execute | int | 1 | | 83 | | warmUp | Preparation time | long | 0 | Unit:mills | 84 | | duration | Execution time | long | 60_000(1 min) | Unit:mills | 85 | | latencyStatistics | Statistics impl | StatisticsCalculator | DefaultStatisticsCalculator | | 86 | | reporter | Reporter impl | Reporter | ConsoleReporter | | 87 | 88 | as following: 89 | 90 | ```java 91 | public class JunitPerfConfigTest { 92 | 93 | /** 94 | * two threads 95 | * Preparation time:1000ms 96 | * Execution time: 2000ms 97 | * @throws InterruptedException if any 98 | */ 99 | @JunitPerfConfig(threads = 2, warmUp = 1000, duration = 2000) 100 | public void junitPerfConfigTest() throws InterruptedException { 101 | System.out.println("junitPerfConfigTest"); 102 | Thread.sleep(200); 103 | } 104 | 105 | } 106 | ``` 107 | 108 | ### Reporter types 109 | 110 | This is mainly the **output method** for performance test latencyStatistics. 111 | 112 | The following methods are supported: 113 | 114 | | TYPE | DEMO | 115 | |:----|:----| 116 | | Default | [DefaultReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/DefaultReporterTest.java) | 117 | | Console | [ConsoleReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/ConsoleReporterTest.java) | 118 | | HTML | [HtmlReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/HtmlReporterTest.java) | 119 | | Multi | [MultiReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/MultiReporterTest.java) | 120 | | Define | [DefineReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/DefineReporterTest.java) | 121 | 122 | ### @JunitPerfRequire 123 | 124 | Specify the requirements to be met when testing. (Optional) 125 | 126 | | Properties | Description | Type | Default | Remarks | 127 | |:----|:----|:----|:----|:----| 128 | | min | best cost | float | -1 | If the fastest running time is higher than this value, it is regarded as a failure. Unit: ms | 129 | | max | worst cost | float | -1 | If the worst running time is higher than this value, it is regarded as failure. Unit: ms | 130 | | average | avg cost | float | -1 | If the average running time is higher than this value, it is regarded as a failure. Unit: ms | 131 | | timesPerSecond | The minimum number of executions per second | int | 0 | If it is lower than this minimum number of executions, it is regarded as a failure. | 132 | | percentiles | Limitation on execution time | String[] | {} | percentiles={"20:220", "30:250"}。20% of the data execution time should not exceed 220ms; 30% of the data execution time should not exceed 250ms; | 133 | 134 | as following: 135 | 136 | ```java 137 | public class JunitPerfRequireTest { 138 | /** 139 | * Configuration: 2 threads running. Preparation time: 1000ms. Running time: 2000ms. 140 | * Requirements: The fastest should not be less than 210ms, the slowest should not be less than 250ms, the average should not be less than 225ms, and the number of operations per second should not be less than 4 times. 141 | * 20% of the data is not less than 220ms, and 50% of the data is not less than 230ms; 142 | * 143 | * @throws InterruptedException if any 144 | */ 145 | @JunitPerfConfig(threads = 2, warmUp = 1000, duration = 2000) 146 | @JunitPerfRequire(min = 210, max = 250, average = 225, timesPerSecond = 4, percentiles = {"20:220", "50:230"}) 147 | public void junitPerfConfigTest() throws InterruptedException { 148 | System.out.println("junitPerfConfigTest"); 149 | Thread.sleep(200); 150 | } 151 | 152 | } 153 | ``` 154 | 155 | ## Reporting method 156 | 157 | ### Command line 158 | 159 | Roughly as follows: 160 | 161 | ``` 162 | [INFO] 2018-01-14 22:16:31.419 [] - Started at: 2018-01-14 22:16:30.194 163 | [INFO] 2018-01-14 22:16:31.419 [] - Invocations: 10 164 | [INFO] 2018-01-14 22:16:31.420 [] - Success: 10 165 | [INFO] 2018-01-14 22:16:31.420 [] - Errors: 0 166 | [INFO] 2018-01-14 22:16:31.420 [] - Thread Count: 2 167 | [INFO] 2018-01-14 22:16:31.421 [] - Warm up: 0ms 168 | [INFO] 2018-01-14 22:16:31.421 [] - Execution time: 1000ms 169 | [INFO] 2018-01-14 22:16:31.421 [] - Throughput: 10/s (Required: -1/s) - PASSED 170 | [INFO] 2018-01-14 22:16:31.424 [] - Min latency: 200.2112ms (Required: -1.0ms) - PASSED 171 | [INFO] 2018-01-14 22:16:31.424 [] - Max latency: 205.67862ms (Required: -1.0ms) - PASSED 172 | [INFO] 2018-01-14 22:16:31.425 [] - Ave latency: 202.97829ms (Required: -1.0ms) - PASSED 173 | ``` 174 | 175 | ### HTML way 176 | 177 | The page is as follows: 178 | 179 | Later, style adjustments will be made. 180 | 181 | ![junitperf-report-html.png](doc/img/junitperf-report-html.png) 182 | 183 | # Relative Open-Source Framework 184 | 185 | [data-factory](https://github.com/houbb/data-factory): Automatically generate test data 186 | 187 | [gen-test-plugin](https://github.com/houbb/gen-test-plugin): Maven plugin to automatically generate test cases 188 | 189 | # Road-MAP 190 | 191 | - [ ] Memory usage latencyStatistics -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # junitperf 2 | 3 | > [README-ENGLISH](README-EN.md) 4 | 5 | [junitperf](https://github.com/houbb/junitperf) 是一款为 java 开发者设计的性能测试框架。 6 | 7 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.houbb/junitperf/badge.svg)](http://mvnrepository.com/artifact/com.github.houbb/junitperf) 8 | [![Build Status](https://www.travis-ci.org/houbb/junitperf.svg)](https://www.travis-ci.org/houbb/junitperf) 9 | [![](https://img.shields.io/badge/license-Apache2-FF0080.svg)](https://github.com/houbb/junitperf/blob/master/LICENSE.txt) 10 | [![Open Source Love](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/houbb/junitperf) 11 | 12 | ## 为什么使用? 13 | 14 | - 可以和 Junit5 完美契合。 15 | 16 | - 使用简单,便于项目开发过程中的测试实用。 17 | 18 | - 提供拓展,用户可进行自定义开发。 19 | 20 | ## 特性 21 | 22 | - 支持 I18N 23 | 24 | - 支持多种报告生成方式,支持自定义 25 | 26 | - Junt5 完美支持,便于 Java 开发者使用 27 | 28 | ## 变更记录 29 | 30 | [变更日志](CHANGELOG.md) 31 | 32 | ### v2.0.7 主要变更 33 | 34 | 1. 升级 heaven 依赖 35 | 36 | # 快速开始 37 | 38 | ## 项目依赖 39 | 40 | - jdk1.8 及其以上版本 41 | 42 | - [Junit5](https://junit.org/junit5/) 及其以上版本 43 | 44 | ## maven 导入 45 | 46 | ```xml 47 | 48 | com.github.houbb 49 | junitperf 50 | 2.0.7 51 | 52 | ``` 53 | 54 | ## 入门案例 55 | 56 | > [入门案例地址](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/HelloWorldTest.java) 57 | 58 | - 使用例子 59 | 60 | ```java 61 | public class HelloWorldTest { 62 | 63 | @JunitPerfConfig(duration = 1000) 64 | public void helloTest() throws InterruptedException { 65 | Thread.sleep(100); 66 | System.out.println("Hello Junit5"); 67 | } 68 | 69 | } 70 | ``` 71 | 72 | # 配置说明 73 | 74 | ## 测试注解指定 75 | 76 | ### @JunitPerfConfig 77 | 78 | 指定测试时的属性配置。(必填项) 79 | 80 | | 属性 | 说明 | 类型 | 默认值 | 备注 | 81 | |:----|:----|:----|:----|:----| 82 | | threads | 执行时使用多少线程执行 | int | 1 | | 83 | | warmUp | 准备时间 | long | 0 | 单位:毫秒 | 84 | | duration | 执行时间 | long | 60_000(1分钟) | 单位:毫秒 | 85 | | latencyStatistics | 统计实现 | StatisticsCalculator | DefaultStatisticsCalculator | | 86 | | reporter | 报告实现 | Reporter | ConsoleReporter | | 87 | 88 | 使用如下: 89 | 90 | ```java 91 | public class JunitPerfConfigTest { 92 | 93 | /** 94 | * 2个线程运行。 95 | * 准备时间:1000ms 96 | * 运行时间: 2000ms 97 | * @throws InterruptedException if any 98 | */ 99 | @JunitPerfConfig(threads = 2, warmUp = 1000, duration = 2000) 100 | public void junitPerfConfigTest() throws InterruptedException { 101 | System.out.println("junitPerfConfigTest"); 102 | Thread.sleep(200); 103 | } 104 | 105 | } 106 | ``` 107 | 108 | ### 各种报告的实现 109 | 110 | 这里主要是对于性能测试统计的**输出方式**。 111 | 支持以下方式: 112 | 113 | | 方式 | 案例 | 114 | |:----|:----| 115 | | 默认方式 | [DefaultReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/DefaultReporterTest.java) | 116 | | 命令行 | [ConsoleReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/ConsoleReporterTest.java) | 117 | | HTML | [HtmlReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/HtmlReporterTest.java) | 118 | | 组合方式 | [MultiReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/MultiReporterTest.java) | 119 | | 自定义方式 | [DefineReporterTest](https://github.com/houbb/junitperf/blob/master/src/test/java/com/github/houbb/junitperf/examples/report/DefineReporterTest.java) | 120 | 121 | ### @JunitPerfRequire 122 | 123 | 指定测试时需要达到的要求。(选填项) 124 | 125 | | 属性 | 说明 | 类型 | 默认值 | 备注 | 126 | |:----|:----|:----|:----|:----| 127 | | min | 最佳的运行耗时 | float | -1 | 最快的运行耗时如果高于这个值,则视为失败。单位:毫秒 | 128 | | max | 平均的运行耗时 | float | -1 | 最坏的运行耗时如果高于这个值,则视为失败。单位:毫秒 | 129 | | average | 平均的运行耗时 | float | -1 | 平均的运行耗时如果高于这个值,则视为失败。单位:毫秒 | 130 | | timesPerSecond | 每秒的最小执行次数 | int | 0 | 如果低于这个最小执行次数,则视为失败。 | 131 | | percentiles | 对于执行耗时的限定 | String[] | {} | percentiles={"20:220", "30:250"}。20% 的数据执行耗时不得超过 220ms;30% 的数据执行耗时不得超过 250ms; | 132 | 133 | 使用如下: 134 | 135 | ```java 136 | public class JunitPerfRequireTest { 137 | /** 138 | * 配置:2个线程运行。准备时间:1000ms。运行时间: 2000ms。 139 | * 要求:最快不可低于 210ms, 最慢不得低于 250ms, 平均不得低于 225ms, 每秒运行次数不得低于 4 次。 140 | * 20% 的数据不低于 220ms, 50% 的数据不得低于 230ms; 141 | * 142 | * @throws InterruptedException if any 143 | */ 144 | @JunitPerfConfig(threads = 2, warmUp = 1000, duration = 2000) 145 | @JunitPerfRequire(min = 210, max = 250, average = 225, timesPerSecond = 4, percentiles = {"20:220", "50:230"}) 146 | public void junitPerfConfigTest() throws InterruptedException { 147 | System.out.println("junitPerfConfigTest"); 148 | Thread.sleep(200); 149 | } 150 | 151 | } 152 | ``` 153 | 154 | ## 报告方式 155 | 156 | ### 命令行方式 157 | 158 | 大致如下: 159 | 160 | ``` 161 | [INFO] [2020-06-16 20:05:53.618] [c.g.h.j.e.HelloWorldTest.helloTest] - Started at: 2020-06-16 20:05:52.512 162 | [INFO] [2020-06-16 20:05:53.619] [c.g.h.j.e.HelloWorldTest.helloTest] - Invocations: 9 163 | [INFO] [2020-06-16 20:05:53.620] [c.g.h.j.e.HelloWorldTest.helloTest] - Success: 9 164 | [INFO] [2020-06-16 20:05:53.620] [c.g.h.j.e.HelloWorldTest.helloTest] - Errors: 0 165 | [INFO] [2020-06-16 20:05:53.621] [c.g.h.j.e.HelloWorldTest.helloTest] - Thread Count: 1 166 | [INFO] [2020-06-16 20:05:53.623] [c.g.h.j.e.HelloWorldTest.helloTest] - Warm up: 0ms 167 | [INFO] [2020-06-16 20:05:53.623] [c.g.h.j.e.HelloWorldTest.helloTest] - Execution time: 1000ms 168 | [INFO] [2020-06-16 20:05:53.624] [c.g.h.j.e.HelloWorldTest.helloTest] - Throughput: 9/s (Required: -1/s) - PASSED 169 | [INFO] [2020-06-16 20:05:53.625] [c.g.h.j.e.HelloWorldTest.helloTest] - Memory cost: 16byte 170 | [INFO] [2020-06-16 20:05:53.635] [c.g.h.j.e.HelloWorldTest.helloTest] - Min latency: 100.191414ms (Required: -1.0ms) - PASSED 171 | [INFO] [2020-06-16 20:05:53.635] [c.g.h.j.e.HelloWorldTest.helloTest] - Max latency: 105.2382ms (Required: -1.0ms) - PASSED 172 | [INFO] [2020-06-16 20:05:53.636] [c.g.h.j.e.HelloWorldTest.helloTest] - Avg latency: 101.43268ms (Required: -1.0ms) - PASSED 173 | ``` 174 | 175 | ### HTML 方式 176 | 177 | 页面如下: 178 | 179 | 后期会进行样式调整。 180 | 181 | ![junitperf-report-html.png](doc/img/junitperf-report-html.png) 182 | 183 | # 指定方法执行顺序 184 | 185 | ## 说明 186 | 187 | 方法的执行顺序会影响到最终的报告显示顺序。 188 | 189 | 如果你想严格指定同一个类方法的执行顺序,推荐使用 [Test Execution Order](https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order) 的方式。 190 | 191 | `@TestMethodOrder` 需要 junit5 在 5.4 及其以后版本 192 | 193 | ## 示例代码 194 | 195 | 参考 [OrderedHtmlTest](https://github.com/houbb/junitperf/tree/master/src/test/java/com/github/houbb/junitperf/examples/OrderedHtmlTest.java) 196 | 197 | # 对于 junit4 的支持 198 | 199 | ## 引入 jar 200 | 201 | ```xml 202 | 203 | com.github.houbb 204 | junitperf 205 | 1.0.3 206 | 207 | ``` 208 | 209 | ## 相关文档 210 | 211 | [junit4 相关使用](https://github.com/houbb/junitperf/tree/release_1.0.3) 212 | 213 | # 友情提示 214 | 215 | I18N 功能中的中文 properties 文件直接使用 `UTF-8` 格式。 216 | 217 | 如果你下载本项目进行测试,出现中文乱码。 218 | 219 | ## IDEA 220 | 221 | idea 用户请勾选上图中的选项 222 | 223 | 【Setting】=》【File Encoding】勾选上 ascii 转换 224 | 225 | ![idea-proerties-ascii.png](doc/img/idea-proerties-ascii.png) 226 | 227 | # 关联开源框架 228 | 229 | [data-factory 自动生成测试数据](https://github.com/houbb/data-factory) 230 | 231 | [gen-test-plugin 自动生成测试案例的 maven 插件](https://github.com/houbb/gen-test-plugin) 232 | 233 | # 后期 Road-MAP 234 | 235 | - [X] 内存使用统计 236 | 237 | - [ ] 支持线程的 sync 同步执行 238 | 239 | - [ ] 优化 html 输出中的英文,支持 I18N 240 | 241 | - [ ] 优化 html 输出中的格式,所有格式对齐 242 | 243 | # 拓展阅读 244 | 245 | [基于 junit4 分析 junitperf 源码](http://houbb.github.io/2021/07/23/junit-performance-junit4) 246 | 247 | [基于 junit5 实现 junitperf 源码分析](http://houbb.github.io/2021/07/23/junit-performance-junit5) 248 | 249 | [浅谈性能测试](http://houbb.github.io/2021/07/23/junit-performance-overview) 250 | -------------------------------------------------------------------------------- /cgit.bat: -------------------------------------------------------------------------------- 1 | :: 用于提交当前变更(windows) 2 | :: author: houbb 3 | :: LastUpdateTime: 2018-11-22 09:08:52 4 | :: 用法:双击运行,或者当前路径 cmd 直接输入 .\cgit.bat 5 | 6 | git add . 7 | git commit -m "[Feature] add for new" 8 | git push 9 | git status 10 | 11 | -------------------------------------------------------------------------------- /cgit.sh: -------------------------------------------------------------------------------- 1 | # 提交 2 | 3 | git add . 4 | git commit -m "[Feature] add for new" 5 | git push 6 | git status 7 | 8 | # 1. 赋值权限: chmod +x ./cgit.sh 9 | # 2. 执行: ./cgit.sh 10 | # Last Update Time: 2018-11-21 21:55:38 11 | # Author: houbb -------------------------------------------------------------------------------- /doc/img/idea-proerties-ascii.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houbb/junitperf/953300951e6021fe8cdc3ec9630a00675f74db91/doc/img/idea-proerties-ascii.png -------------------------------------------------------------------------------- /doc/img/junitperf-report-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houbb/junitperf/953300951e6021fe8cdc3ec9630a00675f74db91/doc/img/junitperf-report-html.png -------------------------------------------------------------------------------- /doc/todo.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houbb/junitperf/953300951e6021fe8cdc3ec9630a00675f74db91/doc/todo.md -------------------------------------------------------------------------------- /doc/创作因由.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 特别鸣谢 4 | 5 | ## 缘由 6 | 7 | 在看性能测试相关内容时发现这类框架(Junit 性能测试框架)。 8 | 9 | 个人初步参考以下2个项目进行实现,后续可能走完全不同的路线。 10 | 11 | - junitperf 12 | 13 | [junitperf](https://github.com/noconnor/JUnitPerf) 14 | 15 | 优点:实现简洁 16 | 17 | 缺点:(对个人而言) 18 | 19 | 使用 Jdk1.8 实现,本项目调整为 Jdk1.7,后续 V2.0 将直接升级为 Jdk1.8+; 20 | 21 | 使用 [Gradle](https://gradle.org/) 作为 jar 管理,个人习惯于 [Maven](http://maven.apache.org); 22 | 23 | 存在 bug(线程打断问题20180115),且更新不是特别活跃。 24 | 25 | - contiperf 26 | 27 | [contiperf](https://github.com/lucaspouzac/contiperf) 28 | 29 | 优点:功能完善 30 | 31 | 缺点:好像已经停止更新了。且代码较为晦涩。 32 | 33 | ## 技术支持 34 | 35 | - Junit 36 | 37 | [Junit Rules](https://github.com/junit-team/junit4/wiki/Rules) 38 | 39 | - 数据统计 40 | 41 | [Apache Commons math](http://commons.apache.org/proper/commons-math/userguide/stat.html#a1.2_Descriptive_statistics) 42 | 43 | - 报告生成 44 | 45 | [Freemarker](https://freemarker.apache.org/) 46 | 47 | - 工具类 48 | 49 | [Guava](https://github.com/google/guava) 50 | 51 | - 二方库 52 | 53 | [log-integration](https://github.com/houbb/log-integration) 用于日志整合。 54 | 55 | [heaven](https://github.com/houbb/heaven) 工具类集合。 -------------------------------------------------------------------------------- /doc/发布流程.md: -------------------------------------------------------------------------------- 1 | # push to mvn center 2 | 3 | 确认版本为 release 4 | 5 | ``` 6 | mvn clean deploy -P release 7 | ``` 8 | 9 | # commit to github 10 | 11 | ``` 12 | git push 13 | ``` 14 | 15 | # merge to master 16 | 17 | ``` 18 | git checkout master 19 | git pull 20 | git checkout branch 21 | git rebase master (用rebase合并主干的修改,如果有冲突在此时解决) 22 | git checkout master 23 | git merge branch 24 | git push 25 | ``` 26 | 27 | # create new branch & checkout 28 | 29 | ``` 30 | git branch release_XXX 31 | git checkout release_XXX 32 | ``` 33 | 34 | # modify project version 35 | 36 | ``` 37 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=paradise* -DoldVersion=1.1.1 -DnewVersion=1.1.2-SNAPSHOT--> 38 | mvn -N versions:update-child-modules 39 | mvn versions:commit 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.github.houbb 6 | junitperf 7 | 2.0.8-SNAPSHOT 8 | 9 | 10 | 11 | 3.2 12 | 3.2 13 | 2.18.1 14 | false 15 | false 16 | 17 | 2.2.1 18 | 2.9.1 19 | 1.5 20 | 21 | 4.3.0 22 | 2.7 23 | 24 | 1.0.1 25 | 26 | 27 | UTF-8 28 | 1.8 29 | 30 | 31 | 0.1.106 32 | 33 | 34 | 3.6.1 35 | 5.6.2 36 | 2.3.23 37 | 29.0-jre 38 | 7.1.0 39 | 40 | 41 | target/classes 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | com.github.houbb 51 | heaven 52 | ${heaven.version} 53 | 54 | 55 | 56 | 57 | org.apache.commons 58 | commons-math3 59 | ${commons-math3.version} 60 | 61 | 62 | 63 | com.google.guava 64 | guava 65 | ${google.guava.version} 66 | 67 | 68 | 69 | org.freemarker 70 | freemarker 71 | ${freemarker.version} 72 | 73 | 74 | 75 | org.junit.jupiter 76 | junit-jupiter-engine 77 | ${junit.version} 78 | 79 | 80 | org.junit.jupiter 81 | junit-jupiter-api 82 | ${junit.version} 83 | 84 | 85 | 86 | org.apache.lucene 87 | lucene-core 88 | ${lucene-core.version} 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | com.github.houbb 99 | heaven 100 | 101 | 102 | 103 | 104 | org.apache.commons 105 | commons-math3 106 | 107 | 108 | 109 | com.google.guava 110 | guava 111 | 112 | 113 | 114 | org.freemarker 115 | freemarker 116 | 117 | 118 | 119 | org.junit.jupiter 120 | junit-jupiter-engine 121 | 122 | 123 | org.junit.jupiter 124 | junit-jupiter-api 125 | 126 | 127 | 128 | org.apache.lucene 129 | lucene-core 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | org.apache.maven.plugins 140 | maven-compiler-plugin 141 | ${plugin.compiler.version} 142 | 143 | ${project.compiler.level} 144 | ${project.compiler.level} 145 | ${project.build.sourceEncoding} 146 | -proc:none 147 | 148 | 149 | 150 | 151 | org.apache.maven.plugins 152 | maven-surefire-plugin 153 | ${plugin.surefire.version} 154 | 155 | ${plugin.surefire.skip-it} 156 | ${plugin.surefire.ignore-failure} 157 | 158 | 159 | 160 | 161 | com.github.houbb 162 | gen-maven-plugin 163 | ${plugin.gen.version} 164 | 165 | false 166 | utf-8 167 | 168 | 169 | 170 | 171 | 172 | 173 | org.eluder.coveralls 174 | coveralls-maven-plugin 175 | ${plugin.coveralls.version} 176 | 177 | 178 | 179 | org.codehaus.mojo 180 | cobertura-maven-plugin 181 | ${plugin.cobertura.version} 182 | 183 | xml 184 | 256m 185 | 186 | true 187 | 188 | 189 | **/*Test.class 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | org.sonarsource.scanner.maven 201 | sonar-maven-plugin 202 | 3.1.1 203 | 204 | 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-javadoc-plugin 209 | ${plugin.maven-javadoc-plugin.version} 210 | 211 | 212 | 213 | 214 | 215 | junitperf 216 | Junit 性能测试框架 217 | 218 | 219 | org.sonatype.oss 220 | oss-parent 221 | 7 222 | 223 | 224 | 225 | The Apache Software License, Version 2.0 226 | http://www.apache.org/licenses/LICENSE-2.0.txt 227 | repo 228 | 229 | 230 | 231 | https://github.com/houbb/junitperf 232 | https://github.com/houbb/junitperf.git 233 | https://houbb.github.io/ 234 | 235 | 236 | 237 | houbb 238 | houbinbin.echo@gmail.com 239 | https://houbb.github.io/ 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | release 248 | 249 | 250 | 251 | 252 | org.apache.maven.plugins 253 | maven-source-plugin 254 | ${plugin.maven-source-plugin.version} 255 | 256 | 257 | package 258 | 259 | jar-no-fork 260 | 261 | 262 | 263 | 264 | 265 | 266 | org.apache.maven.plugins 267 | maven-javadoc-plugin 268 | ${plugin.maven-javadoc-plugin.version} 269 | 270 | 271 | package 272 | 273 | jar 274 | 275 | 276 | 277 | 278 | 279 | 280 | org.apache.maven.plugins 281 | maven-gpg-plugin 282 | ${plugin.maven-gpg-plugin.version} 283 | 284 | 285 | verify 286 | 287 | sign 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | oss 297 | https://oss.sonatype.org/content/repositories/snapshots/ 298 | 299 | 300 | oss 301 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /release.bat: -------------------------------------------------------------------------------- 1 | :: 用于 release 当前项目(windows) 2 | :: author: houbb 3 | :: LastUpdateTime: 2018-1-22 09:08:52 4 | :: 用法:双击运行,或者当前路径 cmd 直接输入 release.bat 5 | 6 | :: 关闭回显 7 | @echo OFF 8 | 9 | ECHO "============================= RELEASE START..." 10 | 11 | :: 版本号信息(需要手动指定) 12 | :::: 旧版本名称 13 | SET version=2.0.7 14 | :::: 新版本名称 15 | SET newVersion=2.0.8 16 | :::: 组织名称 17 | SET groupName=com.github.houbb 18 | :::: 项目名称 19 | SET projectName=junitperf 20 | 21 | :: release 项目版本 22 | :::: snapshot 版本号 23 | SET snapshot_version=%version%"-SNAPSHOT" 24 | :::: 新的版本号 25 | SET release_version=%version% 26 | 27 | call mvn versions:set -DgroupId=%groupName% -DartifactId=%projectName% -DoldVersion=%snapshot_version% -DnewVersion=%release_version% 28 | call mvn -N versions:update-child-modules 29 | call mvn versions:commit 30 | call echo "1. RELEASE %snapshot_version% TO %release_version% DONE." 31 | 32 | :: 推送到 github 33 | git add . 34 | git commit -m "release branch %version%" 35 | git push 36 | git status 37 | 38 | ECHO "2. PUSH TO GITHUB DONE." 39 | 40 | :: 发布到 mvn 中央仓库 41 | mvn clean deploy -P release 42 | 43 | ECHO "3. PUSH TO MAVEN CENTER DONE." 44 | 45 | :: 合并到 master 分支 46 | :::: 分支名称 47 | ::SET branchName="release_"%version% 48 | ::git checkout master 49 | ::git pull 50 | ::git checkout %branchName% 51 | ::git rebase master 52 | ::git checkout master 53 | ::git merge %branchName% 54 | ::git push 55 | :: 56 | ::ECHO "3. MERGE TO MASTER DONE." 57 | 58 | 59 | :::: 拉取新的分支 60 | ::SET newBranchName="release_"%newVersion% 61 | ::git branch %newBranchName% 62 | ::git checkout %newBranchName% 63 | ::git push --set-upstream origin %newBranchName% 64 | :: 65 | ::ECHO "4. NEW BRANCH DONE." 66 | :: 67 | :::: 修改新分支的版本号 68 | ::SET snapshot_new_version=%newVersion%"-SNAPSHOT" 69 | ::call mvn versions:set -DgroupId=%groupName% -DartifactId=%projectName% -DoldVersion=%release_version% -DnewVersion=%snapshot_new_version% 70 | ::call mvn -N versions:update-child-modules 71 | ::call mvn versions:commit 72 | :: 73 | ::git add . 74 | ::git commit -m "modify branch %release_version% TO %snapshot_new_version%" 75 | ::git push 76 | ::git status 77 | ::ECHO "5. MODIFY %release_version% TO %snapshot_new_version% DONE." 78 | :: 79 | ::ECHO "============================= RELEASE END..." 80 | 81 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "============================= RELEASE START..." 3 | 4 | ## 版本号信息(需要手动指定) 5 | oldVersion="2.0.1" 6 | newVersion="2.0.2" 7 | projectName="junitperf" 8 | 9 | # release 项目版本 10 | ## snapshot 版本号 11 | snapshot_version=${oldVersion}"-SNAPSHOT" 12 | ## 新的版本号 13 | release_version=${oldVersion} 14 | 15 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${snapshot_version} -DnewVersion=${release_version} 16 | mvn -N versions:update-child-modules 17 | mvn versions:commit 18 | echo "1. RELEASE ${snapshot_version} TO ${release_version} DONE." 19 | 20 | 21 | # 推送到 github 22 | git add . 23 | git commit -m "release branch ${oldVersion}" 24 | git push 25 | git status 26 | 27 | echo "2. PUSH TO GITHUB DONE." 28 | 29 | 30 | # 推送到 maven 中央仓库 31 | mvn clean deploy -P release 32 | 33 | echo "3. PUSH TO MAVEN CENTER DONE." 34 | 35 | # 合并到 master 分支 36 | branchName="release_"${oldVersion} # 分支名称 37 | git checkout master 38 | git pull 39 | git checkout ${branchName} 40 | git rebase master 41 | git checkout master 42 | git merge ${branchName} 43 | git push 44 | 45 | echo "4. MERGE TO MASTER DONE." 46 | 47 | 48 | # 拉取新的分支 49 | newBranchName="release_"${newVersion} 50 | git branch ${newBranchName} 51 | git checkout ${newBranchName} 52 | git push --set-upstream origin ${newBranchName} 53 | 54 | echo "5. NEW BRANCH DONE." 55 | 56 | # 修改新分支的版本号 57 | ## snapshot 版本号 58 | snapshot_new_version=${newVersion}"-SNAPSHOT" 59 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${release_version} -DnewVersion=${snapshot_new_version} 60 | mvn -N versions:update-child-modules 61 | mvn versions:commit 62 | 63 | git add . 64 | git commit -m "modify branch ${release_version} TO ${snapshot_new_version}" 65 | git push 66 | git status 67 | echo "6. MODIFY ${release_version} TO ${snapshot_new_version} DONE." 68 | 69 | echo "============================= RELEASE END..." 70 | 71 | 72 | # 使用方式: 73 | # 1. 赋值权限: chmod +x ./release.sh 74 | # 2. 执行: ./release.sh 75 | # Last Update Time: 2018-01-20 12:07:34 76 | # Author: houbb 77 | 78 | 79 | -------------------------------------------------------------------------------- /release_rm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "============================= RELEASE START..." 3 | 4 | ## 版本号信息(需要手动指定) 5 | oldVersion="2.0.2" 6 | newVersion="2.0.2" 7 | projectName="junitperf" 8 | 9 | # 删除分支 10 | oldBranchName="release_"${oldVersion} 11 | git branch -d ${oldBranchName} 12 | git push origin --delete ${oldBranchName} 13 | 14 | echo "1. Branch remove success..." 15 | 16 | # 拉取新的分支 17 | newBranchName="release_"${newVersion} 18 | git branch ${newBranchName} 19 | git checkout ${newBranchName} 20 | git push --set-upstream origin ${newBranchName} 21 | 22 | echo "2. NEW BRANCH DONE." 23 | 24 | # 修改新分支的版本号 25 | ## snapshot 版本号 26 | snapshot_new_version=${newVersion}"-SNAPSHOT" 27 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${release_version} -DnewVersion=${snapshot_new_version} 28 | mvn -N versions:update-child-modules 29 | mvn versions:commit 30 | 31 | git add . 32 | git commit -m "modify branch ${release_version} TO ${snapshot_new_version}" 33 | git push 34 | git status 35 | echo "3. MODIFY ${release_version} TO ${snapshot_new_version} DONE." 36 | 37 | echo "============================= BRANCH RE-CREATE END..." 38 | 39 | 40 | # 使用方式: 41 | # 注意:本脚本用于删除分支,谨慎使用! 42 | # 1. 赋值权限: chmod +x ./release_rm.sh 43 | # 2. 执行: ./release_rm.sh 44 | # Last Update Time: 2018-06-21 11:10:42 45 | # Author: houbb 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/constant/VersionConstant.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.constant; 2 | 3 | import org.apiguardian.api.API; 4 | 5 | /** 6 | *

版本常量

7 | * 8 | *
 Created: 2018/6/28 下午4:56  
9 | *
 Project: junitperf  
10 | * 11 | * @author houbinbin 12 | * @version 1.0 13 | * @since JDK 1.7 14 | */ 15 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 16 | public final class VersionConstant { 17 | 18 | private VersionConstant(){} 19 | 20 | /** 21 | * V 2.0.0 22 | */ 23 | public static final String V2_0_0 = "2_0_0"; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/constant/enums/StatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.constant.enums; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | 5 | import org.apiguardian.api.API; 6 | 7 | /** 8 | * 状态枚举 9 | * @author bbhou 10 | * @version 1.0.0 11 | * @since 1.0.0, 2018/01/15 12 | */ 13 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 14 | public enum StatusEnum { 15 | 16 | /** 17 | * 通过 18 | */ 19 | PASSED("PASSED"), 20 | /** 21 | * 失败 22 | */ 23 | FAILED("FAILED") 24 | ; 25 | 26 | /** 27 | * 状态 28 | */ 29 | private String status; 30 | 31 | StatusEnum(String status) { 32 | this.status = status; 33 | } 34 | 35 | public String getStatus() { 36 | return status; 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/annotation/JunitPerfConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.annotation; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | import com.github.houbb.junitperf.core.jupiter.provider.PerfConfigProvider; 5 | import com.github.houbb.junitperf.core.report.Reporter; 6 | import com.github.houbb.junitperf.core.report.impl.ConsoleReporter; 7 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 8 | import com.github.houbb.junitperf.core.statistics.impl.DefaultStatisticsCalculator; 9 | import org.apiguardian.api.API; 10 | import org.junit.jupiter.api.TestTemplate; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | 13 | import java.lang.annotation.*; 14 | 15 | /** 16 | * 执行接口 17 | * 对于每一个测试方法的条件配置 18 | * @author bbhou 19 | * @version 1.0.0 20 | * @since 1.0.0 21 | */ 22 | @Retention(RetentionPolicy.RUNTIME) 23 | @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) 24 | @Documented 25 | @API(status = API.Status.MAINTAINED, since = VersionConstant.V2_0_0) 26 | @ExtendWith(PerfConfigProvider.class) 27 | @TestTemplate 28 | public @interface JunitPerfConfig { 29 | 30 | /** 31 | * 执行时使用多少线程执行 32 | * @return int val 33 | * @since 1.0.0 34 | */ 35 | int threads() default 1; 36 | 37 | /** 38 | * 准备时间(单位:毫秒) 39 | * @return time in mills 40 | * @since 1.0.0 41 | */ 42 | long warmUp() default 0L; 43 | 44 | /** 45 | * 执行时间。(单位:毫秒) 46 | * 默认值:默认为 1min 47 | * 这里的执行时间不包含准备时间。 48 | * @return time in mills 49 | * @since 1.0.0 50 | */ 51 | long duration() default 60_000L; 52 | 53 | /** 54 | * 存放统计信息工具 55 | * @return 统计实现类 56 | * @since 1.0.0 57 | */ 58 | Class statistics() default DefaultStatisticsCalculator.class; 59 | 60 | /** 61 | * 存放报告信息类 62 | * @return 报告信息 63 | * @since 1.0.0 64 | */ 65 | Class[] reporter() default {ConsoleReporter.class}; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/annotation/JunitPerfRequire.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.annotation; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | import org.apiguardian.api.API; 5 | 6 | import java.lang.annotation.*; 7 | 8 | /** 9 | * 对于性能测试来说,所有的要求都应该是消耗时间越低越好。 10 | * 这里基本是一些数学的统计概念:最大值、最小值、平均数等等。 11 | * 这些东西都是一个阈值的概念,可以进行抽象吗? 12 | * @author bbhou 13 | * @version 1.0.0 14 | * @since 1.0.0 15 | */ 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) 18 | @Documented 19 | @API(status = API.Status.MAINTAINED, since = VersionConstant.V2_0_0) 20 | public @interface JunitPerfRequire { 21 | 22 | /** 23 | * 最佳的运行耗时(单位:毫秒) 24 | * 1. 最快的运行耗时如果高于这个值,则视为失败 25 | * @return 最佳的运行耗时 26 | */ 27 | float min() default -1L; 28 | 29 | /** 30 | * 最坏的运行耗时(单位:毫秒) 31 | * 1. 最坏的运行耗时如果高于这个值,则视为失败 32 | * @return 最坏的运行耗时 33 | */ 34 | float max() default -1L; 35 | 36 | /** 37 | * 平均的运行耗时(单位:毫秒) 38 | * 1. 平均的运行耗时如果高于这个值,则视为失败 39 | * @return 平均的运行耗时 40 | */ 41 | float average() default -1L; 42 | 43 | /** 44 | * 对于执行耗时的限定 45 | * 46 | * percentiles={"20:220", "30:250"} 47 | * 20% 的数据执行耗时不得超过 220ms; 48 | * 30% 的数据执行耗时不得超过 250ms; 49 | * @return 执行耗时界定的数组 50 | */ 51 | String[] percentiles() default {}; 52 | 53 | /** 54 | * 每秒的最小执行次数 55 | * 1. 如果低于这个最小执行次数,则视为失败 56 | * @return 每秒的最小执行次数 57 | */ 58 | int timesPerSecond() default 0; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/jupiter/context/PerfConfigContext.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.jupiter.context; 2 | 3 | import com.github.houbb.heaven.util.util.DateUtil; 4 | import com.github.houbb.junitperf.constant.VersionConstant; 5 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 6 | import com.github.houbb.junitperf.core.annotation.JunitPerfRequire; 7 | import com.github.houbb.junitperf.core.report.Reporter; 8 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 9 | import com.github.houbb.junitperf.model.evaluation.EvaluationContext; 10 | import com.github.houbb.junitperf.support.exception.JunitPerfRuntimeException; 11 | import com.github.houbb.junitperf.support.statements.PerformanceEvaluationStatement; 12 | 13 | import org.apiguardian.api.API; 14 | import org.junit.jupiter.api.extension.Extension; 15 | import org.junit.jupiter.api.extension.ExtensionContext; 16 | import org.junit.jupiter.api.extension.TestInstancePostProcessor; 17 | import org.junit.jupiter.api.extension.TestTemplateInvocationContext; 18 | 19 | import java.lang.reflect.Method; 20 | import java.util.*; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | 23 | /** 24 | *

配置上下文

25 | * 26 | *
 Created: 2018/6/28 下午5:01  
27 | *
 Project: junitperf  
28 | * 29 | * @author houbinbin 30 | * @version 2.0.0 31 | * @since 2.0.0 32 | */ 33 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 34 | public class PerfConfigContext implements TestTemplateInvocationContext { 35 | 36 | /** 37 | * 用于存储上下文 38 | */ 39 | private static final ConcurrentHashMap> ACTIVE_CONTEXTS = new ConcurrentHashMap<>(); 40 | private final Method method; 41 | private JunitPerfConfig perfConfig; 42 | private JunitPerfRequire perfRequire; 43 | 44 | 45 | public PerfConfigContext(ExtensionContext context) { 46 | this.method = context.getRequiredTestMethod(); 47 | this.perfConfig = method.getAnnotation(JunitPerfConfig.class); 48 | this.perfRequire = method.getAnnotation(JunitPerfRequire.class); 49 | } 50 | 51 | @Override 52 | public List getAdditionalExtensions() { 53 | return Collections.singletonList( 54 | (TestInstancePostProcessor) (testInstance, context) -> { 55 | final Class clazz = testInstance.getClass(); 56 | // Group test contexts by test class 57 | ACTIVE_CONTEXTS.putIfAbsent(clazz, new ArrayList<>()); 58 | 59 | EvaluationContext evaluationContext = new EvaluationContext(testInstance, 60 | method, 61 | DateUtil.getCurrentDateTimeStr()); 62 | evaluationContext.loadConfig(perfConfig); 63 | evaluationContext.loadRequire(perfRequire); 64 | StatisticsCalculator statisticsCalculator = perfConfig.statistics().newInstance(); 65 | Set reporterSet = getReporterSet(); 66 | ACTIVE_CONTEXTS.get(clazz).add(evaluationContext); 67 | try { 68 | new PerformanceEvaluationStatement(evaluationContext, 69 | statisticsCalculator, 70 | reporterSet, 71 | ACTIVE_CONTEXTS.get(clazz), 72 | clazz).evaluate(); 73 | } catch (Throwable throwable) { 74 | throw new JunitPerfRuntimeException(throwable); 75 | } 76 | } 77 | ); 78 | } 79 | 80 | /** 81 | * 获取报告集合 82 | * 83 | * @return 报告集合 84 | */ 85 | private Set getReporterSet() { 86 | Set reporterSet = new HashSet<>(); 87 | Class[] reporters = perfConfig.reporter(); 88 | for (Class clazz : reporters) { 89 | try { 90 | Reporter reporter = (Reporter) clazz.newInstance(); 91 | reporterSet.add(reporter); 92 | } catch (InstantiationException | IllegalAccessException e) { 93 | throw new JunitPerfRuntimeException(e); 94 | } 95 | } 96 | return reporterSet; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/jupiter/provider/PerfConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.jupiter.provider; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | import com.github.houbb.junitperf.core.jupiter.context.PerfConfigContext; 5 | 6 | import org.apiguardian.api.API; 7 | import org.junit.jupiter.api.extension.ExtensionContext; 8 | import org.junit.jupiter.api.extension.TestTemplateInvocationContext; 9 | import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; 10 | import org.junit.platform.commons.support.AnnotationSupport; 11 | 12 | import java.util.stream.Stream; 13 | 14 | /** 15 | *

配置实现

16 | * 17 | *
 Created: 2018/6/28 下午5:53  
18 | *
 Project: junitperf  
19 | * 20 | * @author houbinbin 21 | * @version 1.0 22 | * @since JDK 1.7 23 | */ 24 | @API(status = API.Status.INTERNAL) 25 | public class PerfConfigProvider implements TestTemplateInvocationContextProvider { 26 | 27 | @Override 28 | public boolean supportsTestTemplate(ExtensionContext context) { 29 | return context.getTestMethod() 30 | .filter(m -> AnnotationSupport.isAnnotated(m, JunitPerfConfig.class)) 31 | .isPresent(); 32 | } 33 | 34 | @Override 35 | public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { 36 | return Stream.of(new PerfConfigContext(context)); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/report/Reporter.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.report; 2 | 3 | 4 | import com.github.houbb.junitperf.constant.VersionConstant; 5 | import com.github.houbb.junitperf.model.evaluation.EvaluationContext; 6 | 7 | import org.apiguardian.api.API; 8 | 9 | import java.util.Collection; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | /** 14 | * 报告接口 15 | * @author bbhou 16 | * @version 1.0.0 17 | * @since 1.0.0, 2018/01/11 18 | */ 19 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 20 | public interface Reporter { 21 | 22 | /** 23 | * 用于生成报告 24 | * @param testClass 测试类 class 相关信息 25 | * @param evaluationContextSet 评价集合 26 | */ 27 | void report(Class testClass, Collection evaluationContextSet); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/report/impl/ConsoleReporter.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.report.impl; 2 | 3 | import com.github.houbb.heaven.util.lang.ConsoleUtil; 4 | import com.github.houbb.junitperf.constant.VersionConstant; 5 | import com.github.houbb.junitperf.constant.enums.StatusEnum; 6 | import com.github.houbb.junitperf.core.report.Reporter; 7 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 8 | import com.github.houbb.junitperf.model.evaluation.EvaluationContext; 9 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationConfig; 10 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationRequire; 11 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationResult; 12 | import org.apiguardian.api.API; 13 | 14 | import java.util.Collection; 15 | import java.util.Map; 16 | 17 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 18 | 19 | /** 20 | * 命令行报告 21 | * 描述:将统计结果输出到命令行。 22 | * 23 | * @author bbhou 24 | * @version 1.0.0 25 | * @since 1.0.0, 2018/01/11 26 | */ 27 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 28 | public class ConsoleReporter implements Reporter { 29 | 30 | @Override 31 | public void report(Class testClass, Collection evaluationContextSet) { 32 | for (EvaluationContext context : evaluationContextSet) { 33 | 34 | StatisticsCalculator statistics = context.getStatisticsCalculator(); 35 | EvaluationConfig evaluationConfig = context.getEvaluationConfig(); 36 | EvaluationRequire evaluationRequire = context.getEvaluationRequire(); 37 | EvaluationResult evaluationResult = context.getEvaluationResult(); 38 | 39 | String throughputStatus = getStatus(evaluationResult.isTimesPerSecondAchieved()); 40 | 41 | infoLog(context, ConsoleUtil.LINE); 42 | infoLog(context, "Started at: {}", context.getStartTime()); 43 | infoLog(context, "Invocations: {}", statistics.getEvaluationCount()); 44 | infoLog(context,"Success: {}", statistics.getEvaluationCount() - statistics.getErrorCount()); 45 | infoLog(context,"Errors: {}", statistics.getErrorCount()); 46 | infoLog(context,"Thread Count: {}", evaluationConfig.getConfigThreads()); 47 | infoLog(context,"Warm up: {}ms", evaluationConfig.getConfigWarmUp()); 48 | infoLog(context,"Execution time: {}ms", evaluationConfig.getConfigDuration()); 49 | infoLog(context,"Throughput: {}/s (Required: {}/s) - {}", 50 | evaluationResult.getThroughputQps(), 51 | evaluationRequire.getRequireTimesPerSecond(), 52 | throughputStatus); 53 | // 内存 54 | infoLog(context,"Memory cost: {}byte", statistics.getMemory()); 55 | 56 | infoLog(context,"Min latency: {}ms (Required: {}ms) - {}", 57 | statistics.getMinLatency(MILLISECONDS), 58 | evaluationRequire.getRequireMin(), 59 | getStatus(evaluationResult.isMinAchieved())); 60 | infoLog(context,"Max latency: {}ms (Required: {}ms) - {}", 61 | statistics.getMaxLatency(MILLISECONDS), 62 | evaluationRequire.getRequireMax(), 63 | getStatus(evaluationResult.isMaxAchieved())); 64 | infoLog(context,"Avg latency: {}ms (Required: {}ms) - {}", 65 | statistics.getMeanLatency(MILLISECONDS), 66 | evaluationRequire.getRequireAverage(), 67 | getStatus(evaluationResult.isAverageAchieved())); 68 | 69 | for (Map.Entry entry : evaluationRequire.getRequirePercentilesMap().entrySet()) { 70 | Integer percentile = entry.getKey(); 71 | Float threshold = entry.getValue(); 72 | boolean result = evaluationResult.getIsPercentilesAchievedMap().get(percentile); 73 | String percentileStatus = getStatus(result); 74 | infoLog(context,"Percentile: {}%% {}ms (Required: {}ms) - {}", 75 | percentile, 76 | statistics.getLatencyPercentile(percentile, MILLISECONDS), 77 | threshold, 78 | percentileStatus); 79 | 80 | } 81 | infoLog(context, ConsoleUtil.LINE); 82 | } 83 | } 84 | 85 | /** 86 | * 日志输出 87 | * @param context 上下文 88 | * @param format 格式化 89 | * @param args 参数 90 | * @since 2.0.6 91 | */ 92 | private void infoLog(final EvaluationContext context, 93 | final String format, 94 | final Object... args) { 95 | String className = context.getTestInstance().getClass().getName(); 96 | String methodName = context.getMethodName(); 97 | 98 | ConsoleUtil.info(className, methodName, format, args); 99 | } 100 | 101 | /** 102 | * 获取状态 103 | * @param isSuccess 是否成功 104 | * @return 显示状态字符串 105 | */ 106 | private String getStatus(boolean isSuccess) { 107 | if (isSuccess) { 108 | return StatusEnum.PASSED.getStatus(); 109 | } 110 | return StatusEnum.FAILED.getStatus(); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/report/impl/HtmlReporter.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.report.impl; 2 | 3 | import com.github.houbb.heaven.util.lang.ConsoleUtil; 4 | import com.github.houbb.heaven.util.nio.PathUtil; 5 | import com.github.houbb.junitperf.constant.VersionConstant; 6 | import com.github.houbb.junitperf.core.report.Reporter; 7 | import com.github.houbb.junitperf.model.evaluation.EvaluationContext; 8 | import com.github.houbb.junitperf.support.i18n.I18N; 9 | import com.github.houbb.junitperf.util.FreemarkerUtil; 10 | import freemarker.template.Configuration; 11 | import freemarker.template.Template; 12 | 13 | import org.apiguardian.api.API; 14 | 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.nio.file.Paths; 18 | import java.util.Collection; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /** 24 | * 网页报告 25 | * 描述:将统计结果输出HTML页面,打算将此种方式作为默认的报告方式。 26 | * @author bbhou 27 | * @version 1.0.0 28 | * @since 1.0.0, 2018/01/11 29 | */ 30 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 31 | public class HtmlReporter implements Reporter { 32 | 33 | /** 34 | * 默认输出文件夹 35 | */ 36 | private static final String DEFAULT_REPORT_PACKAGE = System.getProperty("user.dir") + "/target/junitperf/reports/"; 37 | /** 38 | * 模板文件夹 39 | */ 40 | private static final String REPORT_TEMPLATE = "/templates/"; 41 | 42 | @Override 43 | public void report(Class testClass, Collection evaluationContextSet) { 44 | Path outputPath = Paths.get(DEFAULT_REPORT_PACKAGE + PathUtil.packageToPath(testClass.getName())+".html"); 45 | try { 46 | Configuration configuration = FreemarkerUtil.getConfiguration("UTF-8"); 47 | configuration.setClassForTemplateLoading(FreemarkerUtil.class, 48 | REPORT_TEMPLATE); 49 | 50 | Template template = configuration.getTemplate("report.ftl"); 51 | Files.createDirectories(outputPath.getParent()); 52 | ConsoleUtil.info("Rendering report to: " + outputPath); 53 | 54 | Map root = new HashMap<>(); 55 | root.put("className", testClass.getSimpleName()); 56 | root.put("contextData", evaluationContextSet); 57 | root.put("milliseconds", TimeUnit.MILLISECONDS); 58 | root.put("i18n", I18N.buildI18nVo()); 59 | FreemarkerUtil.createFile(template, outputPath.toString(), root, true); 60 | } catch (Exception e) { 61 | ConsoleUtil.info("HtmlReporter meet ex: {}", e, e); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/statistics/StatisticsCalculator.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.statistics; 2 | 3 | import org.apiguardian.api.API; 4 | 5 | import java.io.Serializable; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * 统计计算接口 10 | * 备注:所有的实现需要提供无参数构造器 11 | * @author bbhou 12 | * @version 1.0.0 13 | * @since 1.0.0, 2018/01/11 14 | */ 15 | @API(status = API.Status.INTERNAL) 16 | public interface StatisticsCalculator extends Serializable { 17 | 18 | /** 19 | * 累加延迟的时间 20 | * @param executionTimeNs 执行时间(纳秒) 21 | */ 22 | void addLatencyMeasurement(long executionTimeNs); 23 | 24 | /** 25 | * 增加错误总次数 26 | */ 27 | void incrementErrorCount(); 28 | 29 | /** 30 | * 获取错误总次数 31 | * @return long 32 | */ 33 | long getErrorCount(); 34 | 35 | /** 36 | * 获取错误的百分比 37 | * @return float 38 | */ 39 | float getErrorPercentage(); 40 | 41 | /** 42 | * 增加校验的总数 43 | */ 44 | void incrementEvaluationCount(); 45 | 46 | /** 47 | * 获取校验的总数 48 | * @return long 49 | */ 50 | long getEvaluationCount(); 51 | 52 | /** 53 | * 获取延迟百分比 54 | * @param percentile 百分比 55 | * @param unit 时间单位 56 | * @return 延迟百分比 57 | */ 58 | float getLatencyPercentile(int percentile, TimeUnit unit); 59 | 60 | /** 61 | * 获取最大延迟 62 | * @param unit 时间单位 63 | * @return 最大延迟 64 | */ 65 | float getMaxLatency(TimeUnit unit); 66 | 67 | /** 68 | * 获取最小延迟 69 | * @param unit 时间单位 70 | * @return 最小延迟 71 | */ 72 | float getMinLatency(TimeUnit unit); 73 | 74 | /** 75 | * 获取平均延迟 76 | * @param unit 时间单位 77 | * @return 平均延迟 78 | */ 79 | float getMeanLatency(TimeUnit unit); 80 | 81 | /** 82 | * 添加内存 83 | * @param memoryKb 内存消耗 84 | * @since 2.0.5 85 | */ 86 | void setMemory(final long memoryKb); 87 | 88 | /** 89 | * 获取内存的消耗 90 | * @return 内存消耗 91 | * @since 2.0.5 92 | */ 93 | long getMemory(); 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/core/statistics/impl/DefaultStatisticsCalculator.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.core.statistics.impl; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 5 | import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; 6 | import org.apache.commons.math3.stat.descriptive.SynchronizedDescriptiveStatistics; 7 | import org.apiguardian.api.API; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicLong; 11 | 12 | /** 13 | * 默认统计计算 14 | * @author bbhou 15 | * @version 1.0.0 16 | * @since 1.0.0, 2018/01/11 17 | */ 18 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 19 | public class DefaultStatisticsCalculator implements StatisticsCalculator { 20 | 21 | private static final long serialVersionUID = 3715867392352544936L; 22 | 23 | //region private fields 24 | /** 25 | * 耗时统计方式 26 | * @since 1.0.0 27 | */ 28 | private final DescriptiveStatistics latencyStatistics; 29 | 30 | /** 31 | * 执行评价计数 32 | */ 33 | private final AtomicLong evaluationCount = new AtomicLong(); 34 | 35 | /** 36 | * 错误计数 37 | */ 38 | private final AtomicLong errorCount = new AtomicLong(); 39 | 40 | /** 41 | * 内存消耗 42 | * @since 2.0.5 43 | */ 44 | private volatile long memoryKb; 45 | 46 | //endregion 47 | 48 | //region constructor 49 | /** 50 | * 默认构造器 51 | * @since 2.0.5 52 | */ 53 | public DefaultStatisticsCalculator() { 54 | this(new SynchronizedDescriptiveStatistics()); 55 | } 56 | 57 | /** 58 | * 私有构造器 59 | * @param latencyStatistics 延迟统计 60 | * @since 1.0.0 61 | */ 62 | private DefaultStatisticsCalculator(final DescriptiveStatistics latencyStatistics) { 63 | this.latencyStatistics = latencyStatistics; 64 | } 65 | //endregion 66 | 67 | //region methods 68 | @Override 69 | public void addLatencyMeasurement(long executionTimeNs) { 70 | latencyStatistics.addValue(executionTimeNs); 71 | } 72 | 73 | @Override 74 | public void incrementErrorCount() { 75 | errorCount.incrementAndGet(); 76 | } 77 | 78 | @Override 79 | public long getErrorCount() { 80 | return errorCount.get(); 81 | } 82 | 83 | @Override 84 | public float getErrorPercentage() { 85 | return ((float)getErrorCount() / getEvaluationCount()) * 100; 86 | } 87 | 88 | @Override 89 | public void incrementEvaluationCount() { 90 | evaluationCount.incrementAndGet(); 91 | } 92 | 93 | @Override 94 | public long getEvaluationCount() { 95 | return evaluationCount.get(); 96 | } 97 | 98 | @Override 99 | public float getLatencyPercentile(int percentile, TimeUnit unit) { 100 | return (float) latencyStatistics.getPercentile((double)(percentile)) / unit.toNanos(1); 101 | } 102 | 103 | @Override 104 | public float getMaxLatency(TimeUnit unit) { 105 | return (float) latencyStatistics.getMax() / unit.toNanos(1); 106 | } 107 | 108 | @Override 109 | public float getMinLatency(TimeUnit unit) { 110 | return (float) latencyStatistics.getMin() / unit.toNanos(1); 111 | } 112 | 113 | @Override 114 | public float getMeanLatency(TimeUnit unit) { 115 | return (float) latencyStatistics.getMean() / unit.toNanos(1); 116 | } 117 | 118 | @Override 119 | public void setMemory(long memoryKb) { 120 | this.memoryKb = memoryKb; 121 | } 122 | 123 | @Override 124 | public long getMemory() { 125 | return this.memoryKb; 126 | } 127 | //endregion 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/model/BaseModel.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.model; 2 | 3 | import org.apiguardian.api.API; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 基础 model 类 9 | * @author bbhou 10 | * @version 1.0.1 11 | * @since 1.0.1, 2018/01/15 12 | */ 13 | @API(status = API.Status.INTERNAL) 14 | public class BaseModel implements Serializable { 15 | 16 | private static final long serialVersionUID = 8216424549704594536L; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/model/evaluation/EvaluationContext.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.model.evaluation; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 5 | import com.github.houbb.junitperf.core.annotation.JunitPerfRequire; 6 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 7 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationConfig; 8 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationRequire; 9 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationResult; 10 | import com.github.houbb.junitperf.support.builder.EvaluationConfigBuilder; 11 | import com.github.houbb.junitperf.support.builder.EvaluationRequireBuilder; 12 | import com.github.houbb.junitperf.support.builder.EvaluationResultBuilder; 13 | 14 | import org.apiguardian.api.API; 15 | 16 | import java.io.Serializable; 17 | import java.lang.reflect.Method; 18 | 19 | /** 20 | * 评价接口定义,用于展现最后的性能评价结果。 21 | * 所有的结果直接继承此接口,可用于生成对应的报告信息。 22 | * 备注:我觉得这部分的代码写的不合理,应该拆分为贫血模型。 23 | * @author bbhou 24 | * @version 1.0.0 25 | * @since 1.0.0, 2018/01/11 26 | */ 27 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 28 | public class EvaluationContext implements Serializable { 29 | 30 | private static final long serialVersionUID = -3314188451986878388L; 31 | 32 | /** 33 | * 测试实例 34 | */ 35 | private final Object testInstance; 36 | 37 | /** 38 | * 测试方法 39 | */ 40 | private final Method testMethod; 41 | 42 | /** 43 | * 测试方法名称 44 | */ 45 | private final String methodName; 46 | 47 | /** 48 | * 开始时间 49 | */ 50 | private final String startTime; 51 | 52 | /** 53 | * 统计者 54 | */ 55 | private StatisticsCalculator statisticsCalculator; 56 | 57 | /** 58 | * 配置 59 | */ 60 | private EvaluationConfig evaluationConfig; 61 | 62 | /** 63 | * 限定 64 | */ 65 | private EvaluationRequire evaluationRequire; 66 | 67 | /** 68 | * 结果 69 | */ 70 | private EvaluationResult evaluationResult; 71 | 72 | 73 | public EvaluationContext(final Object testInstance, 74 | final Method testMethod, 75 | String startTime) { 76 | this.testInstance = testInstance; 77 | this.testMethod = testMethod; 78 | this.methodName = testMethod.getName(); 79 | this.startTime = startTime; 80 | } 81 | 82 | /** 83 | * 加载配置 84 | * @param junitPerfConfig 配置注解 85 | */ 86 | public synchronized void loadConfig(JunitPerfConfig junitPerfConfig) { 87 | this.evaluationConfig = new EvaluationConfigBuilder(junitPerfConfig).build(); 88 | } 89 | 90 | /** 91 | * 加载评判标准 92 | * @param junitPerfRequire 评判注解 93 | */ 94 | public synchronized void loadRequire(JunitPerfRequire junitPerfRequire) { 95 | this.evaluationRequire = new EvaluationRequireBuilder(junitPerfRequire).build(); 96 | } 97 | 98 | /** 99 | * 运行校验 100 | * 1. 必须保证统计这一步已经做完了 101 | */ 102 | public void runValidation() { 103 | evaluationResult = new EvaluationResultBuilder(evaluationConfig, evaluationRequire, statisticsCalculator).build(); 104 | } 105 | 106 | public String getMethodName() { 107 | return methodName; 108 | } 109 | 110 | public String getStartTime() { 111 | return startTime; 112 | } 113 | 114 | public StatisticsCalculator getStatisticsCalculator() { 115 | return statisticsCalculator; 116 | } 117 | 118 | public void setStatisticsCalculator(StatisticsCalculator statisticsCalculator) { 119 | this.statisticsCalculator = statisticsCalculator; 120 | } 121 | 122 | public EvaluationConfig getEvaluationConfig() { 123 | return evaluationConfig; 124 | } 125 | 126 | public EvaluationRequire getEvaluationRequire() { 127 | return evaluationRequire; 128 | } 129 | 130 | public EvaluationResult getEvaluationResult() { 131 | return evaluationResult; 132 | } 133 | 134 | public Object getTestInstance() { 135 | return testInstance; 136 | } 137 | 138 | public Method getTestMethod() { 139 | return testMethod; 140 | } 141 | 142 | @Override 143 | public boolean equals(Object o) { 144 | if (this == o) { 145 | return true; 146 | } 147 | if (o == null || getClass() != o.getClass()) { 148 | return false; 149 | } 150 | 151 | EvaluationContext that = (EvaluationContext) o; 152 | 153 | if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) { 154 | return false; 155 | } 156 | if (startTime != null ? !startTime.equals(that.startTime) : that.startTime != null) { 157 | return false; 158 | } 159 | if (statisticsCalculator != null ? !statisticsCalculator.equals(that.statisticsCalculator) : that.statisticsCalculator != null) { 160 | return false; 161 | } 162 | if (evaluationConfig != null ? !evaluationConfig.equals(that.evaluationConfig) : that.evaluationConfig != null) { 163 | return false; 164 | } 165 | if (evaluationRequire != null ? !evaluationRequire.equals(that.evaluationRequire) : that.evaluationRequire != null) { 166 | return false; 167 | } 168 | return evaluationResult != null ? evaluationResult.equals(that.evaluationResult) : that.evaluationResult == null; 169 | } 170 | 171 | @Override 172 | public int hashCode() { 173 | int result = methodName != null ? methodName.hashCode() : 0; 174 | result = 31 * result + (startTime != null ? startTime.hashCode() : 0); 175 | result = 31 * result + (statisticsCalculator != null ? statisticsCalculator.hashCode() : 0); 176 | result = 31 * result + (evaluationConfig != null ? evaluationConfig.hashCode() : 0); 177 | result = 31 * result + (evaluationRequire != null ? evaluationRequire.hashCode() : 0); 178 | result = 31 * result + (evaluationResult != null ? evaluationResult.hashCode() : 0); 179 | return result; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/model/evaluation/component/EvaluationConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.model.evaluation.component; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | import com.github.houbb.junitperf.model.BaseModel; 5 | 6 | import org.apiguardian.api.API; 7 | 8 | /** 9 | * 验证配置 10 | * @author bbhou 11 | * @version 1.0.1 12 | * @since 1.0.1, 2018/01/15 13 | */ 14 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 15 | public class EvaluationConfig extends BaseModel { 16 | 17 | private static final long serialVersionUID = 3584449169952751834L; 18 | 19 | /** 20 | * 配置-线程数 21 | */ 22 | private int configThreads; 23 | 24 | /** 25 | * 配置-准备时间 26 | */ 27 | private long configWarmUp; 28 | 29 | /** 30 | * 配置-运行时间 31 | */ 32 | private long configDuration; 33 | 34 | public int getConfigThreads() { 35 | return configThreads; 36 | } 37 | 38 | public void setConfigThreads(int configThreads) { 39 | this.configThreads = configThreads; 40 | } 41 | 42 | public long getConfigWarmUp() { 43 | return configWarmUp; 44 | } 45 | 46 | public void setConfigWarmUp(long configWarmUp) { 47 | this.configWarmUp = configWarmUp; 48 | } 49 | 50 | public long getConfigDuration() { 51 | return configDuration; 52 | } 53 | 54 | public void setConfigDuration(long configDuration) { 55 | this.configDuration = configDuration; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/model/evaluation/component/EvaluationRequire.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.model.evaluation.component; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | import com.github.houbb.junitperf.model.BaseModel; 5 | 6 | import org.apiguardian.api.API; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | * 验证需求 12 | * @author bbhou 13 | * @version 1.0.1 14 | * @since 1.0.1, 2018/01/15 15 | */ 16 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 17 | public class EvaluationRequire extends BaseModel { 18 | 19 | private static final long serialVersionUID = 377391606016334079L; 20 | 21 | /** 22 | * 最小延迟 23 | */ 24 | private float requireMin; 25 | /** 26 | * 最大延迟 27 | */ 28 | private float requireMax; 29 | /** 30 | * 平均延迟 31 | */ 32 | private float requireAverage; 33 | /** 34 | * 每秒运行次数 35 | */ 36 | private int requireTimesPerSecond; 37 | /** 38 | * 百分比测试需求 39 | */ 40 | private Map requirePercentilesMap; 41 | 42 | public float getRequireMin() { 43 | return requireMin; 44 | } 45 | 46 | public void setRequireMin(float requireMin) { 47 | this.requireMin = requireMin; 48 | } 49 | 50 | public float getRequireMax() { 51 | return requireMax; 52 | } 53 | 54 | public void setRequireMax(float requireMax) { 55 | this.requireMax = requireMax; 56 | } 57 | 58 | public float getRequireAverage() { 59 | return requireAverage; 60 | } 61 | 62 | public void setRequireAverage(float requireAverage) { 63 | this.requireAverage = requireAverage; 64 | } 65 | 66 | public int getRequireTimesPerSecond() { 67 | return requireTimesPerSecond; 68 | } 69 | 70 | public void setRequireTimesPerSecond(int requireTimesPerSecond) { 71 | this.requireTimesPerSecond = requireTimesPerSecond; 72 | } 73 | 74 | public Map getRequirePercentilesMap() { 75 | return requirePercentilesMap; 76 | } 77 | 78 | public void setRequirePercentilesMap(Map requirePercentilesMap) { 79 | this.requirePercentilesMap = requirePercentilesMap; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/model/evaluation/component/EvaluationResult.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.model.evaluation.component; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | import com.github.houbb.junitperf.model.BaseModel; 5 | 6 | import org.apiguardian.api.API; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | * 验证结果 12 | * @author bbhou 13 | * @version 1.0.1 14 | * @since 1.0.1, 2018/01/15 15 | */ 16 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 17 | public class EvaluationResult extends BaseModel { 18 | 19 | private static final long serialVersionUID = 3402389144055056153L; 20 | 21 | /** 22 | * 运行速度 QPS 23 | */ 24 | private long throughputQps; 25 | 26 | /** 27 | * 最小延迟是否符合 28 | */ 29 | private boolean isMinAchieved; 30 | /** 31 | * 最大延迟是否符合 32 | */ 33 | private boolean isMaxAchieved; 34 | /** 35 | * 平均延迟是否符合 36 | */ 37 | private boolean isAverageAchieved; 38 | /** 39 | * 每秒执行次数是否符合 40 | */ 41 | private boolean isTimesPerSecondAchieved; 42 | /** 43 | * 百分比阈值结果 44 | */ 45 | private boolean isPercentilesAchieved; 46 | 47 | /** 48 | * 百分比测试结果 49 | */ 50 | private Map isPercentilesAchievedMap; 51 | 52 | /** 53 | * 验证是否成功 54 | * 备注:当所有的校验通过则视为通过 55 | */ 56 | private boolean isSuccessful; 57 | 58 | public long getThroughputQps() { 59 | return throughputQps; 60 | } 61 | 62 | public void setThroughputQps(long throughputQps) { 63 | this.throughputQps = throughputQps; 64 | } 65 | 66 | public boolean isMinAchieved() { 67 | return isMinAchieved; 68 | } 69 | 70 | public void setMinAchieved(boolean minAchieved) { 71 | isMinAchieved = minAchieved; 72 | } 73 | 74 | public boolean isMaxAchieved() { 75 | return isMaxAchieved; 76 | } 77 | 78 | public void setMaxAchieved(boolean maxAchieved) { 79 | isMaxAchieved = maxAchieved; 80 | } 81 | 82 | public boolean isAverageAchieved() { 83 | return isAverageAchieved; 84 | } 85 | 86 | public void setAverageAchieved(boolean averageAchieved) { 87 | isAverageAchieved = averageAchieved; 88 | } 89 | 90 | public boolean isTimesPerSecondAchieved() { 91 | return isTimesPerSecondAchieved; 92 | } 93 | 94 | public void setTimesPerSecondAchieved(boolean timesPerSecondAchieved) { 95 | isTimesPerSecondAchieved = timesPerSecondAchieved; 96 | } 97 | 98 | public boolean isPercentilesAchieved() { 99 | return isPercentilesAchieved; 100 | } 101 | 102 | public void setPercentilesAchieved(boolean percentilesAchieved) { 103 | isPercentilesAchieved = percentilesAchieved; 104 | } 105 | 106 | public boolean isSuccessful() { 107 | return isSuccessful; 108 | } 109 | 110 | public void setSuccessful(boolean successful) { 111 | isSuccessful = successful; 112 | } 113 | 114 | public Map getIsPercentilesAchievedMap() { 115 | return isPercentilesAchievedMap; 116 | } 117 | 118 | public void setIsPercentilesAchievedMap(Map isPercentilesAchievedMap) { 119 | this.isPercentilesAchievedMap = isPercentilesAchievedMap; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/model/vo/I18nVo.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.model.vo; 2 | 3 | import com.github.houbb.junitperf.constant.VersionConstant; 4 | 5 | import org.apiguardian.api.API; 6 | 7 | /** 8 | *

i18n 对象

9 | * 10 | *
 Created: 2018/5/4 下午11:00  
11 | *
 Project: junitperf  
12 | * 13 | * @author houbinbin 14 | * @version 1.0.2 15 | * @since 1.0.2 16 | */ 17 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 18 | public class I18nVo { 19 | 20 | private String junit_performance_report; 21 | private String top; 22 | private String report_created_by; 23 | private String invocations; 24 | private String success; 25 | private String warm_up; 26 | private String thread_count; 27 | private String started_at; 28 | private String execution_time; 29 | private String type; 30 | private String actual; 31 | private String required; 32 | private String throughput; 33 | private String min_latency; 34 | private String max_latency; 35 | private String avg_latency; 36 | 37 | /** 38 | * 内存消耗 39 | * @since 2.0.5 40 | */ 41 | private String memory; 42 | 43 | public String getJunit_performance_report() { 44 | return junit_performance_report; 45 | } 46 | 47 | public void setJunit_performance_report(String junit_performance_report) { 48 | this.junit_performance_report = junit_performance_report; 49 | } 50 | 51 | public String getTop() { 52 | return top; 53 | } 54 | 55 | public void setTop(String top) { 56 | this.top = top; 57 | } 58 | 59 | public String getReport_created_by() { 60 | return report_created_by; 61 | } 62 | 63 | public void setReport_created_by(String report_created_by) { 64 | this.report_created_by = report_created_by; 65 | } 66 | 67 | public String getInvocations() { 68 | return invocations; 69 | } 70 | 71 | public void setInvocations(String invocations) { 72 | this.invocations = invocations; 73 | } 74 | 75 | public String getSuccess() { 76 | return success; 77 | } 78 | 79 | public void setSuccess(String success) { 80 | this.success = success; 81 | } 82 | 83 | public String getWarm_up() { 84 | return warm_up; 85 | } 86 | 87 | public void setWarm_up(String warm_up) { 88 | this.warm_up = warm_up; 89 | } 90 | 91 | public String getThread_count() { 92 | return thread_count; 93 | } 94 | 95 | public void setThread_count(String thread_count) { 96 | this.thread_count = thread_count; 97 | } 98 | 99 | public String getStarted_at() { 100 | return started_at; 101 | } 102 | 103 | public void setStarted_at(String started_at) { 104 | this.started_at = started_at; 105 | } 106 | 107 | public String getExecution_time() { 108 | return execution_time; 109 | } 110 | 111 | public void setExecution_time(String execution_time) { 112 | this.execution_time = execution_time; 113 | } 114 | 115 | public String getType() { 116 | return type; 117 | } 118 | 119 | public void setType(String type) { 120 | this.type = type; 121 | } 122 | 123 | public String getActual() { 124 | return actual; 125 | } 126 | 127 | public void setActual(String actual) { 128 | this.actual = actual; 129 | } 130 | 131 | public String getThroughput() { 132 | return throughput; 133 | } 134 | 135 | public void setThroughput(String throughput) { 136 | this.throughput = throughput; 137 | } 138 | 139 | public String getMin_latency() { 140 | return min_latency; 141 | } 142 | 143 | public void setMin_latency(String min_latency) { 144 | this.min_latency = min_latency; 145 | } 146 | 147 | public String getMax_latency() { 148 | return max_latency; 149 | } 150 | 151 | public void setMax_latency(String max_latency) { 152 | this.max_latency = max_latency; 153 | } 154 | 155 | public String getAvg_latency() { 156 | return avg_latency; 157 | } 158 | 159 | public void setAvg_latency(String avg_latency) { 160 | this.avg_latency = avg_latency; 161 | } 162 | 163 | public String getRequired() { 164 | return required; 165 | } 166 | 167 | public void setRequired(String required) { 168 | this.required = required; 169 | } 170 | 171 | public String getMemory() { 172 | return memory; 173 | } 174 | 175 | public void setMemory(String memory) { 176 | this.memory = memory; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/builder/EvaluationConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.support.builder; 2 | 3 | 4 | import com.github.houbb.heaven.support.builder.IBuilder; 5 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 6 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationConfig; 7 | import org.apiguardian.api.API; 8 | 9 | import static com.google.common.base.Preconditions.checkNotNull; 10 | import static com.google.common.base.Preconditions.checkState; 11 | 12 | /** 13 | * 验证配置-构建者 14 | * @author bbhou 15 | * @version 1.0.1 16 | * @since 1.0.1, 2018/01/15 17 | */ 18 | @API(status = API.Status.INTERNAL) 19 | public class EvaluationConfigBuilder implements IBuilder { 20 | 21 | private final JunitPerfConfig junitPerfConfig; 22 | 23 | public EvaluationConfigBuilder(JunitPerfConfig junitPerfConfig) { 24 | this.junitPerfConfig = junitPerfConfig; 25 | } 26 | 27 | @Override 28 | public EvaluationConfig build() { 29 | validateJunitPerfConfig(junitPerfConfig); 30 | 31 | EvaluationConfig evaluationConfig = new EvaluationConfig(); 32 | evaluationConfig.setConfigThreads(junitPerfConfig.threads()); 33 | evaluationConfig.setConfigWarmUp(junitPerfConfig.warmUp()); 34 | evaluationConfig.setConfigDuration(junitPerfConfig.duration()); 35 | return evaluationConfig; 36 | } 37 | 38 | 39 | /** 40 | * 校验配置属性 41 | * @param junitPerfConfig config 注解信息 42 | */ 43 | private void validateJunitPerfConfig(JunitPerfConfig junitPerfConfig) { 44 | checkNotNull(junitPerfConfig, "JunitPerfConfig must not be null!"); 45 | 46 | int threads = junitPerfConfig.threads(); 47 | long warmUp = junitPerfConfig.warmUp(); 48 | long duration = junitPerfConfig.duration(); 49 | checkState(duration > 0, "duration must be > 0ms."); 50 | checkState(warmUp >= 0, "warmUp must be >= 0ms."); 51 | checkState(warmUp < duration, "warmUp must be < duration."); 52 | checkState(threads > 0, "threads must be > 0."); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/builder/EvaluationRequireBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.support.builder; 2 | 3 | import com.github.houbb.heaven.support.builder.IBuilder; 4 | import com.github.houbb.heaven.util.lang.ObjectUtil; 5 | import com.github.houbb.heaven.util.util.ArrayUtil; 6 | import com.github.houbb.junitperf.core.annotation.JunitPerfRequire; 7 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationRequire; 8 | import com.google.common.collect.Maps; 9 | import com.google.common.primitives.Floats; 10 | import com.google.common.primitives.Ints; 11 | 12 | import org.apiguardian.api.API; 13 | 14 | import java.util.Map; 15 | 16 | import static com.google.common.base.Preconditions.checkState; 17 | 18 | /** 19 | * 验证需求-构建者 20 | * @author bbhou 21 | * @version 1.0.1 22 | * @since 1.0.1, 2018/01/15 23 | */ 24 | @API(status = API.Status.INTERNAL) 25 | public class EvaluationRequireBuilder implements IBuilder { 26 | 27 | private final JunitPerfRequire junitPerfRequire; 28 | 29 | public EvaluationRequireBuilder(JunitPerfRequire junitPerfRequire) { 30 | this.junitPerfRequire = junitPerfRequire; 31 | } 32 | 33 | @Override 34 | public EvaluationRequire build() { 35 | EvaluationRequire evaluationRequire = new EvaluationRequire(); 36 | 37 | if(ObjectUtil.isNotNull(junitPerfRequire)) { 38 | validateJunitPerfRequire(junitPerfRequire); 39 | 40 | evaluationRequire.setRequireMin(junitPerfRequire.min()); 41 | evaluationRequire.setRequireMax(junitPerfRequire.max()); 42 | evaluationRequire.setRequireAverage(junitPerfRequire.average()); 43 | evaluationRequire.setRequireTimesPerSecond(junitPerfRequire.timesPerSecond()); 44 | evaluationRequire.setRequirePercentilesMap(parseRequirePercentilesMap(junitPerfRequire.percentiles())); 45 | } else { 46 | evaluationRequire.setRequireMin(-1); 47 | evaluationRequire.setRequireMax(-1); 48 | evaluationRequire.setRequireAverage(-1); 49 | evaluationRequire.setRequireTimesPerSecond(-1); 50 | //避免NPE 51 | evaluationRequire.setRequirePercentilesMap(Maps.newHashMap()); 52 | } 53 | return evaluationRequire; 54 | } 55 | 56 | /** 57 | * 校验请求属性 58 | * @param junitPerfRequire require 注解信息 59 | */ 60 | private void validateJunitPerfRequire(JunitPerfRequire junitPerfRequire) { 61 | checkState(junitPerfRequire.timesPerSecond() >= 0, "timesPerSecond must be >= 0"); 62 | } 63 | 64 | /** 65 | * 转换需求的 map 66 | * @param percentiles 百分比信息数组 67 | * @return map 68 | */ 69 | private Map parseRequirePercentilesMap(String[] percentiles) { 70 | Map percentilesMap = Maps.newHashMap(); 71 | if(ArrayUtil.isNotEmpty(percentiles)) { 72 | try { 73 | for(String percent : percentiles) { 74 | String[] strings = percent.split(":"); 75 | //消耗时间 76 | Integer left = Ints.tryParse(strings[0]); 77 | //百分比例 78 | Float right = Floats.tryParse(strings[1]); 79 | percentilesMap.put(left, right); 80 | } 81 | } catch (Exception e) { 82 | throw new IllegalArgumentException("Percentiles format is error! please like this: 80:50000."); 83 | } 84 | } 85 | 86 | return percentilesMap; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/builder/EvaluationResultBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.support.builder; 2 | 3 | import com.github.houbb.heaven.support.builder.IBuilder; 4 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 5 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationConfig; 6 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationRequire; 7 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationResult; 8 | import org.apiguardian.api.API; 9 | 10 | import java.util.Map; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import static com.google.common.collect.Maps.newTreeMap; 14 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 15 | import static java.util.concurrent.TimeUnit.NANOSECONDS; 16 | 17 | /** 18 | * 验证结果-构建者 19 | * @author bbhou 20 | * @version 1.0.1 21 | * @since 1.0.1, 2018/01/15 22 | */ 23 | @API(status = API.Status.INTERNAL) 24 | public class EvaluationResultBuilder implements IBuilder { 25 | 26 | /** 27 | * 配置 28 | */ 29 | private final EvaluationConfig evaluationConfig; 30 | 31 | /** 32 | * 限定 33 | */ 34 | private final EvaluationRequire evaluationRequire; 35 | 36 | /** 37 | * 统计者 38 | */ 39 | private final StatisticsCalculator statisticsCalculator; 40 | 41 | public EvaluationResultBuilder(EvaluationConfig evaluationConfig, 42 | EvaluationRequire evaluationRequire, 43 | StatisticsCalculator statisticsCalculator) { 44 | this.evaluationConfig = evaluationConfig; 45 | this.evaluationRequire = evaluationRequire; 46 | this.statisticsCalculator = statisticsCalculator; 47 | } 48 | 49 | @Override 50 | public EvaluationResult build() { 51 | EvaluationResult evaluationResult = new EvaluationResult(); 52 | evaluationResult.setMinAchieved(isMinAchieved()); 53 | evaluationResult.setMaxAchieved(isMaxAchieved()); 54 | evaluationResult.setAverageAchieved(isAverageAchieved()); 55 | evaluationResult.setTimesPerSecondAchieved(isTimesPerSecondAchieved()); 56 | 57 | Map isPercentilesAchievedMap = buildIsPercentilesAchievedMap(); 58 | evaluationResult.setIsPercentilesAchievedMap(isPercentilesAchievedMap); 59 | evaluationResult.setPercentilesAchieved(isPercentilesAchieved(isPercentilesAchievedMap)); 60 | evaluationResult.setSuccessful(isSuccessful(evaluationResult)); 61 | evaluationResult.setThroughputQps(getThroughputQps()); 62 | return evaluationResult; 63 | } 64 | 65 | /** 66 | * 获取执行次数QPS 67 | * @return 执行次数QPS 68 | */ 69 | @SuppressWarnings("WeakerAccess") 70 | public long getThroughputQps() { 71 | long configDuration = evaluationConfig.getConfigDuration(); 72 | long configWarmUp = evaluationConfig.getConfigWarmUp(); 73 | return (long)(((float)statisticsCalculator.getEvaluationCount() / ((float)configDuration - configWarmUp)) * 1000); 74 | } 75 | 76 | /** 77 | * 延迟校验 78 | * @param actualNs 实际时间(纳秒) 79 | * @param requiredMs 需求时间(毫秒) 80 | * @return {@code true} 是 81 | */ 82 | private boolean validateLatency(float actualNs, float requiredMs) { 83 | long thresholdNs = (long)(requiredMs * MILLISECONDS.toNanos(1)); 84 | return actualNs <= thresholdNs; 85 | } 86 | 87 | /** 88 | * 最小延迟是否符合 89 | * @return {@code true} 是 90 | */ 91 | public boolean isMinAchieved() { 92 | if(evaluationRequire.getRequireMin() < 0) { 93 | return true; 94 | } 95 | return validateLatency(statisticsCalculator.getMinLatency(TimeUnit.NANOSECONDS), evaluationRequire.getRequireMin()); 96 | } 97 | 98 | /** 99 | * 最大延迟是否符合 100 | * @return {@code true} 是 101 | */ 102 | public boolean isMaxAchieved(){ 103 | if(evaluationRequire.getRequireMax() < 0) { 104 | return true; 105 | } 106 | return validateLatency(statisticsCalculator.getMaxLatency(TimeUnit.NANOSECONDS), evaluationRequire.getRequireMax()); 107 | } 108 | 109 | /** 110 | * 平均延迟是否符合 111 | * @return {@code true} 是 112 | */ 113 | public boolean isAverageAchieved() { 114 | if(evaluationRequire.getRequireAverage() < 0) { 115 | return true; 116 | } 117 | return validateLatency(statisticsCalculator.getMeanLatency(TimeUnit.NANOSECONDS), evaluationRequire.getRequireAverage()); 118 | } 119 | 120 | /** 121 | * 每秒执行次数是否符合 122 | * @return {@code true} 是 123 | */ 124 | public boolean isTimesPerSecondAchieved() { 125 | return evaluationRequire.getRequireTimesPerSecond() < 0 || getThroughputQps() >= evaluationRequire.getRequireTimesPerSecond(); 126 | } 127 | 128 | /** 129 | * 构建百分比是否通过 map 结果 130 | * @return 百分比是否通过 map 结果 131 | */ 132 | private Map buildIsPercentilesAchievedMap() { 133 | Map isPercentilesAchievedMap = newTreeMap(); 134 | //1. 计算结果 135 | for(Map.Entry entry : evaluationRequire.getRequirePercentilesMap().entrySet()) { 136 | Integer percentile = entry.getKey(); 137 | //限制的时间 138 | float thresholdMs = entry.getValue(); 139 | long thresholdNs = (long) (thresholdMs * MILLISECONDS.toNanos(1)); 140 | boolean result = statisticsCalculator.getLatencyPercentile(percentile, NANOSECONDS) <= thresholdNs; 141 | isPercentilesAchievedMap.put(percentile, result); 142 | } 143 | return isPercentilesAchievedMap; 144 | } 145 | 146 | /** 147 | * 百分比阈值是否满足 148 | * @param isPercentilesAchievedMap 是否满足条件 149 | * @return {@code true} 是 150 | */ 151 | private boolean isPercentilesAchieved(Map isPercentilesAchievedMap) { 152 | //校验是否通过 153 | for(Boolean bool : isPercentilesAchievedMap.values()) { 154 | if(!bool) { 155 | return false; 156 | } 157 | } 158 | return true; 159 | } 160 | 161 | /** 162 | * 是否成功 163 | * @param evaluationResult 构建结果 164 | * @return {@code true} 是 165 | */ 166 | public boolean isSuccessful(EvaluationResult evaluationResult) { 167 | return evaluationResult.isMaxAchieved() 168 | && evaluationResult.isMinAchieved() 169 | && evaluationResult.isAverageAchieved() 170 | && evaluationResult.isTimesPerSecondAchieved() 171 | && evaluationResult.isPercentilesAchieved(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/exception/JunitPerfException.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.support.exception; 2 | 3 | import org.apiguardian.api.API; 4 | 5 | /** 6 | * 性能测试异常 7 | * 要求:本项目中所有异常统一使用本类进行处理 8 | * @author bbhou 9 | * @version 1.0.2 10 | * @since 1.0.2, 2018/01/29 11 | */ 12 | @API(status = API.Status.MAINTAINED) 13 | public class JunitPerfException extends Exception { 14 | 15 | private static final long serialVersionUID = 8241804257890026060L; 16 | 17 | public JunitPerfException() { 18 | } 19 | 20 | public JunitPerfException(String message) { 21 | super(message); 22 | } 23 | 24 | public JunitPerfException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | public JunitPerfException(Throwable cause) { 29 | super(cause); 30 | } 31 | 32 | public JunitPerfException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 33 | super(message, cause, enableSuppression, writableStackTrace); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/exception/JunitPerfRuntimeException.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.support.exception; 2 | 3 | import org.apiguardian.api.API; 4 | 5 | /** 6 | * 性能测试运行时异常 7 | * 要求:本项目中所有运行时异常统一使用本类进行处理 8 | * @author bbhou 9 | * @version 1.0.0 10 | * @since 1.0.0, 2018/01/11 11 | */ 12 | @API(status = API.Status.MAINTAINED) 13 | public class JunitPerfRuntimeException extends RuntimeException { 14 | 15 | private static final long serialVersionUID = 7067503448534895751L; 16 | 17 | public JunitPerfRuntimeException() { 18 | } 19 | 20 | public JunitPerfRuntimeException(String message) { 21 | super(message); 22 | } 23 | 24 | public JunitPerfRuntimeException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | public JunitPerfRuntimeException(Throwable cause) { 29 | super(cause); 30 | } 31 | 32 | public JunitPerfRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 33 | super(message, cause, enableSuppression, writableStackTrace); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/i18n/I18N.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 | * Copyright (c) 2012-2018. houbinbin Inc. 4 | * word-checker All rights reserved. 5 | */ 6 | 7 | package com.github.houbb.junitperf.support.i18n; 8 | 9 | import com.github.houbb.junitperf.model.vo.I18nVo; 10 | 11 | import org.apiguardian.api.API; 12 | 13 | import java.io.File; 14 | import java.io.FileInputStream; 15 | import java.io.FileNotFoundException; 16 | import java.io.IOException; 17 | import java.util.Locale; 18 | import java.util.Properties; 19 | import java.util.ResourceBundle; 20 | 21 | /** 22 | *

i18n 对象

23 | * 24 | *
 Created: 2018-05-02 11:24  
25 | *
 Project: junitperf  
26 | * 27 | * @author houbinbin 28 | * @version 1.0.2 29 | * @since 1.0.2 30 | */ 31 | @API(status = API.Status.INTERNAL) 32 | public class I18N { 33 | 34 | private static final String DEFAULT_PROPERTIES_FILE_NAME = "i18n.JunitPerfMessages"; 35 | 36 | public static String get(final String key) { 37 | Locale currentLocale = Locale.getDefault(); 38 | ResourceBundle myResources = ResourceBundle.getBundle(DEFAULT_PROPERTIES_FILE_NAME, currentLocale); 39 | return myResources.getString(key); 40 | } 41 | 42 | public static class Key { 43 | public static final String memory = "memory"; 44 | public static final String warm_up = "warm_up"; 45 | public static final String max_latency = "max_latency"; 46 | public static final String min_latency = "min_latency"; 47 | public static final String thread_count = "thread_count"; 48 | public static final String report_created_by = "report_created_by"; 49 | public static final String started_at = "started_at"; 50 | public static final String required = "required"; 51 | public static final String junit_performance_report = "junit_performance_report"; 52 | public static final String type = "type"; 53 | public static final String top = "top"; 54 | public static final String throughput = "throughput"; 55 | public static final String avg_latency = "avg_latency"; 56 | public static final String invocations = "invocations"; 57 | public static final String execution_time = "execution_time"; 58 | public static final String success = "success"; 59 | public static final String actual = "actual"; 60 | 61 | /** 62 | * 报告信息为空 63 | */ 64 | public static final String reportIsEmpty="reportIsEmpty"; 65 | } 66 | 67 | /** 68 | * 构建 18n 对象 69 | * 70 | * ps: 这里其实可以用反射重新构建,简化代码 71 | * @return 对象 72 | */ 73 | public static I18nVo buildI18nVo() { 74 | I18nVo vo = new I18nVo(); 75 | vo.setJunit_performance_report(get(Key.junit_performance_report)); 76 | vo.setTop(get(Key.top)); 77 | vo.setReport_created_by(get(Key.report_created_by)); 78 | 79 | vo.setWarm_up(get(Key.warm_up)); 80 | vo.setStarted_at(get(Key.started_at)); 81 | vo.setExecution_time(get(Key.execution_time)); 82 | vo.setInvocations(get(Key.invocations)); 83 | vo.setThread_count(get(Key.thread_count)); 84 | vo.setSuccess(get(Key.success)); 85 | 86 | vo.setType(get(Key.type)); 87 | vo.setActual(get(Key.actual)); 88 | vo.setRequired(get(Key.required)); 89 | vo.setThroughput(get(Key.throughput)); 90 | vo.setMax_latency(get(Key.max_latency)); 91 | vo.setMin_latency(get(Key.min_latency)); 92 | vo.setAvg_latency(get(Key.avg_latency)); 93 | vo.setMemory(get(Key.memory)); 94 | return vo; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/statements/PerformanceEvaluationStatement.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.support.statements; 2 | 3 | import com.github.houbb.heaven.util.lang.ThreadUtil; 4 | import com.github.houbb.junitperf.constant.VersionConstant; 5 | import com.github.houbb.junitperf.core.report.Reporter; 6 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 7 | import com.github.houbb.junitperf.model.evaluation.EvaluationContext; 8 | import com.github.houbb.junitperf.model.evaluation.component.EvaluationConfig; 9 | import com.github.houbb.junitperf.support.exception.JunitPerfRuntimeException; 10 | import com.github.houbb.junitperf.support.i18n.I18N; 11 | import com.github.houbb.junitperf.support.task.PerformanceEvaluationTask; 12 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 13 | import org.apiguardian.api.API; 14 | 15 | import java.util.*; 16 | import java.util.concurrent.*; 17 | 18 | /** 19 | * 性能测试 statement 20 | * 21 | * @author bbhou 22 | * @version 2.0.0 23 | * @since 1.0.0, 2018/01/11 24 | */ 25 | @API(status = API.Status.INTERNAL, since = VersionConstant.V2_0_0) 26 | public class PerformanceEvaluationStatement { 27 | 28 | private static final String THREAD_NAME_PATTERN = "performance-evaluation-thread-%d"; 29 | private static final ThreadFactory FACTORY = new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_PATTERN).build(); 30 | 31 | private final EvaluationContext evaluationContext; 32 | private final StatisticsCalculator statisticsCalculator; 33 | private final Set reporterSet; 34 | private final Collection evaluationContextList; 35 | private final Class testClass; 36 | 37 | /** 38 | * 性能测试接口定义 39 | * 40 | * @param evaluationContext 上下文 41 | * @param statisticsCalculator 统计 42 | * @param reporterSet 报告方式 43 | * @param evaluationContextList 上下文 44 | * @param testClass 当前测试 class 信息 45 | */ 46 | public PerformanceEvaluationStatement(EvaluationContext evaluationContext, 47 | StatisticsCalculator statisticsCalculator, 48 | Set reporterSet, 49 | Collection evaluationContextList, 50 | final Class testClass) { 51 | this.evaluationContext = evaluationContext; 52 | this.statisticsCalculator = statisticsCalculator; 53 | this.reporterSet = reporterSet; 54 | this.evaluationContextList = evaluationContextList; 55 | this.testClass = testClass; 56 | } 57 | 58 | /** 59 | * 校验信息 60 | * 61 | * @throws Throwable 异常 62 | */ 63 | public void evaluate() throws Throwable { 64 | List taskList = new LinkedList<>(); 65 | 66 | try { 67 | EvaluationConfig evaluationConfig = evaluationContext.getEvaluationConfig(); 68 | for (int i = 0; i < evaluationConfig.getConfigThreads(); i++) { 69 | PerformanceEvaluationTask task = new PerformanceEvaluationTask(evaluationConfig.getConfigWarmUp(), 70 | statisticsCalculator, 71 | evaluationContext.getTestInstance(), 72 | evaluationContext.getTestMethod()); 73 | Thread t = FACTORY.newThread(task); 74 | taskList.add(task); 75 | t.start(); 76 | } 77 | 78 | Thread.sleep(evaluationConfig.getConfigDuration()); 79 | } finally { 80 | //具体详情,当执行打断时,被打断的任务可能已经开始执行(尚未执行完),会出现主线程往下走,被打断的线程也在继续走的情况 81 | for (PerformanceEvaluationTask task : taskList) { 82 | //终止执行的任务 83 | task.setContinue(false); 84 | } 85 | } 86 | 87 | evaluationContext.setStatisticsCalculator(statisticsCalculator); 88 | evaluationContext.runValidation(); 89 | generateReporter(); 90 | } 91 | 92 | /** 93 | * 报告生成 94 | */ 95 | private synchronized void generateReporter() { 96 | //1. 列表为空 97 | if(reporterSet.isEmpty()) { 98 | final String info = I18N.get(I18N.Key.reportIsEmpty); 99 | } 100 | 101 | int bestThreadNum = ThreadUtil.bestThreadNum(reporterSet.size()); 102 | if(bestThreadNum <= 1) { 103 | //2. 是否为只有单个文件 104 | final Reporter reporter = reporterSet.iterator().next(); 105 | reporter.report(testClass, evaluationContextList); 106 | } else { 107 | //3. 线程池 108 | ExecutorService executorService = Executors.newFixedThreadPool(bestThreadNum); 109 | 110 | List> futureTasks = new ArrayList<>(); 111 | for(final Reporter reporter : reporterSet) { 112 | Callable tocGenCallable = () -> { 113 | reporter.report(testClass, evaluationContextList); 114 | return null; 115 | }; 116 | 117 | Future reporterFuture = executorService.submit(tocGenCallable); 118 | futureTasks.add(reporterFuture); 119 | } 120 | executorService.shutdown(); 121 | 122 | try { 123 | for(Future reporterFuture : futureTasks) { 124 | Void aVoid = reporterFuture.get(); 125 | } 126 | } catch (InterruptedException | ExecutionException e) { 127 | Thread.currentThread().interrupt(); 128 | throw new JunitPerfRuntimeException(e); 129 | } 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/support/task/PerformanceEvaluationTask.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.support.task; 2 | 3 | import com.github.houbb.junitperf.core.statistics.StatisticsCalculator; 4 | import org.apache.lucene.util.RamUsageEstimator; 5 | import org.apiguardian.api.API; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | import static java.lang.System.nanoTime; 10 | 11 | /** 12 | * 性能测试-Task 13 | * 14 | * @author bbhou 15 | * @version 1.0.0 16 | * @since 1.0.0, 2018/01/11 17 | */ 18 | @API(status = API.Status.INTERNAL) 19 | public class PerformanceEvaluationTask implements Runnable { 20 | 21 | /** 22 | * 热身时间 23 | */ 24 | private final long warmUpNs; 25 | 26 | /** 27 | * 统计计算者 28 | */ 29 | private final StatisticsCalculator statisticsCalculator; 30 | 31 | /** 32 | * 是否继续标志位 33 | */ 34 | private volatile boolean isContinue; 35 | 36 | /** 37 | * 测试实例 38 | */ 39 | private final Object testInstance; 40 | 41 | /** 42 | * 测试方法 43 | */ 44 | private final Method testMethod; 45 | 46 | /** 47 | * 构造器 48 | * @param warmUpNs 准备时间 49 | * @param statisticsCalculator 统计 50 | * @param testInstance 测试实例 51 | * @param testMethod 测试方法 52 | * @since 1.0.0 53 | */ 54 | public PerformanceEvaluationTask(long warmUpNs, StatisticsCalculator statisticsCalculator, 55 | Object testInstance, Method testMethod) { 56 | this.warmUpNs = warmUpNs; 57 | this.statisticsCalculator = statisticsCalculator; 58 | this.testInstance = testInstance; 59 | this.testMethod = testMethod; 60 | //默认创建时继续执行 61 | this.isContinue = true; 62 | } 63 | 64 | @Override 65 | public void run() { 66 | long startTimeNs = System.nanoTime(); 67 | long startMeasurements = startTimeNs + warmUpNs; 68 | 69 | // 堆大小 70 | long memoryKb = RamUsageEstimator.shallowSizeOf(testInstance); 71 | statisticsCalculator.setMemory(memoryKb); 72 | 73 | while (isContinue) { 74 | evaluateStatement(startMeasurements); 75 | } 76 | } 77 | 78 | /** 79 | * 执行校验 80 | * 81 | * @param startMeasurements 开始时间 82 | */ 83 | private void evaluateStatement(long startMeasurements) { 84 | //0. 如果继续执行为 false,退出执行。 85 | if (!isContinue) { 86 | return; 87 | } 88 | 89 | //1. 准备阶段 90 | if (nanoTime() < startMeasurements) { 91 | try { 92 | testMethod.invoke(testInstance); 93 | } catch (Exception throwable) { 94 | // IGNORE 95 | } 96 | } else { 97 | long startTimeNs = nanoTime(); 98 | try { 99 | testMethod.invoke(testInstance); 100 | 101 | commonStatisticsUpdate(startTimeNs); 102 | } catch (Exception throwable) { 103 | // 错误信息更新 104 | statisticsCalculator.incrementErrorCount(); 105 | 106 | commonStatisticsUpdate(startTimeNs); 107 | } 108 | } 109 | } 110 | 111 | /** 112 | * 通用的统计更新 113 | * @param startTimeNs 开始时间 114 | * @since 2.0.5 115 | */ 116 | private void commonStatisticsUpdate(final long startTimeNs) { 117 | statisticsCalculator.incrementEvaluationCount(); 118 | statisticsCalculator.addLatencyMeasurement(getCostTimeNs(startTimeNs)); 119 | } 120 | 121 | /** 122 | * 获取消耗的时间(单位:毫秒) 123 | * 124 | * @param startTimeNs 开始时间 125 | * @return 消耗的时间 126 | * @since 1.0.0 127 | */ 128 | private long getCostTimeNs(long startTimeNs) { 129 | long currentTimeNs = System.nanoTime(); 130 | return currentTimeNs - startTimeNs; 131 | } 132 | 133 | public boolean isContinue() { 134 | return isContinue; 135 | } 136 | 137 | public void setContinue(boolean aContinue) { 138 | isContinue = aContinue; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/junitperf/util/FreemarkerUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.util; 2 | 3 | import com.github.houbb.junitperf.support.exception.JunitPerfException; 4 | import freemarker.ext.beans.BeansWrapper; 5 | import freemarker.template.*; 6 | import org.apiguardian.api.API; 7 | 8 | import java.io.*; 9 | import java.util.Locale; 10 | import java.util.Map; 11 | 12 | /** 13 | * 2017/11/13 14 | * 15 | * Freemarker 工具类 16 | * @author houbinbin 17 | * @version 1.0 18 | */ 19 | @API(status = API.Status.INTERNAL) 20 | public final class FreemarkerUtil { 21 | 22 | private FreemarkerUtil(){} 23 | 24 | /** 25 | * 组态 26 | */ 27 | private static Configuration configuration = null; 28 | 29 | /** 30 | * 获取配置 31 | * @param encoding 编码 32 | * @return 配置 33 | */ 34 | public static Configuration getConfiguration(String encoding) { 35 | return getConfiguration(encoding, true); 36 | } 37 | 38 | /** 39 | * define Configuration 40 | * @param encoding 编码 41 | * @param isForce 是否强制 42 | * @return 配置 43 | */ 44 | public static Configuration getConfiguration(String encoding, boolean isForce) { 45 | if (configuration == null 46 | || isForce) { 47 | configuration = new Configuration(); 48 | //编码 49 | configuration.setEncoding(Locale.getDefault(), encoding); 50 | 51 | //// 设置对象的包装器 52 | configuration.setObjectWrapper(new DefaultObjectWrapper()); 53 | 54 | // 设置异常处理器//这样的话就可以${a.b.c.d}即使没有属性也不会出错 55 | configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); 56 | 57 | //默认 FTL map 中不支持非 String 的 key 58 | configuration.setObjectWrapper(new BeansWrapper()); 59 | } 60 | 61 | return configuration; 62 | } 63 | 64 | /** 65 | * create file by template, htmlName, and modal map; 66 | * 1. 不存在直接创建 67 | * 2. 存在 + 覆盖:则删除直接覆盖 68 | * @param template 模板信息 69 | * @param targetFilePath 目标路径 70 | * @param map 配置属性 71 | * @param isOverwriteWhenExists 是否覆盖 72 | * @throws JunitPerfException if any 73 | * @throws IOException if any 74 | * @return 是否创建文件 75 | */ 76 | public static boolean createFile(Template template, String targetFilePath, Map map, boolean isOverwriteWhenExists) 77 | throws JunitPerfException, IOException { 78 | boolean result = true; 79 | File file = new File(targetFilePath); 80 | 81 | //create parent dir first. 82 | boolean makeDirs = file.getParentFile().mkdirs(); 83 | 84 | if (!file.exists()) { 85 | result = file.createNewFile(); 86 | flushFileContent(template, map, file); 87 | } else if(file.exists() 88 | && isOverwriteWhenExists) { 89 | flushFileContent(template, map, file); 90 | } else { 91 | //ignore 92 | } 93 | 94 | return result; 95 | } 96 | 97 | /** 98 | * 刷新文件内容 99 | * @param template 模板 100 | * @param map 配置 101 | * @param file 文件 102 | */ 103 | private static void flushFileContent(Template template, Map map, File file) 104 | throws JunitPerfException { 105 | try { 106 | Writer out = new BufferedWriter(new FileWriter(file)); 107 | template.process(map, out); 108 | out.flush(); 109 | } catch (IOException | TemplateException e) { 110 | throw new JunitPerfException(e); 111 | } 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/resources/htmls/chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Google Charts Tutorial 4 | 6 | 9 | 10 | 11 | 12 |
13 |
14 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/resources/htmls/hello-better.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JUnit Performance Report 7 | 8 | 168 | 169 | 170 | 171 | 172 |
JUnit Performance Report
173 | 174 |
175 | 226 | 227 |
228 | 229 |
230 | helloWorldTest 231 | 232 |
233 | 234 |
235 |
236 |
    237 |
  • 49
  • 238 |
  • 49
  • 239 |
  • 49
  • 240 |
  • 49
  • 241 |
  • 2018-03-01 15:02:55.135
  • 242 |
  • 1,000 ms
  • 243 |
244 |
245 | 246 |
247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 |
TypeActualRequired
Throughput49 / s-1 / s
Min latency19.132 ms-1 ms
Avg latency20.139 ms-1 ms
Max latency24.529 ms-1 ms
278 |
279 |
280 | 281 |
282 | 283 |
284 | 285 |
286 | 287 |
288 | 289 | 290 |
291 | 292 |
293 |
294 | Report created by JunitPerf 295 | Top 296 |
297 |
298 | 299 | 300 | 301 | 302 | 426 | 427 | 428 | 429 | 430 | -------------------------------------------------------------------------------- /src/main/resources/i18n/JunitPerfMessages.properties: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 | # Copyright (c) 2012-2018. houbinbin Inc. 4 | # word-checker All rights reserved. 5 | # 6 | # Header 7 | junit_performance_report=JUnit Performance Report 8 | 9 | # Footer 10 | top=Top 11 | report_created_by=Report created by 12 | 13 | # Config 14 | invocations=Invocations 15 | success=Success 16 | warm_up=Warm up 17 | thread_count=Thread Count 18 | started_at=Started at 19 | execution_time=Execution Time 20 | memory=Memory 21 | 22 | # Analysis 23 | type=Type 24 | actual=Actual 25 | required=Required 26 | throughput=Throughput 27 | min_latency=Min latency 28 | avg_latency=Avg latency 29 | max_latency=Max latency 30 | 31 | 32 | # service info 33 | reportIsEmpty=Report set is empty! 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/resources/i18n/JunitPerfMessages_en.properties: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 | # Copyright (c) 2012-2018. houbinbin Inc. 4 | # word-checker All rights reserved. 5 | # 6 | # Header 7 | junit_performance_report=JUnit Performance Report 8 | 9 | # Footer 10 | top=Top 11 | report_created_by=Report created by 12 | 13 | # Config 14 | invocations=Invocations 15 | success=Success 16 | warm_up=Warm up 17 | thread_count=Thread Count 18 | started_at=Started at 19 | execution_time=Execution Time 20 | memory=Memory 21 | 22 | # Analysis 23 | type=Type 24 | actual=Actual 25 | required=Required 26 | throughput=Throughput 27 | min_latency=Min latency 28 | avg_latency=Avg latency 29 | max_latency=Max latency 30 | 31 | # service info 32 | reportIsEmpty=Report set is empty! 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/resources/i18n/JunitPerfMessages_zh_CN.properties: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 | # Copyright (c) 2012-2018. houbinbin Inc. 4 | # word-checker All rights reserved. 5 | # 6 | # Header 7 | junit_performance_report = JUnit \u6027\u80FD\u62A5\u544A 8 | 9 | # Footer 10 | top = \u9876\u90E8 11 | report_created_by = \u62A5\u544A\u6280\u672F\u652F\u6301 12 | 13 | # Config 14 | invocations = \u8C03\u7528\u6570 15 | success = \u6210\u529F\u6570 16 | thread_count = \u7EBF\u7A0B\u6570 17 | warm_up = \u51C6\u5907\u65F6\u95F4 18 | started_at = \u5F00\u59CB\u65F6\u95F4 19 | execution_time = \u6267\u884C\u65F6\u95F4 20 | memory = \u5185\u5b58\u6d88\u8017 21 | 22 | # Analysis 23 | type = \u7C7B\u578B 24 | actual = \u5B9E\u9645 25 | required = \u9700\u6C42 26 | throughput = \u541E\u5410\u91CF 27 | min_latency = \u6700\u5C0F\u5EF6\u8FDF 28 | avg_latency = \u5E73\u5747\u5EF6\u8FDF 29 | max_latency = \u6700\u5927\u5EF6\u8FDF 30 | 31 | 32 | # service info 33 | reportIsEmpty=\u62A5\u544A\u5217\u8868\u4E3A\u7A7A 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/resources/templates/report.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ${className}-JUnit Performance Report 7 | 8 | 179 | 180 | 181 | 182 | 183 |
${i18n.junit_performance_report}
184 | 185 |
186 | 204 | 205 |
206 | 207 | <#list contextData as context> 208 | 209 |
210 | ${context.methodName} 211 | 212 |
213 | 214 | 238 |
239 | 240 |
241 |
242 |
    243 |
  • ${context.statisticsCalculator.evaluationCount} 245 |
  • 246 |
  • 247 | ${context.statisticsCalculator.evaluationCount - context.statisticsCalculator.errorCount} 248 |
  • 249 |
  • ${context.evaluationConfig.configThreads}
  • 251 | 252 |
  • ${context.evaluationConfig.configWarmUp} ms
  • 254 |
  • ${context.evaluationConfig.configDuration} ms 256 |
  • 257 |
  • ${context.statisticsCalculator.memory} byte 258 |
  • 259 |
  • ${context.startTime} 260 |
  • 261 |
262 |
263 | 264 |
265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | <#assign tt_c = context.evaluationResult.isTimesPerSecondAchieved() ? string("#00000", "#ec971f")> 275 | <#assign min_c = context.evaluationResult.isMinAchieved() ? string("#00000", "#ec971f")> 276 | <#assign avg_c = context.evaluationResult.isAverageAchieved() ? string("#00000", "#ec971f")> 277 | <#assign max_c = context.evaluationResult.isMaxAchieved() ? string("#00000", "#ec971f")> 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 |
${i18n.type}${i18n.actual}${i18n.required}
${i18n.throughput}${context.evaluationResult.getThroughputQps()} / s${context.evaluationRequire.requireTimesPerSecond} / s
${i18n.min_latency}${context.statisticsCalculator.getMinLatency(milliseconds)} ms${context.evaluationRequire.requireMin} ms
${i18n.avg_latency}${context.statisticsCalculator.getMeanLatency(milliseconds)} ms${context.evaluationRequire.requireAverage} ms
${i18n.max_latency}${context.statisticsCalculator.getMaxLatency(milliseconds)} ms${context.evaluationRequire.requireMax} ms
301 |
302 |
303 | 304 |
305 | 306 | 307 | 308 |
309 | 310 | 311 |
312 | 313 | 319 | 320 | 321 | 322 | 323 | 324 | -------------------------------------------------------------------------------- /src/main/resources/templates/report_old.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JUnit Performance Report 7 | 8 | 9 | 10 |
11 |

JUnit Performance Report

12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | <#list contextData as context> 21 | 22 | 23 | <#if context.evaluationResult.isSuccessful()> 24 | 25 | <#else> 26 | 27 | 28 | 29 | 30 | 31 | 32 |
   Tests
  ${context.methodName}
33 |
34 |
35 | 36 | 37 | <#list contextData as context> 38 |

${context.methodName}

39 | 40 | 41 | <#--一半图片--> 42 | 71 | 72 | 73 | <#--一半统计信息--> 74 | 161 | 162 | 163 | 164 | 165 |
43 | 44 | 68 | 69 |
70 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | <#assign colour = context.evaluationResult.isTimesPerSecondAchieved() ? string("#2b67a4", "#d9534f")> 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | <#assign colour = context.evaluationResult.isMinAchieved() ? string("#2b67a4", "#d9534f")> 127 | 128 | 129 | 130 | 131 | 132 | 133 | <#assign colour = context.evaluationResult.isAverageAchieved() ? string("#2b67a4", "#d9534f")> 134 | 135 | 136 | 137 | 138 | 139 | 140 | <#assign colour = context.evaluationResult.isMaxAchieved() ? string("#2b67a4", "#d9534f")> 141 | 142 | 143 | 144 | 145 | 146 | <#assign percentMap = context.evaluationRequire.getRequirePercentilesMap()> 147 | 148 | <#list percentMap.keySet() as entryKey> 149 | <#assign colour = context.evaluationResult.getIsPercentilesAchievedMap().get(entryKey) ? string("#2b67a4", "#d9534f")> 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 |
Started at:${context.startTime}
Invocations:${context.statisticsCalculator.evaluationCount}
- Success:${context.statisticsCalculator.evaluationCount - context.statisticsCalculator.errorCount}
   
Thread Count:${context.evaluationConfig.configThreads}
Warm up:${context.evaluationConfig.configWarmUp} ms
   
 Measured
(system)
Required
Execution time:${context.evaluationConfig.configDuration} ms
Throughput:${context.evaluationResult.getThroughputQps()} / s${context.evaluationRequire.requireTimesPerSecond} / s
Min. latency:${context.statisticsCalculator.getMinLatency(milliseconds)} ms${context.evaluationRequire.requireMin} ms
Average latency:${context.statisticsCalculator.getMeanLatency(milliseconds)} ms${context.evaluationRequire.requireAverage} ms
Max latency:${context.statisticsCalculator.getMaxLatency(milliseconds)} ms${context.evaluationRequire.requireMax} ms
Percent ${entryKey}:${context.statisticsCalculator.getLatencyPercentile(entryKey, milliseconds)} ms${percentMap.get(entryKey)} ms
159 | 160 |
166 | 167 | 168 |
169 |
Report created by JunitPerf
170 |
171 | 172 | 173 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/HelloWorldMultiTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | 5 | /** 6 | *

7 | * 8 | *
 Created: 2018/7/2 上午10:59  
9 | *
 Project: junitperf  
10 | * 11 | * @author houbinbin 12 | * @version 1.0 13 | * @since 2.0.6 14 | */ 15 | public class HelloWorldMultiTest { 16 | 17 | @JunitPerfConfig(duration = 1000) 18 | public void helloTest() throws InterruptedException { 19 | Thread.sleep(100); 20 | System.out.println("Hello Junit5"); 21 | } 22 | 23 | @JunitPerfConfig(duration = 1000) 24 | public void helloTest2() throws InterruptedException { 25 | Thread.sleep(100); 26 | System.out.println("Hello Junit5"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/HelloWorldTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | 5 | /** 6 | *

7 | * 8 | *
 Created: 2018/7/2 上午10:59  
9 | *
 Project: junitperf  
10 | * 11 | * @author houbinbin 12 | * @version 1.0 13 | * @since JDK 1.7 14 | */ 15 | public class HelloWorldTest { 16 | 17 | @JunitPerfConfig(duration = 1000) 18 | public void helloTest() throws InterruptedException { 19 | Thread.sleep(100); 20 | System.out.println("Hello Junit5"); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/LongNameHtmlTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | import com.github.houbb.junitperf.core.report.impl.HtmlReporter; 5 | 6 | /** 7 | *

方法名字较长的情况

8 | * 9 | *
 Created: 2018/7/2 上午10:59  
10 | *
 Project: junitperf  
11 | * 12 | * @author houbinbin 13 | * @version 1.0 14 | * @since 2.0.6 15 | */ 16 | public class LongNameHtmlTest { 17 | 18 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 19 | public void myNameIsLongLongLongLong() throws InterruptedException { 20 | Thread.sleep(100); 21 | System.out.println("Hello Junit5"); 22 | } 23 | 24 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 25 | public void myNameIsLongLongLongLong2() throws InterruptedException { 26 | Thread.sleep(100); 27 | System.out.println("Hello Junit5"); 28 | } 29 | 30 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 31 | public void myNameIsLongLongLongLong3() throws InterruptedException { 32 | Thread.sleep(100); 33 | System.out.println("Hello Junit5"); 34 | } 35 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 36 | public void myNameIsLongLongLongLong4() throws InterruptedException { 37 | Thread.sleep(100); 38 | System.out.println("Hello Junit5"); 39 | } 40 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 41 | public void myNameIsLongLongLongLong5() throws InterruptedException { 42 | Thread.sleep(100); 43 | System.out.println("Hello Junit5"); 44 | } 45 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 46 | public void myNameIsLongLongLongLong6() throws InterruptedException { 47 | Thread.sleep(100); 48 | System.out.println("Hello Junit5"); 49 | } 50 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 51 | public void myNameIsLongLongLongLong7() throws InterruptedException { 52 | Thread.sleep(100); 53 | System.out.println("Hello Junit5"); 54 | } 55 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 56 | public void myNameIsLongLongLongLong8() throws InterruptedException { 57 | Thread.sleep(100); 58 | System.out.println("Hello Junit5"); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/OrderedHtmlTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | import com.github.houbb.junitperf.core.report.impl.HtmlReporter; 5 | import org.junit.jupiter.api.MethodOrderer; 6 | import org.junit.jupiter.api.Order; 7 | import org.junit.jupiter.api.TestMethodOrder; 8 | 9 | /** 10 | *

按照顺序执行

11 | * 12 | *
 Created: 2018/7/2 上午10:59  
13 | *
 Project: junitperf  
14 | * 15 | * https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order 16 | * 17 | * @author houbinbin 18 | * @version 1.0 19 | * @since 2.0.6 20 | */ 21 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 22 | public class OrderedHtmlTest { 23 | 24 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 25 | @Order(1) 26 | public void myNameIsLongLongLongLong() throws InterruptedException { 27 | Thread.sleep(100); 28 | System.out.println("Hello Junit5"); 29 | } 30 | 31 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 32 | @Order(2) 33 | public void myNameIsLongLongLongLong2() throws InterruptedException { 34 | Thread.sleep(100); 35 | System.out.println("Hello Junit5"); 36 | } 37 | 38 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 39 | @Order(3) 40 | public void myNameIsLongLongLongLong3() throws InterruptedException { 41 | Thread.sleep(100); 42 | System.out.println("Hello Junit5"); 43 | } 44 | 45 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 46 | @Order(4) 47 | public void myNameIsLongLongLongLong4() throws InterruptedException { 48 | Thread.sleep(100); 49 | System.out.println("Hello Junit5"); 50 | } 51 | 52 | @Order(5) 53 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 54 | public void myNameIsLongLongLongLong5() throws InterruptedException { 55 | Thread.sleep(100); 56 | System.out.println("Hello Junit5"); 57 | } 58 | 59 | @Order(6) 60 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 61 | public void myNameIsLongLongLongLong6() throws InterruptedException { 62 | Thread.sleep(100); 63 | System.out.println("Hello Junit5"); 64 | } 65 | 66 | @Order(7) 67 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 68 | public void myNameIsLongLongLongLong7() throws InterruptedException { 69 | Thread.sleep(100); 70 | System.out.println("Hello Junit5"); 71 | } 72 | 73 | @Order(8) 74 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 75 | public void myNameIsLongLongLongLong8() throws InterruptedException { 76 | Thread.sleep(100); 77 | System.out.println("Hello Junit5"); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/report/DefaultReporterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples.report; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | 5 | /** 6 | *

默认输出

7 | * 8 | *
 Created: 2018/7/2 上午11:03  
9 | *
 Project: junitperf  
10 | * 11 | * @author houbinbin 12 | * @version 1.0 13 | * @since JDK 1.7 14 | */ 15 | public class DefaultReporterTest { 16 | 17 | @JunitPerfConfig(duration = 1000) 18 | public void helloTest() throws InterruptedException { 19 | Thread.sleep(100); 20 | System.out.println("Hello Junit5"); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/report/DefineReporterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples.report; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | import com.github.houbb.junitperf.examples.report.support.MyReporter; 5 | 6 | /** 7 | *

自定义输出

8 | * 9 | *
 Created: 2018/7/2 上午11:04  
10 | *
 Project: junitperf  
11 | * 12 | * @author houbinbin 13 | * @version 1.0 14 | * @since JDK 1.7 15 | */ 16 | public class DefineReporterTest { 17 | 18 | @JunitPerfConfig(duration = 1000, reporter = MyReporter.class) 19 | public void helloTest() throws InterruptedException { 20 | Thread.sleep(100); 21 | System.out.println("Hello Junit5"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/report/HtmlReporterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples.report; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | import com.github.houbb.junitperf.core.report.impl.HtmlReporter; 5 | 6 | /** 7 | *

HTML 输出

8 | * 9 | *
 Created: 2018/7/2 上午11:03  
10 | *
 Project: junitperf  
11 | * 12 | * @author houbinbin 13 | * @version 1.0 14 | * @since JDK 1.7 15 | */ 16 | public class HtmlReporterTest { 17 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class}) 18 | public void helloTest() throws InterruptedException { 19 | Thread.sleep(100); 20 | System.out.println("Hello Junit5"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/report/MultiReporterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples.report; 2 | 3 | import com.github.houbb.junitperf.core.annotation.JunitPerfConfig; 4 | import com.github.houbb.junitperf.core.report.impl.ConsoleReporter; 5 | import com.github.houbb.junitperf.core.report.impl.HtmlReporter; 6 | 7 | /** 8 | *

混合输出

9 | * 10 | *
 Created: 2018/7/2 上午11:03  
11 | *
 Project: junitperf  
12 | * 13 | * @author houbinbin 14 | * @version 1.0 15 | * @since JDK 1.7 16 | */ 17 | public class MultiReporterTest { 18 | 19 | @JunitPerfConfig(duration = 1000, reporter = {HtmlReporter.class, 20 | ConsoleReporter.class}) 21 | public void helloTest() throws InterruptedException { 22 | Thread.sleep(100); 23 | System.out.println("Hello Junit5"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/junitperf/examples/report/support/MyReporter.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.junitperf.examples.report.support; 2 | 3 | import com.github.houbb.junitperf.core.report.Reporter; 4 | import com.github.houbb.junitperf.model.evaluation.EvaluationContext; 5 | 6 | import java.util.Collection; 7 | import java.util.Set; 8 | 9 | /** 10 | *

11 | * 12 | *
 Created: 2018/7/2 上午11:05  
13 | *
 Project: junitperf  
14 | * 15 | * @author houbinbin 16 | * @version 1.0 17 | * @since JDK 1.7 18 | */ 19 | public class MyReporter implements Reporter { 20 | @Override 21 | public void report(Class testClass, Collection evaluationContextSet) { 22 | System.out.println("MyReporter"); 23 | } 24 | } 25 | --------------------------------------------------------------------------------