├── .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 | [](http://mvnrepository.com/artifact/com.github.houbb/junitperf)
8 | [](https://www.travis-ci.org/houbb/junitperf)
9 | [](https://github.com/houbb/junitperf/blob/master/LICENSE.txt)
10 | [](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 | 
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 | [](http://mvnrepository.com/artifact/com.github.houbb/junitperf)
8 | [](https://www.travis-ci.org/houbb/junitperf)
9 | [](https://github.com/houbb/junitperf/blob/master/LICENSE.txt)
10 | [](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 | 
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 | 
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 extends StatisticsCalculator> statistics() default DefaultStatisticsCalculator.class;
59 |
60 | /**
61 | * 存放报告信息类
62 | * @return 报告信息
63 | * @since 1.0.0
64 | */
65 | Class extends Reporter>[] 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 extends Reporter>[] 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 |
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 | Type |
251 | Actual |
252 | Required |
253 |
254 |
255 |
256 |
257 | Throughput |
258 | 49 / s |
259 | -1 / s |
260 |
261 |
262 | Min latency |
263 | 19.132 ms |
264 | -1 ms |
265 |
266 |
267 | Avg latency |
268 | 20.139 ms |
269 | -1 ms |
270 |
271 |
272 | Max latency |
273 | 24.529 ms |
274 | -1 ms |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
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 | ${i18n.type} |
269 | ${i18n.actual} |
270 | ${i18n.required} |
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 | ${i18n.throughput} |
281 | ${context.evaluationResult.getThroughputQps()} / s |
282 | ${context.evaluationRequire.requireTimesPerSecond} / s |
283 |
284 |
285 | ${i18n.min_latency} |
286 | ${context.statisticsCalculator.getMinLatency(milliseconds)} ms |
287 | ${context.evaluationRequire.requireMin} ms |
288 |
289 |
290 | ${i18n.avg_latency} |
291 | ${context.statisticsCalculator.getMeanLatency(milliseconds)} ms |
292 | ${context.evaluationRequire.requireAverage} ms |
293 |
294 |
295 | ${i18n.max_latency} |
296 | ${context.statisticsCalculator.getMaxLatency(milliseconds)} ms |
297 | ${context.evaluationRequire.requireMax} ms |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 | #list>
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 | Tests |
18 |
19 |
20 | <#list contextData as context>
21 |
22 |
23 | <#if context.evaluationResult.isSuccessful()>
24 | |
25 | <#else>
26 | |
27 | #if>
28 | ${context.methodName} |
29 |
30 | #list>
31 |
32 |
33 |
34 |
35 |
36 |
37 | <#list contextData as context>
38 | ${context.methodName}
39 |
40 |
41 | <#--一半图片-->
42 |
43 |
44 |
68 |
69 |
70 | |
71 |
72 |
73 | <#--一半统计信息-->
74 |
75 |
76 |
77 |
78 | Started at: |
79 | ${context.startTime} |
80 |
81 |
82 |
83 | Invocations: |
84 | ${context.statisticsCalculator.evaluationCount} |
85 | |
86 |
87 |
88 | - Success: |
89 | ${context.statisticsCalculator.evaluationCount - context.statisticsCalculator.errorCount} |
90 | |
91 |
92 |
93 |
94 | | | |
95 |
96 | Thread Count: |
97 | ${context.evaluationConfig.configThreads} |
98 | |
99 |
100 |
101 | Warm up: |
102 | ${context.evaluationConfig.configWarmUp} ms |
103 | |
104 |
105 | | | |
106 |
107 | |
108 | Measured (system) |
109 | Required |
110 |
111 |
112 | Execution time: |
113 | ${context.evaluationConfig.configDuration} ms |
114 | |
115 |
116 |
117 |
118 | <#assign colour = context.evaluationResult.isTimesPerSecondAchieved() ? string("#2b67a4", "#d9534f")>
119 |
120 | Throughput: |
121 | ${context.evaluationResult.getThroughputQps()} / s |
122 | ${context.evaluationRequire.requireTimesPerSecond} / s |
123 |
124 |
125 |
126 | <#assign colour = context.evaluationResult.isMinAchieved() ? string("#2b67a4", "#d9534f")>
127 | Min. latency: |
128 | ${context.statisticsCalculator.getMinLatency(milliseconds)} ms |
129 | ${context.evaluationRequire.requireMin} ms |
130 |
131 |
132 |
133 | <#assign colour = context.evaluationResult.isAverageAchieved() ? string("#2b67a4", "#d9534f")>
134 | Average latency: |
135 | ${context.statisticsCalculator.getMeanLatency(milliseconds)} ms |
136 | ${context.evaluationRequire.requireAverage} ms |
137 |
138 |
139 |
140 | <#assign colour = context.evaluationResult.isMaxAchieved() ? string("#2b67a4", "#d9534f")>
141 | Max latency: |
142 | ${context.statisticsCalculator.getMaxLatency(milliseconds)} ms |
143 | ${context.evaluationRequire.requireMax} ms |
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 | Percent ${entryKey}: |
153 | ${context.statisticsCalculator.getLatencyPercentile(entryKey, milliseconds)} ms |
154 | ${percentMap.get(entryKey)} ms |
155 |
156 | #list>
157 |
158 |
159 |
160 | |
161 |
162 |
163 |
164 |
165 |
166 | #list>
167 |
168 |
169 |
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 |
--------------------------------------------------------------------------------