├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE-MPL-RabbitMQ ├── Makefile ├── README.md ├── build.config ├── certs ├── Makefile └── openssl.cnf ├── erlang.mk ├── qpid_config.py ├── qpid_patch ├── rabbit_failing.txt ├── rabbitmq-components.mk ├── src ├── rabbit_runtime_parameters_test.erl ├── rabbit_test_configs.erl ├── rabbit_test_runner.erl ├── rabbit_test_util.erl └── rabbitmq_test.app.src └── test ├── snippets.config └── src ├── gm_qc.erl ├── gm_soak_test.erl ├── gm_speed_test.erl └── rabbit_backing_queue_qc.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .sw? 2 | .*.sw? 3 | *.beam 4 | .erlang.mk/ 5 | cover/ 6 | deps/ 7 | doc/ 8 | ebin/ 9 | examples 10 | example_files 11 | logs/ 12 | plugins/ 13 | schema/ 14 | 15 | rabbitmq_test.d 16 | 17 | # Generated certificates. 18 | certs/client/ 19 | certs/server/ 20 | certs/testca/ 21 | 22 | # Downloaded sources. 23 | qpid_testsuite/ 24 | 25 | ct.cover.spec 26 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open 4 | and welcoming community, we pledge to respect all people who contribute through reporting 5 | issues, posting feature requests, updating documentation, submitting pull requests or 6 | patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free experience for 9 | everyone, regardless of level of experience, gender, gender identity and expression, 10 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, 11 | religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic addresses, 20 | without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 24 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 25 | Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors 26 | that they deem inappropriate, threatening, offensive, or harmful. 27 | 28 | By adopting this Code of Conduct, project maintainers commit themselves to fairly and 29 | consistently applying these principles to every aspect of managing this project. Project 30 | maintainers who do not follow or enforce the Code of Conduct may be permanently removed 31 | from the project team. 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an 34 | individual is representing the project or its community. 35 | 36 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 37 | contacting a project maintainer at [info@rabbitmq.com](mailto:info@rabbitmq.com). All complaints will 38 | be reviewed and investigated and will result in a response that is deemed necessary and 39 | appropriate to the circumstances. Maintainers are obligated to maintain confidentiality 40 | with regard to the reporter of an incident. 41 | 42 | This Code of Conduct is adapted from the 43 | [Contributor Covenant](https://contributor-covenant.org), version 1.3.0, available at 44 | [contributor-covenant.org/version/1/3/0/](https://contributor-covenant.org/version/1/3/0/) 45 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | RabbitMQ projects use pull requests to discuss, collaborate on and accept code contributions. 4 | Pull requests is the primary place of discussing code changes. 5 | 6 | ## How to Contribute 7 | 8 | The process is fairly standard: 9 | 10 | * Fork the repository or repositories you plan on contributing to 11 | * Clone [RabbitMQ umbrella repository](https://github.com/rabbitmq/rabbitmq-public-umbrella) 12 | * `cd umbrella`, `make co` 13 | * Create a branch with a descriptive name in the relevant repositories 14 | * Make your changes, run tests, commit with a [descriptive message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), push to your fork 15 | * Submit pull requests with an explanation what has been changed and **why** 16 | * Submit a filled out and signed [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) if needed (see below) 17 | * Be patient. We will get to your pull request eventually 18 | 19 | If what you are going to work on is a substantial change, please first ask the core team 20 | of their opinion on [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). 21 | 22 | 23 | ## Code of Conduct 24 | 25 | See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md). 26 | 27 | 28 | ## Contributor Agreement 29 | 30 | If you want to contribute a non-trivial change, please submit a signed copy of our 31 | [Contributor Agreement](https://github.com/rabbitmq/ca#how-to-submit) around the time 32 | you submit your pull request. This will make it much easier (in some cases, possible) 33 | for the RabbitMQ team at Pivotal to merge your contribution. 34 | 35 | 36 | ## Where to Ask Questions 37 | 38 | If something isn't clear, feel free to ask on our [mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users). 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This package, the RabbitMQ Management Plugin is licensed under the MPL. For the 2 | MPL, please see LICENSE-MPL-RabbitMQ. 3 | 4 | This package makes use of the following third party libraries: 5 | jQuery - https://jquery.com/ - MIT license, see LICENSE-MIT-jQuery164 6 | EJS - https://embeddedjs.com/ - MIT license, see LICENSE-MIT-EJS10 7 | Sammy - https://code.quirkey.com/sammy/ - MIT license, see LICENSE-MIT-Sammy060 8 | Cowboy - https://ninenines.eu/ - ISC license 9 | base64.js - https://code.google.com/p/stringencoders/ - BSD license, see LICENSE-BSD-base64js 10 | If you have any questions regarding licensing, please contact us at 11 | info@rabbitmq.com. 12 | -------------------------------------------------------------------------------- /LICENSE-MPL-RabbitMQ: -------------------------------------------------------------------------------- 1 | MOZILLA PUBLIC LICENSE 2 | Version 1.1 3 | 4 | --------------- 5 | 6 | 1. Definitions. 7 | 8 | 1.0.1. "Commercial Use" means distribution or otherwise making the 9 | Covered Code available to a third party. 10 | 11 | 1.1. "Contributor" means each entity that creates or contributes to 12 | the creation of Modifications. 13 | 14 | 1.2. "Contributor Version" means the combination of the Original 15 | Code, prior Modifications used by a Contributor, and the Modifications 16 | made by that particular Contributor. 17 | 18 | 1.3. "Covered Code" means the Original Code or Modifications or the 19 | combination of the Original Code and Modifications, in each case 20 | including portions thereof. 21 | 22 | 1.4. "Electronic Distribution Mechanism" means a mechanism generally 23 | accepted in the software development community for the electronic 24 | transfer of data. 25 | 26 | 1.5. "Executable" means Covered Code in any form other than Source 27 | Code. 28 | 29 | 1.6. "Initial Developer" means the individual or entity identified 30 | as the Initial Developer in the Source Code notice required by Exhibit 31 | A. 32 | 33 | 1.7. "Larger Work" means a work which combines Covered Code or 34 | portions thereof with code not governed by the terms of this License. 35 | 36 | 1.8. "License" means this document. 37 | 38 | 1.8.1. "Licensable" means having the right to grant, to the maximum 39 | extent possible, whether at the time of the initial grant or 40 | subsequently acquired, any and all of the rights conveyed herein. 41 | 42 | 1.9. "Modifications" means any addition to or deletion from the 43 | substance or structure of either the Original Code or any previous 44 | Modifications. When Covered Code is released as a series of files, a 45 | Modification is: 46 | A. Any addition to or deletion from the contents of a file 47 | containing Original Code or previous Modifications. 48 | 49 | B. Any new file that contains any part of the Original Code or 50 | previous Modifications. 51 | 52 | 1.10. "Original Code" means Source Code of computer software code 53 | which is described in the Source Code notice required by Exhibit A as 54 | Original Code, and which, at the time of its release under this 55 | License is not already Covered Code governed by this License. 56 | 57 | 1.10.1. "Patent Claims" means any patent claim(s), now owned or 58 | hereafter acquired, including without limitation, method, process, 59 | and apparatus claims, in any patent Licensable by grantor. 60 | 61 | 1.11. "Source Code" means the preferred form of the Covered Code for 62 | making modifications to it, including all modules it contains, plus 63 | any associated interface definition files, scripts used to control 64 | compilation and installation of an Executable, or source code 65 | differential comparisons against either the Original Code or another 66 | well known, available Covered Code of the Contributor's choice. The 67 | Source Code can be in a compressed or archival form, provided the 68 | appropriate decompression or de-archiving software is widely available 69 | for no charge. 70 | 71 | 1.12. "You" (or "Your") means an individual or a legal entity 72 | exercising rights under, and complying with all of the terms of, this 73 | License or a future version of this License issued under Section 6.1. 74 | For legal entities, "You" includes any entity which controls, is 75 | controlled by, or is under common control with You. For purposes of 76 | this definition, "control" means (a) the power, direct or indirect, 77 | to cause the direction or management of such entity, whether by 78 | contract or otherwise, or (b) ownership of more than fifty percent 79 | (50%) of the outstanding shares or beneficial ownership of such 80 | entity. 81 | 82 | 2. Source Code License. 83 | 84 | 2.1. The Initial Developer Grant. 85 | The Initial Developer hereby grants You a world-wide, royalty-free, 86 | non-exclusive license, subject to third party intellectual property 87 | claims: 88 | (a) under intellectual property rights (other than patent or 89 | trademark) Licensable by Initial Developer to use, reproduce, 90 | modify, display, perform, sublicense and distribute the Original 91 | Code (or portions thereof) with or without Modifications, and/or 92 | as part of a Larger Work; and 93 | 94 | (b) under Patents Claims infringed by the making, using or 95 | selling of Original Code, to make, have made, use, practice, 96 | sell, and offer for sale, and/or otherwise dispose of the 97 | Original Code (or portions thereof). 98 | 99 | (c) the licenses granted in this Section 2.1(a) and (b) are 100 | effective on the date Initial Developer first distributes 101 | Original Code under the terms of this License. 102 | 103 | (d) Notwithstanding Section 2.1(b) above, no patent license is 104 | granted: 1) for code that You delete from the Original Code; 2) 105 | separate from the Original Code; or 3) for infringements caused 106 | by: i) the modification of the Original Code or ii) the 107 | combination of the Original Code with other software or devices. 108 | 109 | 2.2. Contributor Grant. 110 | Subject to third party intellectual property claims, each Contributor 111 | hereby grants You a world-wide, royalty-free, non-exclusive license 112 | 113 | (a) under intellectual property rights (other than patent or 114 | trademark) Licensable by Contributor, to use, reproduce, modify, 115 | display, perform, sublicense and distribute the Modifications 116 | created by such Contributor (or portions thereof) either on an 117 | unmodified basis, with other Modifications, as Covered Code 118 | and/or as part of a Larger Work; and 119 | 120 | (b) under Patent Claims infringed by the making, using, or 121 | selling of Modifications made by that Contributor either alone 122 | and/or in combination with its Contributor Version (or portions 123 | of such combination), to make, use, sell, offer for sale, have 124 | made, and/or otherwise dispose of: 1) Modifications made by that 125 | Contributor (or portions thereof); and 2) the combination of 126 | Modifications made by that Contributor with its Contributor 127 | Version (or portions of such combination). 128 | 129 | (c) the licenses granted in Sections 2.2(a) and 2.2(b) are 130 | effective on the date Contributor first makes Commercial Use of 131 | the Covered Code. 132 | 133 | (d) Notwithstanding Section 2.2(b) above, no patent license is 134 | granted: 1) for any code that Contributor has deleted from the 135 | Contributor Version; 2) separate from the Contributor Version; 136 | 3) for infringements caused by: i) third party modifications of 137 | Contributor Version or ii) the combination of Modifications made 138 | by that Contributor with other software (except as part of the 139 | Contributor Version) or other devices; or 4) under Patent Claims 140 | infringed by Covered Code in the absence of Modifications made by 141 | that Contributor. 142 | 143 | 3. Distribution Obligations. 144 | 145 | 3.1. Application of License. 146 | The Modifications which You create or to which You contribute are 147 | governed by the terms of this License, including without limitation 148 | Section 2.2. The Source Code version of Covered Code may be 149 | distributed only under the terms of this License or a future version 150 | of this License released under Section 6.1, and You must include a 151 | copy of this License with every copy of the Source Code You 152 | distribute. You may not offer or impose any terms on any Source Code 153 | version that alters or restricts the applicable version of this 154 | License or the recipients' rights hereunder. However, You may include 155 | an additional document offering the additional rights described in 156 | Section 3.5. 157 | 158 | 3.2. Availability of Source Code. 159 | Any Modification which You create or to which You contribute must be 160 | made available in Source Code form under the terms of this License 161 | either on the same media as an Executable version or via an accepted 162 | Electronic Distribution Mechanism to anyone to whom you made an 163 | Executable version available; and if made available via Electronic 164 | Distribution Mechanism, must remain available for at least twelve (12) 165 | months after the date it initially became available, or at least six 166 | (6) months after a subsequent version of that particular Modification 167 | has been made available to such recipients. You are responsible for 168 | ensuring that the Source Code version remains available even if the 169 | Electronic Distribution Mechanism is maintained by a third party. 170 | 171 | 3.3. Description of Modifications. 172 | You must cause all Covered Code to which You contribute to contain a 173 | file documenting the changes You made to create that Covered Code and 174 | the date of any change. You must include a prominent statement that 175 | the Modification is derived, directly or indirectly, from Original 176 | Code provided by the Initial Developer and including the name of the 177 | Initial Developer in (a) the Source Code, and (b) in any notice in an 178 | Executable version or related documentation in which You describe the 179 | origin or ownership of the Covered Code. 180 | 181 | 3.4. Intellectual Property Matters 182 | (a) Third Party Claims. 183 | If Contributor has knowledge that a license under a third party's 184 | intellectual property rights is required to exercise the rights 185 | granted by such Contributor under Sections 2.1 or 2.2, 186 | Contributor must include a text file with the Source Code 187 | distribution titled "LEGAL" which describes the claim and the 188 | party making the claim in sufficient detail that a recipient will 189 | know whom to contact. If Contributor obtains such knowledge after 190 | the Modification is made available as described in Section 3.2, 191 | Contributor shall promptly modify the LEGAL file in all copies 192 | Contributor makes available thereafter and shall take other steps 193 | (such as notifying appropriate mailing lists or newsgroups) 194 | reasonably calculated to inform those who received the Covered 195 | Code that new knowledge has been obtained. 196 | 197 | (b) Contributor APIs. 198 | If Contributor's Modifications include an application programming 199 | interface and Contributor has knowledge of patent licenses which 200 | are reasonably necessary to implement that API, Contributor must 201 | also include this information in the LEGAL file. 202 | 203 | (c) Representations. 204 | Contributor represents that, except as disclosed pursuant to 205 | Section 3.4(a) above, Contributor believes that Contributor's 206 | Modifications are Contributor's original creation(s) and/or 207 | Contributor has sufficient rights to grant the rights conveyed by 208 | this License. 209 | 210 | 3.5. Required Notices. 211 | You must duplicate the notice in Exhibit A in each file of the Source 212 | Code. If it is not possible to put such notice in a particular Source 213 | Code file due to its structure, then You must include such notice in a 214 | location (such as a relevant directory) where a user would be likely 215 | to look for such a notice. If You created one or more Modification(s) 216 | You may add your name as a Contributor to the notice described in 217 | Exhibit A. You must also duplicate this License in any documentation 218 | for the Source Code where You describe recipients' rights or ownership 219 | rights relating to Covered Code. You may choose to offer, and to 220 | charge a fee for, warranty, support, indemnity or liability 221 | obligations to one or more recipients of Covered Code. However, You 222 | may do so only on Your own behalf, and not on behalf of the Initial 223 | Developer or any Contributor. You must make it absolutely clear than 224 | any such warranty, support, indemnity or liability obligation is 225 | offered by You alone, and You hereby agree to indemnify the Initial 226 | Developer and every Contributor for any liability incurred by the 227 | Initial Developer or such Contributor as a result of warranty, 228 | support, indemnity or liability terms You offer. 229 | 230 | 3.6. Distribution of Executable Versions. 231 | You may distribute Covered Code in Executable form only if the 232 | requirements of Section 3.1-3.5 have been met for that Covered Code, 233 | and if You include a notice stating that the Source Code version of 234 | the Covered Code is available under the terms of this License, 235 | including a description of how and where You have fulfilled the 236 | obligations of Section 3.2. The notice must be conspicuously included 237 | in any notice in an Executable version, related documentation or 238 | collateral in which You describe recipients' rights relating to the 239 | Covered Code. You may distribute the Executable version of Covered 240 | Code or ownership rights under a license of Your choice, which may 241 | contain terms different from this License, provided that You are in 242 | compliance with the terms of this License and that the license for the 243 | Executable version does not attempt to limit or alter the recipient's 244 | rights in the Source Code version from the rights set forth in this 245 | License. If You distribute the Executable version under a different 246 | license You must make it absolutely clear that any terms which differ 247 | from this License are offered by You alone, not by the Initial 248 | Developer or any Contributor. You hereby agree to indemnify the 249 | Initial Developer and every Contributor for any liability incurred by 250 | the Initial Developer or such Contributor as a result of any such 251 | terms You offer. 252 | 253 | 3.7. Larger Works. 254 | You may create a Larger Work by combining Covered Code with other code 255 | not governed by the terms of this License and distribute the Larger 256 | Work as a single product. In such a case, You must make sure the 257 | requirements of this License are fulfilled for the Covered Code. 258 | 259 | 4. Inability to Comply Due to Statute or Regulation. 260 | 261 | If it is impossible for You to comply with any of the terms of this 262 | License with respect to some or all of the Covered Code due to 263 | statute, judicial order, or regulation then You must: (a) comply with 264 | the terms of this License to the maximum extent possible; and (b) 265 | describe the limitations and the code they affect. Such description 266 | must be included in the LEGAL file described in Section 3.4 and must 267 | be included with all distributions of the Source Code. Except to the 268 | extent prohibited by statute or regulation, such description must be 269 | sufficiently detailed for a recipient of ordinary skill to be able to 270 | understand it. 271 | 272 | 5. Application of this License. 273 | 274 | This License applies to code to which the Initial Developer has 275 | attached the notice in Exhibit A and to related Covered Code. 276 | 277 | 6. Versions of the License. 278 | 279 | 6.1. New Versions. 280 | Netscape Communications Corporation ("Netscape") may publish revised 281 | and/or new versions of the License from time to time. Each version 282 | will be given a distinguishing version number. 283 | 284 | 6.2. Effect of New Versions. 285 | Once Covered Code has been published under a particular version of the 286 | License, You may always continue to use it under the terms of that 287 | version. You may also choose to use such Covered Code under the terms 288 | of any subsequent version of the License published by Netscape. No one 289 | other than Netscape has the right to modify the terms applicable to 290 | Covered Code created under this License. 291 | 292 | 6.3. Derivative Works. 293 | If You create or use a modified version of this License (which you may 294 | only do in order to apply it to code which is not already Covered Code 295 | governed by this License), You must (a) rename Your license so that 296 | the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", 297 | "MPL", "NPL" or any confusingly similar phrase do not appear in your 298 | license (except to note that your license differs from this License) 299 | and (b) otherwise make it clear that Your version of the license 300 | contains terms which differ from the Mozilla Public License and 301 | Netscape Public License. (Filling in the name of the Initial 302 | Developer, Original Code or Contributor in the notice described in 303 | Exhibit A shall not of themselves be deemed to be modifications of 304 | this License.) 305 | 306 | 7. DISCLAIMER OF WARRANTY. 307 | 308 | COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 309 | WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 310 | WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF 311 | DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. 312 | THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE 313 | IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, 314 | YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE 315 | COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER 316 | OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF 317 | ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 318 | 319 | 8. TERMINATION. 320 | 321 | 8.1. This License and the rights granted hereunder will terminate 322 | automatically if You fail to comply with terms herein and fail to cure 323 | such breach within 30 days of becoming aware of the breach. All 324 | sublicenses to the Covered Code which are properly granted shall 325 | survive any termination of this License. Provisions which, by their 326 | nature, must remain in effect beyond the termination of this License 327 | shall survive. 328 | 329 | 8.2. If You initiate litigation by asserting a patent infringement 330 | claim (excluding declatory judgment actions) against Initial Developer 331 | or a Contributor (the Initial Developer or Contributor against whom 332 | You file such action is referred to as "Participant") alleging that: 333 | 334 | (a) such Participant's Contributor Version directly or indirectly 335 | infringes any patent, then any and all rights granted by such 336 | Participant to You under Sections 2.1 and/or 2.2 of this License 337 | shall, upon 60 days notice from Participant terminate prospectively, 338 | unless if within 60 days after receipt of notice You either: (i) 339 | agree in writing to pay Participant a mutually agreeable reasonable 340 | royalty for Your past and future use of Modifications made by such 341 | Participant, or (ii) withdraw Your litigation claim with respect to 342 | the Contributor Version against such Participant. If within 60 days 343 | of notice, a reasonable royalty and payment arrangement are not 344 | mutually agreed upon in writing by the parties or the litigation claim 345 | is not withdrawn, the rights granted by Participant to You under 346 | Sections 2.1 and/or 2.2 automatically terminate at the expiration of 347 | the 60 day notice period specified above. 348 | 349 | (b) any software, hardware, or device, other than such Participant's 350 | Contributor Version, directly or indirectly infringes any patent, then 351 | any rights granted to You by such Participant under Sections 2.1(b) 352 | and 2.2(b) are revoked effective as of the date You first made, used, 353 | sold, distributed, or had made, Modifications made by that 354 | Participant. 355 | 356 | 8.3. If You assert a patent infringement claim against Participant 357 | alleging that such Participant's Contributor Version directly or 358 | indirectly infringes any patent where such claim is resolved (such as 359 | by license or settlement) prior to the initiation of patent 360 | infringement litigation, then the reasonable value of the licenses 361 | granted by such Participant under Sections 2.1 or 2.2 shall be taken 362 | into account in determining the amount or value of any payment or 363 | license. 364 | 365 | 8.4. In the event of termination under Sections 8.1 or 8.2 above, 366 | all end user license agreements (excluding distributors and resellers) 367 | which have been validly granted by You or any distributor hereunder 368 | prior to termination shall survive termination. 369 | 370 | 9. LIMITATION OF LIABILITY. 371 | 372 | UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT 373 | (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL 374 | DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, 375 | OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR 376 | ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY 377 | CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, 378 | WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER 379 | COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN 380 | INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF 381 | LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY 382 | RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW 383 | PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE 384 | EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO 385 | THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 386 | 387 | 10. U.S. GOVERNMENT END USERS. 388 | 389 | The Covered Code is a "commercial item," as that term is defined in 390 | 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer 391 | software" and "commercial computer software documentation," as such 392 | terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 393 | C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), 394 | all U.S. Government End Users acquire Covered Code with only those 395 | rights set forth herein. 396 | 397 | 11. MISCELLANEOUS. 398 | 399 | This License represents the complete agreement concerning subject 400 | matter hereof. If any provision of this License is held to be 401 | unenforceable, such provision shall be reformed only to the extent 402 | necessary to make it enforceable. This License shall be governed by 403 | California law provisions (except to the extent applicable law, if 404 | any, provides otherwise), excluding its conflict-of-law provisions. 405 | With respect to disputes in which at least one party is a citizen of, 406 | or an entity chartered or registered to do business in the United 407 | States of America, any litigation relating to this License shall be 408 | subject to the jurisdiction of the Federal Courts of the Northern 409 | District of California, with venue lying in Santa Clara County, 410 | California, with the losing party responsible for costs, including 411 | without limitation, court costs and reasonable attorneys' fees and 412 | expenses. The application of the United Nations Convention on 413 | Contracts for the International Sale of Goods is expressly excluded. 414 | Any law or regulation which provides that the language of a contract 415 | shall be construed against the drafter shall not apply to this 416 | License. 417 | 418 | 12. RESPONSIBILITY FOR CLAIMS. 419 | 420 | As between Initial Developer and the Contributors, each party is 421 | responsible for claims and damages arising, directly or indirectly, 422 | out of its utilization of rights under this License and You agree to 423 | work with Initial Developer and Contributors to distribute such 424 | responsibility on an equitable basis. Nothing herein is intended or 425 | shall be deemed to constitute any admission of liability. 426 | 427 | 13. MULTIPLE-LICENSED CODE. 428 | 429 | Initial Developer may designate portions of the Covered Code as 430 | "Multiple-Licensed". "Multiple-Licensed" means that the Initial 431 | Developer permits you to utilize portions of the Covered Code under 432 | Your choice of the NPL or the alternative licenses, if any, specified 433 | by the Initial Developer in the file described in Exhibit A. 434 | 435 | EXHIBIT A -Mozilla Public License. 436 | 437 | ``The contents of this file are subject to the Mozilla Public License 438 | Version 1.1 (the "License"); you may not use this file except in 439 | compliance with the License. You may obtain a copy of the License at 440 | https://www.mozilla.org/MPL/ 441 | 442 | Software distributed under the License is distributed on an "AS IS" 443 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 444 | License for the specific language governing rights and limitations 445 | under the License. 446 | 447 | The Original Code is RabbitMQ Management Plugin. 448 | 449 | The Initial Developer of the Original Code is GoPivotal, Inc. 450 | Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.'' 451 | 452 | [NOTE: The text of this Exhibit A may differ slightly from the text of 453 | the notices in the Source Code files of the Original Code. You should 454 | use the text of this Exhibit A rather than the text found in the 455 | Original Code Source Code for Your Modifications.] 456 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = rabbitmq_test 2 | 3 | BUILD_DEPS = rabbitmq_codegen 4 | DEPS = amqp_client rabbitmq_java_client meck 5 | 6 | ifneq ($(IS_DEP),1) 7 | # Include rabbit as a dependency when the testsuite is started from this 8 | # project. However, do not include it when the testsuite is started from 9 | # rabbit itself. 10 | TEST_DEPS += rabbit 11 | endif 12 | 13 | # We need many plugins for their Cuttlefish schemas. 14 | TEST_DEPS += rabbitmq_amqp1_0 \ 15 | rabbitmq_auth_backend_amqp \ 16 | rabbitmq_auth_backend_http \ 17 | rabbitmq_auth_backend_ldap \ 18 | rabbitmq_clusterer \ 19 | rabbitmq_management \ 20 | rabbitmq_metronome \ 21 | rabbitmq_mqtt \ 22 | rabbitmq_stomp \ 23 | rabbitmq_web_mqtt \ 24 | rabbitmq_web_stomp 25 | 26 | DEP_PLUGINS = rabbit_common/mk/rabbitmq-build.mk \ 27 | rabbit_common/mk/rabbitmq-run.mk \ 28 | rabbit_common/mk/rabbitmq-tests.mk \ 29 | rabbit_common/mk/rabbitmq-tools.mk 30 | 31 | # FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be 32 | # reviewed and merged. 33 | 34 | ERLANG_MK_REPO = https://github.com/rabbitmq/erlang.mk.git 35 | ERLANG_MK_COMMIT = rabbitmq-tmp 36 | 37 | include rabbitmq-components.mk 38 | include erlang.mk 39 | 40 | TEST_RABBIT_PORT=5672 41 | TEST_HARE_PORT=5673 42 | TEST_RABBIT_SSL_PORT=5671 43 | TEST_HARE_SSL_PORT=5670 44 | 45 | FILTER ?= all 46 | COVER ?= true 47 | 48 | ifeq ($(COVER), true) 49 | COVER_START = start-cover 50 | COVER_STOP = stop-cover 51 | endif 52 | 53 | WITH_BROKER_TEST_COMMANDS := rabbit_test_runner:run_in_broker(\"$(CURDIR)/test\",\"$(FILTER)\") 54 | 55 | # This requires Erlang R15B+. 56 | STANDALONE_TEST_COMMANDS := rabbit_test_runner:run_multi(\"$(DEPS_DIR)\",\"$(CURDIR)/test\",\"$(FILTER)\",$(COVER),none) 57 | 58 | pre-standalone-tests:: test-tmpdir 59 | 60 | RMQ_ERLC_OPTS := -Derlang_r15b_or_later 61 | 62 | RMQ_ERLC_OPTS += -I $(DEPS_DIR)/rabbit_common/include \ 63 | -I $(RABBITMQ_BROKER_DIR)/include \ 64 | -pa $(RABBITMQ_BROKER_DIR)/ebin 65 | 66 | # This requires Erlang R13B+. 67 | SSL_VERIFY_OPTION :={verify,verify_peer},{fail_if_no_peer_cert,false} 68 | export SSL_CERTS_DIR := $(realpath certs) 69 | export PASSWORD := test 70 | RABBIT_BROKER_OPTIONS := "-rabbit ssl_listeners [{\\\"0.0.0.0\\\",$(TEST_RABBIT_SSL_PORT)}] -rabbit ssl_options [{cacertfile,\\\"$(SSL_CERTS_DIR)/testca/cacert.pem\\\"},{certfile,\\\"$(SSL_CERTS_DIR)/server/cert.pem\\\"},{keyfile,\\\"$(SSL_CERTS_DIR)/server/key.pem\\\"},$(SSL_VERIFY_OPTION)] -rabbit auth_mechanisms ['PLAIN','AMQPLAIN','EXTERNAL','RABBIT-CR-DEMO']" 71 | HARE_BROKER_OPTIONS := "-rabbit ssl_listeners [{\\\"0.0.0.0\\\",$(TEST_HARE_SSL_PORT)}] -rabbit ssl_options [{cacertfile,\\\"$(SSL_CERTS_DIR)/testca/cacert.pem\\\"},{certfile,\\\"$(SSL_CERTS_DIR)/server/cert.pem\\\"},{keyfile,\\\"$(SSL_CERTS_DIR)/server/key.pem\\\"},$(SSL_VERIFY_OPTION)] -rabbit auth_mechanisms ['PLAIN','AMQPLAIN','EXTERNAL','RABBIT-CR-DEMO']" 72 | 73 | TESTS_FAILED := echo '\n============'\ 74 | '\nTESTS FAILED'\ 75 | '\n============\n' 76 | 77 | TEST_EBIN_DIR = $(CURDIR)/test 78 | JAVA_CLIENT_DIR = $(DEPS_DIR)/rabbitmq_java_client 79 | RABBITMQ_TEST_DIR = $(CURDIR) 80 | RABBITMQ_UMBRELLA_DIR = $(CURDIR)/../.. 81 | export RABBITMQ_TEST_DIR 82 | 83 | tests:: full 84 | 85 | full: 86 | $(test_verbose) OK=true && \ 87 | $(MAKE) prepare && \ 88 | trap '$(MAKE) cleanup' EXIT INT && \ 89 | { $(MAKE) run-tests || { OK=false; $(TESTS_FAILED); } } && \ 90 | { $(MAKE) run-lazy-vq-tests || { OK=false; $(TESTS_FAILED); } } && \ 91 | { $(MAKE) run-qpid-testsuite || { OK=false; $(TESTS_FAILED); } } && \ 92 | { ( cd $(JAVA_CLIENT_DIR) && MAKE=$(MAKE) $(ANT) $(ANT_FLAGS) test-suite ) || { OK=false; $(TESTS_FAILED); } } && \ 93 | { $$OK || $(TESTS_FAILED); } && $$OK 94 | 95 | unit: 96 | $(test_verbose) OK=true && \ 97 | $(MAKE) prepare && \ 98 | trap '$(MAKE) cleanup' EXIT INT && \ 99 | { $(MAKE) run-tests || OK=false; } && \ 100 | $$OK 101 | 102 | lite: 103 | $(test_verbose) OK=true && \ 104 | $(MAKE) prepare && \ 105 | trap '$(MAKE) cleanup' EXIT INT && \ 106 | { $(MAKE) run-tests || OK=false; } && \ 107 | { ( cd $(JAVA_CLIENT_DIR) && MAKE=$(MAKE) $(ANT) $(ANT_FLAGS) test-suite ) || OK=false; } && \ 108 | $$OK 109 | 110 | conformance16: 111 | $(test_verbose) OK=true && \ 112 | $(MAKE) prepare && \ 113 | trap '$(MAKE) cleanup' EXIT INT && \ 114 | { $(MAKE) run-tests || OK=false; } && \ 115 | { ( cd $(JAVA_CLIENT_DIR) && MAKE=$(MAKE) $(ANT) $(ANT_FLAGS) test-suite ) || OK=false; } && \ 116 | $$OK 117 | 118 | lazy-vq-tests: 119 | $(test_verbose) OK=true && \ 120 | $(MAKE) prepare && \ 121 | trap '$(MAKE) cleanup' EXIT INT && \ 122 | { $(MAKE) run-lazy-vq-tests || OK=false; } && \ 123 | $$OK 124 | 125 | qpid_testsuite: 126 | $(verbose) $(MAKE) update-qpid-testsuite 127 | 128 | update-qpid-testsuite: 129 | $(verbose) svn co -r 906960 https://svn.apache.org/repos/asf/qpid/trunk/qpid/python qpid_testsuite 130 | # hg clone https://rabbit-hg.eng.vmware.com/mirrors/qpid_testsuite 131 | 132 | prepare-qpid-patch: 133 | $(verbose) cd qpid_testsuite && svn diff > ../qpid_patch && cd .. 134 | 135 | run-qpid-testsuite: qpid_testsuite 136 | $(verbose) cd qpid_testsuite && svn revert -R . 137 | $(verbose) patch -N -r - -p0 -d qpid_testsuite/ < qpid_patch 138 | $(test_verbose) ! test -f $(RABBITMQ_UMBRELLA_DIR)/UMBRELLA.md || \ 139 | AMQP_SPEC_DIR=$(RABBITMQ_UMBRELLA_DIR)/rabbitmq-docs/specs \ 140 | AMQP_SPEC=$(RABBITMQ_UMBRELLA_DIR)/rabbitmq-docs/specs/amqp0-8.xml \ 141 | qpid_testsuite/qpid-python-test -m tests_0-8 -I rabbit_failing.txt 142 | $(verbose) ! test -f $(RABBITMQ_UMBRELLA_DIR)/UMBRELLA.md || \ 143 | AMQP_SPEC_DIR=$(RABBITMQ_UMBRELLA_DIR)/rabbitmq-docs/specs \ 144 | AMQP_SPEC=$(RABBITMQ_UMBRELLA_DIR)/rabbitmq-docs/specs/amqp0-9-1.xml \ 145 | qpid_testsuite/qpid-python-test -m tests_0-9 -I rabbit_failing.txt 146 | 147 | clean:: clean-qpid-testsuite 148 | 149 | clean-qpid-testsuite: 150 | $(gen_verbose) rm -rf qpid_testsuite 151 | 152 | prepare: test-dist create_ssl_certs 153 | $(verbose) rm -rf plugins/rabbitmq_metronome* 154 | $(verbose) rm -rf plugins/rabbitmq_amqp1_0* 155 | $(verbose) $(MAKE) \ 156 | RABBITMQ_NODENAME=hare \ 157 | RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 \ 158 | RABBITMQ_NODE_PORT=${TEST_HARE_PORT} \ 159 | RABBITMQ_SERVER_START_ARGS=$(HARE_BROKER_OPTIONS) \ 160 | RABBITMQ_CONFIG_FILE=/does-not-exist \ 161 | RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-rabbitmq_stomp tcp_listeners [61614] -rabbitmq_mqtt tcp_listeners [1884] -rabbitmq_web_mqtt tcp_config [{port,15676}] -rabbitmq_web_stomp port 15685 -rabbitmq_management listener [{port,15673}]" \ 162 | stop-node clean-node-db start-background-node start-rabbit-on-node \ 163 | || ($(MAKE) cleanup; false) 164 | $(verbose) $(MAKE) \ 165 | RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 \ 166 | RABBITMQ_NODE_PORT=${TEST_RABBIT_PORT} \ 167 | RABBITMQ_SERVER_START_ARGS=$(RABBIT_BROKER_OPTIONS) \ 168 | RABBITMQ_CONFIG_FILE=/does-not-exist \ 169 | stop-node clean-node-db start-background-node ${COVER_START} start-rabbit-on-node \ 170 | || ($(MAKE) cleanup; false) 171 | $(verbose) $(MAKE) RABBITMQ_NODENAME=hare stop-rabbit-on-node \ 172 | || ($(MAKE) cleanup; false) 173 | # To determine the name of the remote node to join: 174 | # 1. We use $(BASIC_SCRIPT_ENV_SETTINGS) to initialize the environment 175 | # used by the testsuite. 176 | # 2. We source rabbitmq-env to get proper values for $RABBITMQ_NODENAME 177 | # and $RABBITMQ_NODE_TYPE. 178 | # 3. We start a node with those informations to get the full node name 179 | # as computed by Erlang. 180 | $(verbose) $(RABBITMQCTL) -n hare join_cluster \ 181 | $$($(BASIC_SCRIPT_ENV_SETTINGS); \ 182 | . $(RABBITMQ_SCRIPTS_DIR)/rabbitmq-env && \ 183 | erl -A0 -noinput -boot start_clean -hidden \ 184 | $$RABBITMQ_NAME_TYPE getname-$$$$-$$RABBITMQ_NODENAME \ 185 | -eval 'io:format("~s~n", [node()]), halt(0).' \ 186 | | sed s/^getname-$$$$-//) \ 187 | || ($(MAKE) cleanup; false) 188 | $(verbose) $(MAKE) RABBITMQ_NODENAME=hare start-rabbit-on-node \ 189 | || ($(MAKE) cleanup; false) 190 | 191 | start-app: 192 | $(exec_verbose) $(MAKE) \ 193 | RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 \ 194 | RABBITMQ_NODE_PORT=${TEST_RABBIT_PORT} \ 195 | RABBITMQ_SERVER_START_ARGS=$(RABBIT_BROKER_OPTIONS) \ 196 | RABBITMQ_CONFIG_FILE=/does-not-exist \ 197 | start-rabbit-on-node 198 | 199 | stop-app: 200 | $(exec_verbose) $(MAKE) stop-rabbit-on-node 201 | 202 | restart-app: stop-app start-app 203 | 204 | start-secondary-app: 205 | $(exec_verbose) $(MAKE) RABBITMQ_NODENAME=hare start-rabbit-on-node 206 | 207 | stop-secondary-app: 208 | $(exec_verbose) $(MAKE) RABBITMQ_NODENAME=hare stop-rabbit-on-node 209 | 210 | restart-secondary-node: 211 | $(exec_verbose) $(MAKE) \ 212 | RABBITMQ_NODENAME=hare \ 213 | RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 \ 214 | RABBITMQ_NODE_PORT=${TEST_HARE_PORT} \ 215 | RABBITMQ_SERVER_START_ARGS=$(HARE_BROKER_OPTIONS) \ 216 | RABBITMQ_CONFIG_FILE=/does-not-exist \ 217 | stop-node start-background-node 218 | $(verbose) $(MAKE) RABBITMQ_NODENAME=hare start-rabbit-on-node 219 | 220 | force-snapshot: 221 | $(exec_verbose) $(MAKE) force-snapshot 222 | 223 | enable-ha: 224 | $(exec_verbose) $(RABBITMQCTL) set_policy HA \ 225 | ".*" '{"ha-mode": "all"}' 226 | 227 | disable-ha: 228 | $(exec_verbose) $(RABBITMQCTL) clear_policy HA 229 | 230 | cleanup: 231 | -$(exec_verbose) $(MAKE) \ 232 | RABBITMQ_NODENAME=hare \ 233 | RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 \ 234 | RABBITMQ_NODE_PORT=${TEST_HARE_PORT} \ 235 | RABBITMQ_SERVER_START_ARGS=$(HARE_BROKER_OPTIONS) \ 236 | RABBITMQ_CONFIG_FILE=/does-not-exist \ 237 | stop-rabbit-on-node stop-node 238 | -$(verbose) $(MAKE) \ 239 | RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 \ 240 | RABBITMQ_NODE_PORT=${TEST_RABBIT_PORT} \ 241 | RABBITMQ_SERVER_START_ARGS=$(RABBIT_BROKER_OPTIONS) \ 242 | RABBITMQ_CONFIG_FILE=/does-not-exist \ 243 | stop-rabbit-on-node ${COVER_STOP} stop-node 244 | 245 | # This requires Erlang R16B01+. 246 | create_ssl_certs: 247 | $(gen_verbose) $(MAKE) -C certs DIR=$(SSL_CERTS_DIR) clean all 248 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RabbitMQ Test Suites 2 | 3 | ## Useful targets 4 | 5 | make unit # runs the Erlang unit tests 6 | make lite # runs the Erlang unit tests and the Java client / functional tests 7 | make full # runs both the above plus the QPid test suite 8 | make tests # runs the Erlang multi-node integration tests + all of the above 9 | 10 | The multi-node tests take a long time, so you might want to run a subset: 11 | 12 | make standalone-tests FILTER=dynamic_ha # <- run just one suite 13 | make standalone-tests FILTER=dynamic_ha:change_policy # <- run just one test 14 | 15 | The multi-node tests also default to coverage on, to turn it off: 16 | 17 | make standalone-tests COVER=false 18 | 19 | `tests-with-broker` target also supports `FILTER` and `COVER` 20 | arguments, but instead runs modern single-node test - where all suites 21 | are executed inside single broker instance. 22 | 23 | This repository is not related to plugin tests; run "make tests" in a 24 | plugin directory to test that plugin. 25 | 26 | ## Kinds of tests 27 | 28 | There are different kinds of tests located inside `tests/src` directory: 29 | - Legacy test suite that can be only run as a whole using 30 | `rabbit_tests:all_tests/0`. Tests are run inside a running broker, 31 | so they could perform testing by doing direct calls to modules under 32 | the test. 33 | - Modern tests that are run in a running broker. This tests are 34 | auto-discovered by function name, which should end with `_test`. As 35 | tests are being run inside a running broker, testing can be 36 | performed by directly calling modules under the test. But as tests 37 | are run independently by eunit, some care should be taken to 38 | preserve broker in a good state. (It's not a big problem for legacy 39 | test set as they stop after detecting the first failure). 40 | - Multi-node tests, where node(s) are being setup from clean slate for 41 | every test case. This test are auto-disovered by function name, 42 | which should end with `_with`. Those `_with` functions provide 43 | meta-information about the way that nodes(s) should be setup, actual 44 | testing code should be placed in the function without `_with` 45 | postfix. 46 | 47 | ## Writing multi-node tests 48 | 49 | Multi-node test-case is represented by 2 functions: test itself with 50 | arbitary name, and setup metadata in a function named like the test 51 | function plus suffix `_with`. So, e.g. test named `abc` should be 52 | represented by: `abc/1` and `abc_with/0`. 53 | 54 | `rabbit_test_runner` calls `XXX_with/0` and performs all 55 | initializations mentioned there, accumulating some config data while 56 | doing this. Possible return values for `_with` functions are of type 57 | `rabbit_test_runner:initializers()`: 58 | - atom referencing predefined function from `rabbit_test_configs`. 59 | - function reference which should accept single parameter of type 60 | `rabbit_test_configs:config()`. 61 | - list of things mentioned above. 62 | 63 | In the end we will have a proplist (or list of proplists for each 64 | node), that will be passed to test case itself. 65 | -------------------------------------------------------------------------------- /build.config: -------------------------------------------------------------------------------- 1 | # Do *not* comment or remove core modules 2 | # unless you know what you are doing. 3 | # 4 | # Feel free to comment plugins out however. 5 | 6 | # Core modules. 7 | core/core 8 | index/* 9 | core/index 10 | core/deps 11 | 12 | # Plugins that must run before Erlang code gets compiled. 13 | plugins/erlydtl 14 | plugins/protobuffs 15 | 16 | # Core modules, continued. 17 | core/erlc 18 | core/docs 19 | core/rel 20 | core/test 21 | core/compat 22 | 23 | # Plugins. 24 | plugins/asciidoc 25 | plugins/bootstrap 26 | plugins/c_src 27 | plugins/ci 28 | plugins/ct 29 | plugins/dialyzer 30 | plugins/edoc 31 | plugins/elvis 32 | plugins/escript 33 | # plugins/eunit 34 | plugins/relx 35 | plugins/shell 36 | plugins/triq 37 | plugins/xref 38 | 39 | # Plugins enhancing the functionality of other plugins. 40 | plugins/cover 41 | 42 | # Core modules which can use variables from plugins. 43 | core/deps-tools 44 | -------------------------------------------------------------------------------- /certs/Makefile: -------------------------------------------------------------------------------- 1 | OPENSSL=openssl 2 | 3 | ifndef DIR 4 | DIR := . 5 | endif 6 | 7 | ifdef PASSWORD 8 | P12PASS := true 9 | else 10 | P12PASS := @echo No PASSWORD defined. && false 11 | endif 12 | 13 | .PRECIOUS: %/testca 14 | .PHONY: %/clean target all p12pass 15 | 16 | all: client server 17 | 18 | client server: p12pass 19 | @echo $(DIR) 20 | $(MAKE) target DIR=$(DIR) TARGET=$@ EXTENSIONS=$@_ca_extensions 21 | 22 | p12pass: 23 | $(P12PASS) 24 | 25 | target: $(DIR)/testca 26 | mkdir -p $(DIR)/$(TARGET) 27 | { ( cd $(DIR)/$(TARGET) && \ 28 | openssl genrsa -out key.pem 2048 &&\ 29 | openssl req -new -key key.pem -out req.pem -outform PEM\ 30 | -subj /CN=$$(hostname)/O=$(TARGET)/L=$$$$/ -nodes &&\ 31 | cd ../testca && \ 32 | openssl ca -config openssl.cnf -in ../$(TARGET)/req.pem -out \ 33 | ../$(TARGET)/cert.pem -notext -batch -extensions \ 34 | $(EXTENSIONS) && \ 35 | cd ../$(TARGET) && \ 36 | openssl pkcs12 -export -out keycert.p12 -in cert.pem -inkey key.pem \ 37 | -passout pass:$(PASSWORD) ) || (rm -rf $(DIR)/$(TARGET) && false); } 38 | 39 | $(DIR)/testca: 40 | mkdir -p $(DIR)/testca 41 | cp openssl.cnf $(DIR)/testca/openssl.cnf 42 | { ( cd $(DIR)/testca && \ 43 | mkdir -p certs private && \ 44 | chmod 700 private && \ 45 | echo 01 > serial && \ 46 | touch index.txt && \ 47 | openssl req -x509 -config openssl.cnf -newkey rsa:2048 -days 365 \ 48 | -out cacert.pem -outform PEM -subj /CN=MyTestCA/L=$$$$/ -nodes && \ 49 | openssl x509 -in cacert.pem -out cacert.cer -outform DER ) \ 50 | || (rm -rf $@ && false); } 51 | 52 | clean: 53 | rm -rf $(DIR)/testca 54 | rm -rf $(DIR)/server 55 | rm -rf $(DIR)/client 56 | -------------------------------------------------------------------------------- /certs/openssl.cnf: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = testca 3 | 4 | [ testca ] 5 | dir = . 6 | certificate = $dir/cacert.pem 7 | database = $dir/index.txt 8 | new_certs_dir = $dir/certs 9 | private_key = $dir/private/cakey.pem 10 | serial = $dir/serial 11 | 12 | default_crl_days = 7 13 | default_days = 365 14 | default_md = sha1 15 | 16 | policy = testca_policy 17 | x509_extensions = certificate_extensions 18 | 19 | [ testca_policy ] 20 | commonName = supplied 21 | stateOrProvinceName = optional 22 | countryName = optional 23 | emailAddress = optional 24 | organizationName = optional 25 | organizationalUnitName = optional 26 | domainComponent = optional 27 | 28 | [ certificate_extensions ] 29 | basicConstraints = CA:false 30 | 31 | [ req ] 32 | default_bits = 2048 33 | default_keyfile = ./private/cakey.pem 34 | default_md = sha1 35 | prompt = yes 36 | distinguished_name = root_ca_distinguished_name 37 | x509_extensions = root_ca_extensions 38 | 39 | [ root_ca_distinguished_name ] 40 | commonName = hostname 41 | 42 | [ root_ca_extensions ] 43 | basicConstraints = CA:true 44 | keyUsage = keyCertSign, cRLSign 45 | 46 | [ client_ca_extensions ] 47 | basicConstraints = CA:false 48 | keyUsage = digitalSignature 49 | extendedKeyUsage = 1.3.6.1.5.5.7.3.2 50 | 51 | [ server_ca_extensions ] 52 | basicConstraints = CA:false 53 | keyUsage = keyEncipherment 54 | extendedKeyUsage = 1.3.6.1.5.5.7.3.1 55 | -------------------------------------------------------------------------------- /qpid_config.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | import os 21 | 22 | AMQP_SPEC_DIR=os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "../rabbitmq-docs/specs") 23 | amqp_spec = os.path.join(AMQP_SPEC_DIR, "amqp.0-10-qpid-errata.xml") 24 | amqp_spec_0_8 = os.path.join(AMQP_SPEC_DIR, "amqp0-8.xml") 25 | amqp_spec_0_9 = os.path.join(AMQP_SPEC_DIR, "amqp0-9.xml") 26 | amqp_spec = 'file://'+os.path.join(AMQP_SPEC_DIR, 'amqp.0-10.xml') 27 | -------------------------------------------------------------------------------- /qpid_patch: -------------------------------------------------------------------------------- 1 | Index: qpid/codec.py 2 | =================================================================== 3 | --- qpid/codec.py (revision 906960) 4 | +++ qpid/codec.py (working copy) 5 | @@ -76,6 +76,7 @@ 6 | if not self.types: 7 | self.typecode(ord('S'), "longstr") 8 | self.typecode(ord('I'), "long") 9 | + self.typecode(ord('t'), "bool") 10 | 11 | def typecode(self, code, type): 12 | self.types[code] = type 13 | @@ -206,6 +207,22 @@ 14 | """ 15 | return self.unpack("!B") 16 | 17 | + def encode_bool(self, b): 18 | + """ 19 | + encodes bool (8 bits) data 't' in network byte order 20 | + """ 21 | + 22 | + if ((b is not True) and (b is not False)): 23 | + raise ValueError('Valid range of bool is True or False') 24 | + 25 | + self.pack("!B", int(b)) 26 | + 27 | + def decode_bool(self): 28 | + """ 29 | + decodes a bool (8 bits) encoded in network byte order 30 | + """ 31 | + return bool(self.unpack("!B")) 32 | + 33 | def encode_short(self, o): 34 | """ 35 | encodes short (16 bits) data 'o' in network byte order 36 | Index: qpid/testlib.py 37 | =================================================================== 38 | --- qpid/testlib.py (revision 906960) 39 | +++ qpid/testlib.py (working copy) 40 | @@ -67,8 +67,7 @@ 41 | 42 | if not self.client.closed: 43 | self.client.channel(0).connection_close(reply_code=200) 44 | - else: 45 | - self.client.close() 46 | + self.client.close() 47 | 48 | def connect(self, host=None, port=None, user=None, password=None, tune_params=None): 49 | """Create a new connction, return the Client object""" 50 | Index: qpid_config.py 51 | =================================================================== 52 | --- qpid_config.py (revision 906960) 53 | +++ qpid_config.py (working copy) 54 | @@ -19,7 +19,8 @@ 55 | 56 | import os 57 | 58 | -AMQP_SPEC_DIR=os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "specs") 59 | +AMQP_SPEC_DIR=os.environ['AMQP_SPEC_DIR'] 60 | amqp_spec = os.path.join(AMQP_SPEC_DIR, "amqp.0-10-qpid-errata.xml") 61 | -amqp_spec_0_8 = os.path.join(AMQP_SPEC_DIR, "amqp.0-8.xml") 62 | -amqp_spec_0_9 = os.path.join(AMQP_SPEC_DIR, "amqp.0-9.xml") 63 | +amqp_spec_0_8 = os.path.join(AMQP_SPEC_DIR, "amqp0-8.xml") 64 | +amqp_spec_0_9 = os.path.join(AMQP_SPEC_DIR, "amqp0-9.xml") 65 | +amqp_spec = 'file://'+os.path.join(AMQP_SPEC_DIR, 'amqp.0-10.xml') 66 | Index: tests_0-8/basic.py 67 | =================================================================== 68 | --- tests_0-8/basic.py (revision 906960) 69 | +++ tests_0-8/basic.py (working copy) 70 | @@ -98,7 +98,7 @@ 71 | channel.basic_consume(queue="") 72 | self.fail("Expected failure when consuming from unspecified queue") 73 | except Closed, e: 74 | - self.assertConnectionException(530, e.args[0]) 75 | + self.assertChannelException(404, e.args[0]) 76 | 77 | def test_consume_unique_consumers(self): 78 | """ 79 | Index: tests_0-8/exchange.py 80 | =================================================================== 81 | --- tests_0-8/exchange.py (revision 906960) 82 | +++ tests_0-8/exchange.py (working copy) 83 | @@ -138,8 +138,6 @@ 84 | # Test automatic binding by queue name. 85 | self.queue_declare(queue="d") 86 | self.assertPublishConsume(queue="d", routing_key="d") 87 | - # Test explicit bind to default queue 88 | - self.verifyDirectExchange("") 89 | 90 | 91 | # TODO aconway 2006-09-27: Fill in empty tests: 92 | @@ -318,7 +316,7 @@ 93 | self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") 94 | self.fail("Expected 530 for redeclaration of exchange with different type.") 95 | except Closed, e: 96 | - self.assertConnectionException(530, e.args[0]) 97 | + self.assertChannelException(406, e.args[0]) 98 | #cleanup 99 | other = self.connect() 100 | c2 = other.channel(1) 101 | Index: tests_0-8/queue.py 102 | =================================================================== 103 | --- tests_0-8/queue.py (revision 906960) 104 | +++ tests_0-8/queue.py (working copy) 105 | @@ -37,14 +37,10 @@ 106 | channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("two")) 107 | channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("three")) 108 | 109 | - #check that the queue now reports 3 messages: 110 | - reply = channel.queue_declare(queue="test-queue") 111 | - self.assertEqual(3, reply.message_count) 112 | - 113 | #now do the purge, then test that three messages are purged and the count drops to 0 114 | reply = channel.queue_purge(queue="test-queue"); 115 | self.assertEqual(3, reply.message_count) 116 | - reply = channel.queue_declare(queue="test-queue") 117 | + reply = channel.queue_declare(queue="test-queue", exclusive=True) 118 | self.assertEqual(0, reply.message_count) 119 | 120 | #send a further message and consume it, ensuring that the other messages are really gone 121 | @@ -71,7 +67,7 @@ 122 | channel.queue_purge() 123 | self.fail("Expected failure when purging unspecified queue") 124 | except Closed, e: 125 | - self.assertConnectionException(530, e.args[0]) 126 | + self.assertChannelException(404, e.args[0]) 127 | 128 | #cleanup 129 | other = self.connect() 130 | @@ -174,11 +170,7 @@ 131 | #check attempted deletion of non-existant queue is handled correctly: 132 | channel = self.client.channel(2) 133 | channel.channel_open() 134 | - try: 135 | - channel.queue_delete(queue="i-dont-exist", if_empty="True") 136 | - self.fail("Expected delete of non-existant queue to fail") 137 | - except Closed, e: 138 | - self.assertChannelException(404, e.args[0]) 139 | + channel.queue_delete(queue="i-dont-exist", if_empty="True") 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /rabbit_failing.txt: -------------------------------------------------------------------------------- 1 | tests_0-8.basic.BasicTests.test_ack 2 | tests_0-8.basic.BasicTests.test_consume_no_local 3 | tests_0-8.basic.BasicTests.test_qos_prefetch_count 4 | tests_0-8.basic.BasicTests.test_qos_prefetch_size 5 | tests_0-8.broker.BrokerTests.test_basic_delivery_immediate 6 | tests_0-8.broker.BrokerTests.test_channel_flow 7 | tests_0-8.tx.TxTests.test_auto_rollback 8 | tests_0-8.tx.TxTests.test_rollback 9 | tests_0-9.query.* 10 | -------------------------------------------------------------------------------- /rabbitmq-components.mk: -------------------------------------------------------------------------------- 1 | ifeq ($(.DEFAULT_GOAL),) 2 | # Define default goal to `all` because this file defines some targets 3 | # before the inclusion of erlang.mk leading to the wrong target becoming 4 | # the default. 5 | .DEFAULT_GOAL = all 6 | endif 7 | 8 | # Automatically add rabbitmq-common to the dependencies, at least for 9 | # the Makefiles. 10 | ifneq ($(PROJECT),rabbit_common) 11 | ifneq ($(PROJECT),rabbitmq_public_umbrella) 12 | ifeq ($(filter rabbit_common,$(DEPS)),) 13 | DEPS += rabbit_common 14 | endif 15 | endif 16 | endif 17 | 18 | # -------------------------------------------------------------------- 19 | # RabbitMQ components. 20 | # -------------------------------------------------------------------- 21 | 22 | # For RabbitMQ repositories, we want to checkout branches which match 23 | # the parent project. For instance, if the parent project is on a 24 | # release tag, dependencies must be on the same release tag. If the 25 | # parent project is on a topic branch, dependencies must be on the same 26 | # topic branch or fallback to `stable` or `master` whichever was the 27 | # base of the topic branch. 28 | 29 | dep_amqp_client = git_rmq rabbitmq-erlang-client $(current_rmq_ref) $(base_rmq_ref) master 30 | dep_rabbit = git_rmq rabbitmq-server $(current_rmq_ref) $(base_rmq_ref) master 31 | dep_rabbit_common = git_rmq rabbitmq-common $(current_rmq_ref) $(base_rmq_ref) master 32 | dep_rabbitmq_amqp1_0 = git_rmq rabbitmq-amqp1.0 $(current_rmq_ref) $(base_rmq_ref) master 33 | dep_rabbitmq_auth_backend_amqp = git_rmq rabbitmq-auth-backend-amqp $(current_rmq_ref) $(base_rmq_ref) master 34 | dep_rabbitmq_auth_backend_http = git_rmq rabbitmq-auth-backend-http $(current_rmq_ref) $(base_rmq_ref) master 35 | dep_rabbitmq_auth_backend_ldap = git_rmq rabbitmq-auth-backend-ldap $(current_rmq_ref) $(base_rmq_ref) master 36 | dep_rabbitmq_auth_mechanism_ssl = git_rmq rabbitmq-auth-mechanism-ssl $(current_rmq_ref) $(base_rmq_ref) master 37 | dep_rabbitmq_boot_steps_visualiser = git_rmq rabbitmq-boot-steps-visualiser $(current_rmq_ref) $(base_rmq_ref) master 38 | dep_rabbitmq_clusterer = git_rmq rabbitmq-clusterer $(current_rmq_ref) $(base_rmq_ref) master 39 | dep_rabbitmq_codegen = git_rmq rabbitmq-codegen $(current_rmq_ref) $(base_rmq_ref) master 40 | dep_rabbitmq_consistent_hash_exchange = git_rmq rabbitmq-consistent-hash-exchange $(current_rmq_ref) $(base_rmq_ref) master 41 | dep_rabbitmq_delayed_message_exchange = git_rmq rabbitmq-delayed-message-exchange $(current_rmq_ref) $(base_rmq_ref) master 42 | dep_rabbitmq_dotnet_client = git_rmq rabbitmq-dotnet-client $(current_rmq_ref) $(base_rmq_ref) master 43 | dep_rabbitmq_event_exchange = git_rmq rabbitmq-event-exchange $(current_rmq_ref) $(base_rmq_ref) master 44 | dep_rabbitmq_federation = git_rmq rabbitmq-federation $(current_rmq_ref) $(base_rmq_ref) master 45 | dep_rabbitmq_federation_management = git_rmq rabbitmq-federation-management $(current_rmq_ref) $(base_rmq_ref) master 46 | dep_rabbitmq_java_client = git_rmq rabbitmq-java-client $(current_rmq_ref) $(base_rmq_ref) master 47 | dep_rabbitmq_jms_client = git_rmq rabbitmq-jms-client $(current_rmq_ref) $(base_rmq_ref) master 48 | dep_rabbitmq_jms_topic_exchange = git_rmq rabbitmq-jms-topic-exchange $(current_rmq_ref) $(base_rmq_ref) master 49 | dep_rabbitmq_lvc = git_rmq rabbitmq-lvc-plugin $(current_rmq_ref) $(base_rmq_ref) master 50 | dep_rabbitmq_management = git_rmq rabbitmq-management $(current_rmq_ref) $(base_rmq_ref) master 51 | dep_rabbitmq_management_agent = git_rmq rabbitmq-management-agent $(current_rmq_ref) $(base_rmq_ref) master 52 | dep_rabbitmq_management_exchange = git_rmq rabbitmq-management-exchange $(current_rmq_ref) $(base_rmq_ref) master 53 | dep_rabbitmq_management_themes = git_rmq rabbitmq-management-themes $(current_rmq_ref) $(base_rmq_ref) master 54 | dep_rabbitmq_management_visualiser = git_rmq rabbitmq-management-visualiser $(current_rmq_ref) $(base_rmq_ref) master 55 | dep_rabbitmq_message_timestamp = git_rmq rabbitmq-message-timestamp $(current_rmq_ref) $(base_rmq_ref) master 56 | dep_rabbitmq_metronome = git_rmq rabbitmq-metronome $(current_rmq_ref) $(base_rmq_ref) master 57 | dep_rabbitmq_mqtt = git_rmq rabbitmq-mqtt $(current_rmq_ref) $(base_rmq_ref) master 58 | dep_rabbitmq_objc_client = git_rmq rabbitmq-objc-client $(current_rmq_ref) $(base_rmq_ref) master 59 | dep_rabbitmq_recent_history_exchange = git_rmq rabbitmq-recent-history-exchange $(current_rmq_ref) $(base_rmq_ref) master 60 | dep_rabbitmq_routing_node_stamp = git_rmq rabbitmq-routing-node-stamp $(current_rmq_ref) $(base_rmq_ref) master 61 | dep_rabbitmq_rtopic_exchange = git_rmq rabbitmq-rtopic-exchange $(current_rmq_ref) $(base_rmq_ref) master 62 | dep_rabbitmq_sharding = git_rmq rabbitmq-sharding $(current_rmq_ref) $(base_rmq_ref) master 63 | dep_rabbitmq_shovel = git_rmq rabbitmq-shovel $(current_rmq_ref) $(base_rmq_ref) master 64 | dep_rabbitmq_shovel_management = git_rmq rabbitmq-shovel-management $(current_rmq_ref) $(base_rmq_ref) master 65 | dep_rabbitmq_stomp = git_rmq rabbitmq-stomp $(current_rmq_ref) $(base_rmq_ref) master 66 | dep_rabbitmq_toke = git_rmq rabbitmq-toke $(current_rmq_ref) $(base_rmq_ref) master 67 | dep_rabbitmq_top = git_rmq rabbitmq-top $(current_rmq_ref) $(base_rmq_ref) master 68 | dep_rabbitmq_tracing = git_rmq rabbitmq-tracing $(current_rmq_ref) $(base_rmq_ref) master 69 | dep_rabbitmq_trust_store = git_rmq rabbitmq-trust-store $(current_rmq_ref) $(base_rmq_ref) master 70 | dep_rabbitmq_test = git_rmq rabbitmq-test $(current_rmq_ref) $(base_rmq_ref) master 71 | dep_rabbitmq_web_dispatch = git_rmq rabbitmq-web-dispatch $(current_rmq_ref) $(base_rmq_ref) master 72 | dep_rabbitmq_web_stomp = git_rmq rabbitmq-web-stomp $(current_rmq_ref) $(base_rmq_ref) master 73 | dep_rabbitmq_web_stomp_examples = git_rmq rabbitmq-web-stomp-examples $(current_rmq_ref) $(base_rmq_ref) master 74 | dep_rabbitmq_web_mqtt = git_rmq rabbitmq-web-mqtt $(current_rmq_ref) $(base_rmq_ref) master 75 | dep_rabbitmq_web_mqtt_examples = git_rmq rabbitmq-web-mqtt-examples $(current_rmq_ref) $(base_rmq_ref) master 76 | dep_rabbitmq_website = git_rmq rabbitmq-website $(current_rmq_ref) $(base_rmq_ref) live master 77 | dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) master 78 | dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) master 79 | 80 | dep_rabbitmq_public_umbrella = git_rmq rabbitmq-public-umbrella $(current_rmq_ref) $(base_rmq_ref) master 81 | 82 | # FIXME: As of 2015-11-20, we depend on Ranch 1.2.1, but erlang.mk 83 | # defaults to Ranch 1.1.0. All projects depending indirectly on Ranch 84 | # needs to add "ranch" as a BUILD_DEPS. The list of projects needing 85 | # this workaround are: 86 | # o rabbitmq-web-stomp 87 | dep_ranch = git https://github.com/ninenines/ranch 1.2.1 88 | 89 | RABBITMQ_COMPONENTS = amqp_client \ 90 | rabbit \ 91 | rabbit_common \ 92 | rabbitmq_amqp1_0 \ 93 | rabbitmq_auth_backend_amqp \ 94 | rabbitmq_auth_backend_http \ 95 | rabbitmq_auth_backend_ldap \ 96 | rabbitmq_auth_mechanism_ssl \ 97 | rabbitmq_boot_steps_visualiser \ 98 | rabbitmq_clusterer \ 99 | rabbitmq_codegen \ 100 | rabbitmq_consistent_hash_exchange \ 101 | rabbitmq_delayed_message_exchange \ 102 | rabbitmq_dotnet_client \ 103 | rabbitmq_event_exchange \ 104 | rabbitmq_federation \ 105 | rabbitmq_federation_management \ 106 | rabbitmq_java_client \ 107 | rabbitmq_jms_client \ 108 | rabbitmq_jms_topic_exchange \ 109 | rabbitmq_lvc \ 110 | rabbitmq_management \ 111 | rabbitmq_management_agent \ 112 | rabbitmq_management_exchange \ 113 | rabbitmq_management_themes \ 114 | rabbitmq_management_visualiser \ 115 | rabbitmq_message_timestamp \ 116 | rabbitmq_metronome \ 117 | rabbitmq_mqtt \ 118 | rabbitmq_objc_client \ 119 | rabbitmq_recent_history_exchange \ 120 | rabbitmq_routing_node_stamp \ 121 | rabbitmq_rtopic_exchange \ 122 | rabbitmq_sharding \ 123 | rabbitmq_shovel \ 124 | rabbitmq_shovel_management \ 125 | rabbitmq_stomp \ 126 | rabbitmq_test \ 127 | rabbitmq_toke \ 128 | rabbitmq_top \ 129 | rabbitmq_tracing \ 130 | rabbitmq_trust_store \ 131 | rabbitmq_web_dispatch \ 132 | rabbitmq_web_mqtt \ 133 | rabbitmq_web_mqtt_examples \ 134 | rabbitmq_web_stomp \ 135 | rabbitmq_web_stomp_examples \ 136 | rabbitmq_website 137 | 138 | # Several components have a custom erlang.mk/build.config, mainly 139 | # to disable eunit. Therefore, we can't use the top-level project's 140 | # erlang.mk copy. 141 | NO_AUTOPATCH += $(RABBITMQ_COMPONENTS) 142 | 143 | ifeq ($(origin current_rmq_ref),undefined) 144 | ifneq ($(wildcard .git),) 145 | current_rmq_ref := $(shell (\ 146 | ref=$$(git branch --list | awk '/^\* \(.*detached / {ref=$$0; sub(/.*detached [^ ]+ /, "", ref); sub(/\)$$/, "", ref); print ref; exit;} /^\* / {ref=$$0; sub(/^\* /, "", ref); print ref; exit}');\ 147 | if test "$$(git rev-parse --short HEAD)" != "$$ref"; then echo "$$ref"; fi)) 148 | else 149 | current_rmq_ref := master 150 | endif 151 | endif 152 | export current_rmq_ref 153 | 154 | ifeq ($(origin base_rmq_ref),undefined) 155 | ifneq ($(wildcard .git),) 156 | base_rmq_ref := $(shell \ 157 | (git rev-parse --verify -q stable >/dev/null && \ 158 | git merge-base --is-ancestor $$(git merge-base master HEAD) stable && \ 159 | echo stable) || \ 160 | echo master) 161 | else 162 | base_rmq_ref := master 163 | endif 164 | endif 165 | export base_rmq_ref 166 | 167 | # Repository URL selection. 168 | # 169 | # First, we infer other components' location from the current project 170 | # repository URL, if it's a Git repository: 171 | # - We take the "origin" remote URL as the base 172 | # - The current project name and repository name is replaced by the 173 | # target's properties: 174 | # eg. rabbitmq-common is replaced by rabbitmq-codegen 175 | # eg. rabbit_common is replaced by rabbitmq_codegen 176 | # 177 | # If cloning from this computed location fails, we fallback to RabbitMQ 178 | # upstream which is GitHub. 179 | 180 | # Maccro to transform eg. "rabbit_common" to "rabbitmq-common". 181 | rmq_cmp_repo_name = $(word 2,$(dep_$(1))) 182 | 183 | # Upstream URL for the current project. 184 | RABBITMQ_COMPONENT_REPO_NAME := $(call rmq_cmp_repo_name,$(PROJECT)) 185 | RABBITMQ_UPSTREAM_FETCH_URL ?= https://github.com/rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git 186 | RABBITMQ_UPSTREAM_PUSH_URL ?= git@github.com:rabbitmq/$(RABBITMQ_COMPONENT_REPO_NAME).git 187 | 188 | # Current URL for the current project. If this is not a Git clone, 189 | # default to the upstream Git repository. 190 | ifneq ($(wildcard .git),) 191 | git_origin_fetch_url := $(shell git config remote.origin.url) 192 | git_origin_push_url := $(shell git config remote.origin.pushurl || git config remote.origin.url) 193 | RABBITMQ_CURRENT_FETCH_URL ?= $(git_origin_fetch_url) 194 | RABBITMQ_CURRENT_PUSH_URL ?= $(git_origin_push_url) 195 | else 196 | RABBITMQ_CURRENT_FETCH_URL ?= $(RABBITMQ_UPSTREAM_FETCH_URL) 197 | RABBITMQ_CURRENT_PUSH_URL ?= $(RABBITMQ_UPSTREAM_PUSH_URL) 198 | endif 199 | 200 | # Macro to replace the following pattern: 201 | # 1. /foo.git -> /bar.git 202 | # 2. /foo -> /bar 203 | # 3. /foo/ -> /bar/ 204 | subst_repo_name = $(patsubst %/$(1)/%,%/$(2)/%,$(patsubst %/$(1),%/$(2),$(patsubst %/$(1).git,%/$(2).git,$(3)))) 205 | 206 | # Macro to replace both the project's name (eg. "rabbit_common") and 207 | # repository name (eg. "rabbitmq-common") by the target's equivalent. 208 | # 209 | # This macro is kept on one line because we don't want whitespaces in 210 | # the returned value, as it's used in $(dep_fetch_git_rmq) in a shell 211 | # single-quoted string. 212 | dep_rmq_repo = $(if $(dep_$(2)),$(call subst_repo_name,$(PROJECT),$(2),$(call subst_repo_name,$(RABBITMQ_COMPONENT_REPO_NAME),$(call rmq_cmp_repo_name,$(2)),$(1))),$(pkg_$(1)_repo)) 213 | 214 | dep_rmq_commits = $(if $(dep_$(1)), \ 215 | $(wordlist 3,$(words $(dep_$(1))),$(dep_$(1))), \ 216 | $(pkg_$(1)_commit)) 217 | 218 | define dep_fetch_git_rmq 219 | fetch_url1='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_FETCH_URL),$(1))'; \ 220 | fetch_url2='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_FETCH_URL),$(1))'; \ 221 | if test "$$$$fetch_url1" != '$(RABBITMQ_CURRENT_FETCH_URL)' && \ 222 | git clone -q -n -- "$$$$fetch_url1" $(DEPS_DIR)/$(call dep_name,$(1)); then \ 223 | fetch_url="$$$$fetch_url1"; \ 224 | push_url='$(call dep_rmq_repo,$(RABBITMQ_CURRENT_PUSH_URL),$(1))'; \ 225 | elif git clone -q -n -- "$$$$fetch_url2" $(DEPS_DIR)/$(call dep_name,$(1)); then \ 226 | fetch_url="$$$$fetch_url2"; \ 227 | push_url='$(call dep_rmq_repo,$(RABBITMQ_UPSTREAM_PUSH_URL),$(1))'; \ 228 | fi; \ 229 | cd $(DEPS_DIR)/$(call dep_name,$(1)) && ( \ 230 | $(foreach ref,$(call dep_rmq_commits,$(1)), \ 231 | git checkout -q $(ref) >/dev/null 2>&1 || \ 232 | ) \ 233 | (echo "error: no valid pathspec among: $(call dep_rmq_commits,$(1))" \ 234 | 1>&2 && false) ) && \ 235 | (test "$$$$fetch_url" = "$$$$push_url" || \ 236 | git remote set-url --push origin "$$$$push_url") 237 | endef 238 | 239 | # -------------------------------------------------------------------- 240 | # Component distribution. 241 | # -------------------------------------------------------------------- 242 | 243 | list-dist-deps:: 244 | @: 245 | 246 | prepare-dist:: 247 | @: 248 | 249 | # -------------------------------------------------------------------- 250 | # Run a RabbitMQ node (moved from rabbitmq-run.mk as a workaround). 251 | # -------------------------------------------------------------------- 252 | 253 | # Add "rabbit" to the build dependencies when the user wants to start 254 | # a broker or to the test dependencies when the user wants to test a 255 | # project. 256 | # 257 | # NOTE: This should belong to rabbitmq-run.mk. Unfortunately, it is 258 | # loaded *after* erlang.mk which is too late to add a dependency. That's 259 | # why rabbitmq-components.mk knows the list of targets which start a 260 | # broker and add "rabbit" to the dependencies in this case. 261 | 262 | ifneq ($(PROJECT),rabbit) 263 | ifeq ($(filter rabbit,$(DEPS) $(BUILD_DEPS)),) 264 | RUN_RMQ_TARGETS = run-broker \ 265 | run-background-broker \ 266 | run-node \ 267 | run-background-node \ 268 | start-background-node 269 | 270 | ifneq ($(filter $(RUN_RMQ_TARGETS),$(MAKECMDGOALS)),) 271 | BUILD_DEPS += rabbit 272 | endif 273 | endif 274 | 275 | ifeq ($(filter rabbit,$(DEPS) $(BUILD_DEPS) $(TEST_DEPS)),) 276 | ifneq ($(filter check tests tests-with-broker test,$(MAKECMDGOALS)),) 277 | TEST_DEPS += rabbit 278 | endif 279 | endif 280 | endif 281 | 282 | # -------------------------------------------------------------------- 283 | # rabbitmq-components.mk checks. 284 | # -------------------------------------------------------------------- 285 | 286 | ifeq ($(PROJECT),rabbit_common) 287 | else ifdef SKIP_RMQCOMP_CHECK 288 | else ifeq ($(IS_DEP),1) 289 | else ifneq ($(filter co up,$(MAKECMDGOALS)),) 290 | else 291 | # In all other cases, rabbitmq-components.mk must be in sync. 292 | deps:: check-rabbitmq-components.mk 293 | fetch-deps: check-rabbitmq-components.mk 294 | endif 295 | 296 | # If this project is under the Umbrella project, we override $(DEPS_DIR) 297 | # to point to the Umbrella's one. We also disable `make distclean` so 298 | # $(DEPS_DIR) is not accidentally removed. 299 | 300 | ifneq ($(wildcard ../../UMBRELLA.md),) 301 | UNDER_UMBRELLA = 1 302 | else ifneq ($(wildcard UMBRELLA.md),) 303 | UNDER_UMBRELLA = 1 304 | endif 305 | 306 | ifeq ($(UNDER_UMBRELLA),1) 307 | ifneq ($(PROJECT),rabbitmq_public_umbrella) 308 | DEPS_DIR ?= $(abspath ..) 309 | 310 | distclean:: distclean-components 311 | @: 312 | 313 | distclean-components: 314 | endif 315 | 316 | ifneq ($(filter distclean distclean-deps,$(MAKECMDGOALS)),) 317 | SKIP_DEPS = 1 318 | endif 319 | endif 320 | 321 | UPSTREAM_RMQ_COMPONENTS_MK = $(DEPS_DIR)/rabbit_common/mk/rabbitmq-components.mk 322 | 323 | check-rabbitmq-components.mk: 324 | $(verbose) cmp -s rabbitmq-components.mk \ 325 | $(UPSTREAM_RMQ_COMPONENTS_MK) || \ 326 | (echo "error: rabbitmq-components.mk must be updated!" 1>&2; \ 327 | false) 328 | 329 | ifeq ($(PROJECT),rabbit_common) 330 | rabbitmq-components-mk: 331 | @: 332 | else 333 | rabbitmq-components-mk: 334 | $(gen_verbose) cp -a $(UPSTREAM_RMQ_COMPONENTS_MK) . 335 | ifeq ($(DO_COMMIT),yes) 336 | $(verbose) git diff --quiet rabbitmq-components.mk \ 337 | || git commit -m 'Update rabbitmq-components.mk' rabbitmq-components.mk 338 | endif 339 | endif 340 | -------------------------------------------------------------------------------- /src/rabbit_runtime_parameters_test.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_runtime_parameters_test). 18 | -behaviour(rabbit_runtime_parameter). 19 | -behaviour(rabbit_policy_validator). 20 | 21 | -include("rabbit.hrl"). 22 | 23 | -export([validate/5, notify/4, notify_clear/3]). 24 | -export([register/0, unregister/0]). 25 | -export([validate_policy/1]). 26 | -export([register_policy_validator/0, unregister_policy_validator/0]). 27 | 28 | %---------------------------------------------------------------------------- 29 | 30 | register() -> 31 | rabbit_registry:register(runtime_parameter, <<"test">>, ?MODULE). 32 | 33 | unregister() -> 34 | rabbit_registry:unregister(runtime_parameter, <<"test">>). 35 | 36 | validate(_, <<"test">>, <<"good">>, _Term, _User) -> ok; 37 | validate(_, <<"test">>, <<"maybe">>, <<"good">>, _User) -> ok; 38 | validate(_, <<"test">>, <<"admin">>, _Term, none) -> ok; 39 | validate(_, <<"test">>, <<"admin">>, _Term, User) -> 40 | case lists:member(administrator, User#user.tags) of 41 | true -> ok; 42 | false -> {error, "meh", []} 43 | end; 44 | validate(_, <<"test">>, _, _, _) -> {error, "meh", []}. 45 | 46 | notify(_, _, _, _) -> ok. 47 | notify_clear(_, _, _) -> ok. 48 | 49 | %---------------------------------------------------------------------------- 50 | 51 | register_policy_validator() -> 52 | rabbit_registry:register(policy_validator, <<"testeven">>, ?MODULE), 53 | rabbit_registry:register(policy_validator, <<"testpos">>, ?MODULE). 54 | 55 | unregister_policy_validator() -> 56 | rabbit_registry:unregister(policy_validator, <<"testeven">>), 57 | rabbit_registry:unregister(policy_validator, <<"testpos">>). 58 | 59 | validate_policy([{<<"testeven">>, Terms}]) when is_list(Terms) -> 60 | case length(Terms) rem 2 =:= 0 of 61 | true -> ok; 62 | false -> {error, "meh", []} 63 | end; 64 | 65 | validate_policy([{<<"testpos">>, Terms}]) when is_list(Terms) -> 66 | case lists:all(fun (N) -> is_integer(N) andalso N > 0 end, Terms) of 67 | true -> ok; 68 | false -> {error, "meh", []} 69 | end; 70 | 71 | validate_policy(_) -> 72 | {error, "meh", []}. 73 | -------------------------------------------------------------------------------- /src/rabbit_test_configs.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | -module(rabbit_test_configs). 17 | 18 | -include_lib("amqp_client/include/amqp_client.hrl"). 19 | 20 | -export([enable_plugins/1]). 21 | -export([cluster/2, cluster_ab/1, cluster_abc/1, start_and_connect_a/1, start_ab/1, start_abc/1]). 22 | -export([start_connections/1, build_cluster/1]). 23 | -export([ha_policy_all/1, ha_policy_two_pos/1, ha_policy_two_pos_batch_sync/1]). 24 | -export([start_nodes/2, start_nodes/3, add_to_cluster/2, 25 | rabbitmqctl/2, rabbitmqctl_fail/2, rabbitmqctl_list/2]). 26 | -export([stop_nodes/1, start_node/1, stop_node/1, kill_node/1, restart_node/1, 27 | start_node_fail/1, execute/1]). 28 | -export([cover_work_factor/2]). 29 | 30 | -import(rabbit_test_util, [set_ha_policy/3, set_ha_policy/4, a2b/1]). 31 | -import(rabbit_misc, [pget/2, pget/3]). 32 | 33 | -ifdef(use_specs). 34 | 35 | -type initial_config() :: [{atom(), term()}]. 36 | -type nodes_config() :: [initial_config()]. 37 | -type config() :: initial_config() | nodes_config(). 38 | -type maybe_formatted_string() :: string() | {string(), [term()]}. 39 | -export_type([config/0]). 40 | 41 | -spec cluster(config(), NodeNames :: [atom()]) -> nodes_config(). 42 | -spec cluster_ab(config()) -> nodes_config(). 43 | -spec cluster_abc(config()) -> nodes_config(). 44 | -spec start_ab(config()) -> nodes_config(). 45 | -spec start_abc(config()) -> nodes_config(). 46 | -spec start_and_connect_a(config()) -> nodes_config(). 47 | 48 | -spec start_connections(nodes_config()) -> nodes_config(). 49 | -spec build_cluster(nodes_config()) -> nodes_config(). 50 | 51 | -spec ha_policy_all(nodes_config()) -> nodes_config(). 52 | -spec ha_policy_two_pos(nodes_config()) -> nodes_config(). 53 | -spec ha_policy_two_pos_batch_sync(nodes_config()) -> nodes_config(). 54 | 55 | -spec start_nodes(config(), NodeNames ::[atom()]) -> nodes_config(). 56 | -spec start_nodes(config(), NodeNames ::[atom()], FirstPort :: non_neg_integer()) -> nodes_config(). 57 | -spec add_to_cluster(nodes_config(), nodes_config()) -> nodes_config(). 58 | -spec rabbitmqctl(initial_config(), maybe_formatted_string()) -> string(). 59 | -spec rabbitmqctl_fail(initial_config(), maybe_formatted_string()) -> string(). 60 | 61 | -spec stop_nodes(config()) -> config(). 62 | -spec start_node(initial_config()) -> initial_config(). 63 | -spec stop_node(initial_config()) -> initial_config(). 64 | -spec kill_node(initial_config()) -> initial_config(). 65 | -spec restart_node(initial_config()) -> initial_config(). 66 | -spec start_node_fail(initial_config()) -> ok. 67 | -spec execute(maybe_formatted_string()) -> string(). 68 | 69 | -spec cover_work_factor(number(), initial_config()) -> number(). 70 | 71 | -endif. 72 | 73 | -define(INITIAL_KEYS, [cover, base, server, test_framework, plugins]). 74 | -define(NON_RUNNING_KEYS, ?INITIAL_KEYS ++ [nodename, initial_nodename, 75 | port, mnesia_dir]). 76 | 77 | cluster_ab(InitialCfg) -> cluster(InitialCfg, [a, b]). 78 | cluster_abc(InitialCfg) -> cluster(InitialCfg, [a, b, c]). 79 | start_ab(InitialCfg) -> start_nodes(InitialCfg, [a, b]). 80 | start_abc(InitialCfg) -> start_nodes(InitialCfg, [a, b, c]). 81 | 82 | cluster(InitialCfg, NodeNames) -> 83 | start_connections(build_cluster(start_nodes(InitialCfg, NodeNames))). 84 | 85 | start_and_connect_a(InitialCfg) -> 86 | start_connections(start_nodes(InitialCfg, [a])). 87 | 88 | 89 | start_nodes(InitialCfg, NodeNames) -> 90 | start_nodes(InitialCfg, NodeNames, 5672). 91 | 92 | start_nodes(InitialCfg0, NodeNames, FirstPort) -> 93 | {ok, Already0} = net_adm:names(), 94 | Already = [list_to_atom(N) || {N, _P} <- Already0], 95 | [check_node_not_running(Node, Already) || Node <- NodeNames], 96 | Ports = lists:seq(FirstPort, length(NodeNames) + FirstPort - 1), 97 | InitialCfgs = case InitialCfg0 of 98 | [{_, _}|_] -> [InitialCfg0 || _ <- NodeNames]; 99 | _ -> InitialCfg0 100 | end, 101 | Nodes = [[{nodename, N}, {port, P}, 102 | {initial_nodename, N}, 103 | {mnesia_dir, "mnesia"} | 104 | strip_non_initial(Cfg)] 105 | || {N, P, Cfg} <- lists:zip3(NodeNames, Ports, InitialCfgs)], 106 | [start_node(Node) || Node <- Nodes]. 107 | 108 | check_node_not_running(Node, Already) -> 109 | case lists:member(Node, Already) of 110 | true -> exit({node_already_running, Node}); 111 | false -> ok 112 | end. 113 | 114 | strip_non_initial(Cfg) -> 115 | [{K, V} || {K, V} <- Cfg, lists:member(K, ?INITIAL_KEYS)]. 116 | 117 | strip_running(Cfg) -> 118 | [{K, V} || {K, V} <- Cfg, lists:member(K, ?NON_RUNNING_KEYS)]. 119 | 120 | enable_plugins(Cfg) -> 121 | enable_plugins(pget(plugins, Cfg), pget(server, Cfg), Cfg). 122 | 123 | enable_plugins(none, _Server, _Cfg) -> ok; 124 | enable_plugins(_Dir, Server, Cfg) -> 125 | RabbitmqPluginsCmd = rabbitmq_plugins_cmd(Server), 126 | R = execute(Cfg, RabbitmqPluginsCmd ++ " list -m"), 127 | Plugins = string:join(string:tokens(R, "\n"), " "), 128 | execute(Cfg, {RabbitmqPluginsCmd ++ " set --offline ~s", [Plugins]}), 129 | ok. 130 | 131 | start_node(Cfg0) -> 132 | Node = rabbit_nodes:make(pget(nodename, Cfg0)), 133 | Cfg = [{node, Node} | Cfg0], 134 | Server = pget(server, Cfg), 135 | Linked = execute_bg(Cfg, rabbitmq_server_cmd(Server)), 136 | rabbitmqctl(Cfg, {"wait ~s", [pid_file(Cfg)]}), 137 | OSPid = rpc:call(Node, os, getpid, []), 138 | %% The cover system thinks all nodes with the same name are the 139 | %% same node and will automaticaly re-establish cover as soon as 140 | %% we see them, so we only want to start cover once per node name 141 | %% for the entire test run. 142 | case {pget(cover, Cfg), lists:member(Node, cover:which_nodes())} of 143 | {true, false} -> cover:start([Node]); 144 | _ -> ok 145 | end, 146 | [{os_pid, OSPid}, 147 | {linked_pid, Linked} | Cfg]. 148 | 149 | start_node_fail(Cfg0) -> 150 | Node = rabbit_nodes:make(pget(nodename, Cfg0)), 151 | Cfg = [{node, Node}, {acceptable_exit_codes, lists:seq(1, 255)} | Cfg0], 152 | Server = pget(server, Cfg), 153 | execute(Cfg, rabbitmq_server_cmd(Server)), 154 | ok. 155 | 156 | build_cluster([First | Rest]) -> 157 | add_to_cluster([First], Rest). 158 | 159 | add_to_cluster([First | _] = Existing, New) -> 160 | [cluster_with(First, Node) || Node <- New], 161 | Existing ++ New. 162 | 163 | cluster_with(Cfg, NewCfg) -> 164 | Node = pget(node, Cfg), 165 | rabbitmqctl(NewCfg, stop_app), 166 | rabbitmqctl(NewCfg, {"join_cluster ~s", [Node]}), 167 | rabbitmqctl(NewCfg, start_app). 168 | 169 | rabbitmqctl_list(Cfg, Str) -> 170 | StdOut = rabbitmqctl(Cfg, Str), 171 | [<<"Listing", _/binary>>|Rows] = re:split(StdOut, <<"\n">>, [trim]), 172 | [re:split(Row, <<"\t">>) || Row <- Rows]. 173 | 174 | rabbitmqctl(Cfg, Str) -> 175 | Node = pget(node, Cfg), 176 | Server = pget(server, Cfg), 177 | Cmd = case Node of 178 | undefined -> {"~s", [fmt(Str)]}; 179 | _ -> {"-n ~s ~s", [Node, fmt(Str)]} 180 | end, 181 | execute(Cfg, {rabbitmqctl_cmd(Server) ++ " ~s", [fmt(Cmd)]}). 182 | 183 | rabbitmqctl_fail(Cfg, Str) -> 184 | rabbitmqctl([{acceptable_exit_codes, lists:seq(1, 255)} | Cfg], Str). 185 | 186 | ha_policy_all([Cfg | _] = Cfgs) -> 187 | set_ha_policy(Cfg, <<".*">>, <<"all">>), 188 | Cfgs. 189 | 190 | ha_policy_two_pos([Cfg | _] = Cfgs) -> 191 | Members = [a2b(pget(node, C)) || C <- Cfgs], 192 | TwoNodes = [M || M <- lists:sublist(Members, 2)], 193 | set_ha_policy(Cfg, <<"^ha.two.">>, {<<"nodes">>, TwoNodes}, 194 | [{<<"ha-promote-on-shutdown">>, <<"always">>}]), 195 | set_ha_policy(Cfg, <<"^ha.auto.">>, {<<"nodes">>, TwoNodes}, 196 | [{<<"ha-sync-mode">>, <<"automatic">>}, 197 | {<<"ha-promote-on-shutdown">>, <<"always">>}]), 198 | Cfgs. 199 | 200 | ha_policy_two_pos_batch_sync([Cfg | _] = Cfgs) -> 201 | Members = [a2b(pget(node, C)) || C <- Cfgs], 202 | TwoNodes = [M || M <- lists:sublist(Members, 2)], 203 | set_ha_policy(Cfg, <<"^ha.two.">>, {<<"nodes">>, TwoNodes}, 204 | [{<<"ha-promote-on-shutdown">>, <<"always">>}]), 205 | set_ha_policy(Cfg, <<"^ha.auto.">>, {<<"nodes">>, TwoNodes}, 206 | [{<<"ha-sync-mode">>, <<"automatic">>}, 207 | {<<"ha-sync-batch-size">>, 200}, 208 | {<<"ha-promote-on-shutdown">>, <<"always">>}]), 209 | Cfgs. 210 | 211 | start_connections(Nodes) -> [start_connection(Node) || Node <- Nodes]. 212 | 213 | start_connection(Cfg) -> 214 | Port = pget(port, Cfg), 215 | {ok, Conn} = amqp_connection:start(#amqp_params_network{port = Port}), 216 | {ok, Ch} = amqp_connection:open_channel(Conn), 217 | [{connection, Conn}, {channel, Ch} | Cfg]. 218 | 219 | stop_nodes(Nodes) -> [stop_node(Node) || Node <- Nodes]. 220 | 221 | stop_node(Cfg) -> 222 | maybe_flush_cover(Cfg), 223 | catch rabbitmqctl(Cfg, {"stop ~s", [pid_file(Cfg)]}), 224 | strip_running(Cfg). 225 | 226 | kill_node(Cfg) -> 227 | maybe_flush_cover(Cfg), 228 | OSPid = pget(os_pid, Cfg), 229 | catch execute(Cfg, {"kill -9 ~s", [OSPid]}), 230 | await_os_pid_death(OSPid), 231 | strip_running(Cfg). 232 | 233 | await_os_pid_death(OSPid) -> 234 | case rabbit_misc:is_os_process_alive(OSPid) of 235 | true -> timer:sleep(100), 236 | await_os_pid_death(OSPid); 237 | false -> ok 238 | end. 239 | 240 | restart_node(Cfg) -> 241 | start_node(stop_node(Cfg)). 242 | 243 | maybe_flush_cover(Cfg) -> 244 | case pget(cover, Cfg) of 245 | true -> cover:flush(pget(node, Cfg)); 246 | false -> ok 247 | end. 248 | 249 | %% Cover slows things down enough that if we are sending messages in 250 | %% bulk, we want to send fewer or we'll be here all day... 251 | cover_work_factor(Without, Cfg) -> 252 | case pget(cover, Cfg) of 253 | true -> trunc(Without * 0.1); 254 | false -> Without 255 | end. 256 | 257 | %%---------------------------------------------------------------------------- 258 | 259 | rabbitmqctl_cmd(Server) -> 260 | case os:getenv("RABBITMQCTL") of 261 | false -> Server ++ "/scripts/rabbitmqctl"; 262 | Path -> Path 263 | end. 264 | 265 | rabbitmq_plugins_cmd(Server) -> 266 | case os:getenv("RABBITMQ_PLUGINS") of 267 | false -> Server ++ "/scripts/rabbitmq-plugins"; 268 | Path -> Path 269 | end. 270 | 271 | rabbitmq_server_cmd(Server) -> 272 | case os:getenv("RABBITMQ_SERVER") of 273 | false -> Server ++ "/scripts/rabbitmq-server"; 274 | Path -> Path 275 | end. 276 | 277 | execute(Cmd) -> 278 | execute([], Cmd, [0]). 279 | 280 | execute(Cfg, Cmd) -> 281 | %% code 137 -> killed with SIGKILL which we do in some tests 282 | execute(environment(Cfg), Cmd, pget(acceptable_exit_codes, Cfg, [0, 137])). 283 | 284 | execute(Env0, Cmd0, AcceptableExitCodes) -> 285 | Env = [{"RABBITMQ_" ++ K, fmt(V)} || {K, V} <- Env0], 286 | Cmd = fmt(Cmd0), 287 | error_logger:info_msg("Invoking '~s' with env:~n~p~n", [Cmd, Env]), 288 | Port = erlang:open_port( 289 | {spawn, "/usr/bin/env sh -c \"" ++ Cmd ++ "\""}, 290 | [{env, Env}, exit_status, 291 | stderr_to_stdout, use_stdio]), 292 | port_receive_loop(Port, "", AcceptableExitCodes). 293 | 294 | environment(Cfg) -> 295 | Nodename = pget(nodename, Cfg), 296 | Plugins = pget(plugins, Cfg), 297 | case Nodename of 298 | undefined -> 299 | plugins_env(Plugins); 300 | _ -> 301 | Port = pget(port, Cfg), 302 | Base = pget(base, Cfg), 303 | InitialNodename = pget(initial_nodename, Cfg), 304 | TestFramework = pget(test_framework, Cfg), 305 | [{"MNESIA_DIR", {"~s/~s/~s", [Base, InitialNodename, 306 | pget(mnesia_dir, Cfg)]}}, 307 | {"SCHEMA_DIR", {"~s/~s/schema", [Base, Nodename]}}, 308 | {"PLUGINS_EXPAND_DIR", {"~s/~s/plugins", [Base, Nodename]}}, 309 | {"LOG_BASE", {"~s/~s/log", [Base, Nodename]}}, 310 | {"NODENAME", {"~s", [Nodename]}}, 311 | {"NODE_PORT", {"~B", [Port]}}, 312 | {"PID_FILE", pid_file(Cfg)}, 313 | {"CONFIG_FILE", "/some/path/which/does/not/exist"}, 314 | {"ALLOW_INPUT", "1"}, %% Needed to make it close on exit 315 | %% Bit of a hack - only needed for mgmt tests. 316 | {"SERVER_START_ARGS", 317 | {"-rabbitmq_management listener [{port,1~B}]", [Port]}}, 318 | {"SERVER_ERL_ARGS", 319 | %% Next two lines are defaults 320 | "+K true +A30 +P 1048576 " 321 | "-kernel inet_default_connect_options [{nodelay,true}]"} 322 | | plugins_env(Plugins)] 323 | end. 324 | 325 | plugins_env(none) -> 326 | [{"ENABLED_PLUGINS_FILE", "/does-not-exist"}]; 327 | plugins_env(Dir) -> 328 | [{"PLUGINS_DIR", {"~s/plugins", [Dir]}}, 329 | {"PLUGINS_EXPAND_DIR", {"~s/expand", [Dir]}}, 330 | {"ENABLED_PLUGINS_FILE", {"~s/enabled_plugins", [Dir]}}]. 331 | 332 | pid_file(Cfg) -> 333 | Nodename = pget(nodename, Cfg), 334 | rabbit_misc:format("~s/~s/~s.pid", [pget(base, Cfg), Nodename, Nodename]). 335 | 336 | port_receive_loop(Port, Stdout, AcceptableExitCodes) -> 337 | receive 338 | {Port, {exit_status, X}} -> 339 | Fmt = "Command exited with code ~p~nStdout: ~s~n", 340 | Args = [X, Stdout], 341 | case lists:member(X, AcceptableExitCodes) of 342 | true -> error_logger:info_msg(Fmt, Args), 343 | Stdout; 344 | false -> error_logger:error_msg(Fmt, Args), 345 | exit({exit_status, X, AcceptableExitCodes, Stdout}) 346 | end; 347 | {Port, {data, Out}} -> 348 | port_receive_loop(Port, Stdout ++ Out, AcceptableExitCodes) 349 | end. 350 | 351 | execute_bg(Cfg, Cmd) -> 352 | spawn_link(fun () -> 353 | execute(Cfg, Cmd), 354 | {links, Links} = process_info(self(), links), 355 | [unlink(L) || L <- Links] 356 | end). 357 | 358 | fmt({Fmt, Args}) -> rabbit_misc:format(Fmt, Args); 359 | fmt(Str) -> Str. 360 | -------------------------------------------------------------------------------- /src/rabbit_test_runner.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License at 4 | %% https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 8 | %% License for the specific language governing rights and limitations 9 | %% under the License. 10 | %% 11 | %% The Original Code is RabbitMQ 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_test_runner). 18 | 19 | -include_lib("kernel/include/file.hrl"). 20 | 21 | -define(TIMEOUT, 600). 22 | 23 | -import(rabbit_misc, [pget/2]). 24 | 25 | -export([run_in_broker/2, run_multi/5]). 26 | 27 | -ifdef(use_specs). 28 | 29 | -type fun_initializer() :: fun((rabbitmq_test_configs:config()) -> rabbitmq_test_configs:config()). 30 | -type predefined_initializer() :: atom(). 31 | -type single_initializer() :: fun_initializer() | predefined_initializer(). 32 | -type initializers() :: single_initializer() | [single_initializer()]. 33 | -export_type([initializers/0]). 34 | 35 | -spec apply_config(initializers(), rabbitmq_test_configs:config()) -> rabbitmq_test_configs:config(). 36 | 37 | -endif. 38 | 39 | run_in_broker(Dir, Filter) -> 40 | io:format("~nIn-broker tests~n================~n~n", []), 41 | application:start(inets), 42 | eunit:test(make_tests_single(Dir, Filter, ?TIMEOUT), []). 43 | 44 | run_multi(DepsDir, Dir, Filter, Cover, PluginsDir) -> 45 | io:format("~nMulti-node tests~n================~n~n", []), 46 | %% Umbrella does not give us -sname 47 | net_kernel:start([?MODULE, shortnames]), 48 | inets:start(), %% Used by HTTP tests 49 | error_logger:tty(false), 50 | case Cover of 51 | true -> io:format("Cover compiling..."), 52 | cover:start(), 53 | BrokerPath = filename:join(os:getenv("DEPS_DIR"), "rabbit"), 54 | ok = rabbit_misc:enable_cover([BrokerPath]), 55 | io:format(" done.~n~n"); 56 | false -> ok 57 | end, 58 | R = eunit:test(make_tests_multi( 59 | DepsDir, Dir, Filter, Cover, PluginsDir, ?TIMEOUT), []), 60 | case Cover of 61 | true -> io:format("~nCover reporting..."), 62 | ok = rabbit_misc:report_cover(), 63 | io:format(" done.~n~n"); 64 | false -> ok 65 | end, 66 | R. 67 | 68 | make_tests_single(Dir, Filter, Timeout) -> 69 | {Filtered, AllCount, Width} = find_tests(Dir, Filter, "_test"), 70 | io:format("Running ~B of ~B tests; FILTER=~s~n~n", 71 | [length(Filtered), AllCount, Filter]), 72 | [make_test_single(M, FWith, F, ShowHeading, Timeout, Width) 73 | || {M, FWith, F, ShowHeading} <- annotate_show_heading(Filtered)]. 74 | 75 | make_tests_multi(DepsDir, Dir, Filter, Cover, PluginsDir, Timeout) -> 76 | {Filtered, AllCount, Width} = find_tests(Dir, Filter, "_with"), 77 | io:format("Running ~B of ~B tests; FILTER=~s; COVER=~s~n~n", 78 | [length(Filtered), AllCount, Filter, Cover]), 79 | Cfg = [{cover, Cover}, 80 | {base, basedir()}, 81 | {server, filename:join(DepsDir, "rabbit")}, 82 | {test_framework, filename:join(DepsDir, "rabbitmq_test")}, 83 | {plugins, PluginsDir}], 84 | rabbit_test_configs:enable_plugins(Cfg), 85 | [make_test_multi(M, FWith, F, ShowHeading, Timeout, Width, Cfg) 86 | || {M, FWith, F, ShowHeading} <- annotate_show_heading(Filtered)]. 87 | 88 | find_tests(Dir, Filter, Suffix) -> 89 | All = [{M, FWith, F} || 90 | M <- modules(Dir), 91 | {FWith, _Arity} <- proplists:get_value(exports, M:module_info()), 92 | string:right(atom_to_list(FWith), length(Suffix)) =:= Suffix, 93 | F <- [truncate_function_name(FWith, length(Suffix))]], 94 | Filtered = [Test || {M, _FWith, F} = Test <- All, 95 | should_run(M, F, Filter)], 96 | Width = case Filtered of 97 | [] -> 0; 98 | _ -> lists:max([atom_length(F) || {_, _, F} <- Filtered]) 99 | end, 100 | {Filtered, length(All), Width}. 101 | 102 | make_test_single(M, FWith, F, ShowHeading, Timeout, Width) -> 103 | {timeout, 104 | Timeout, 105 | fun () -> 106 | maybe_print_heading(M, ShowHeading), 107 | io:format(user, "~s [running]", [name(F, Width)]), 108 | M:FWith(), 109 | io:format(user, " [PASSED].~n", []) 110 | end}. 111 | 112 | make_test_multi(M, FWith, F, ShowHeading, Timeout, Width, InitialCfg) -> 113 | {setup, 114 | fun () -> 115 | maybe_print_heading(M, ShowHeading), 116 | io:format(user, "~s [setup]", [name(F, Width)]), 117 | setup_error_logger(M, F, basedir()), 118 | recursive_delete(pget(base, InitialCfg)), 119 | try 120 | apply_config(M:FWith(), InitialCfg) 121 | catch 122 | error:{Type, Error, Cfg, Stack} -> 123 | case Cfg of 124 | InitialCfg -> ok; %% [0] 125 | _ -> rabbit_test_configs:stop_nodes(Cfg) 126 | end, 127 | exit({Type, Error, Stack}) 128 | end 129 | end, 130 | fun (Nodes) -> 131 | rabbit_test_configs:stop_nodes(Nodes), 132 | %% Partition tests change this, let's revert 133 | net_kernel:set_net_ticktime(60, 1), 134 | io:format(user, ".~n", []) 135 | end, 136 | fun (Nodes) -> 137 | [{timeout, 138 | Timeout, 139 | fun () -> 140 | [link(pget(linked_pid, N)) || N <- Nodes], 141 | io:format(user, " [running]", []), 142 | %%try 143 | M:F(Nodes), 144 | io:format(user, " [PASSED]", []) 145 | %% catch 146 | %% Type:Reason -> 147 | %% io:format(user, "YYY stop~n", []), 148 | %% rabbit_test_configs:stop_nodes(Nodes), 149 | %% exit({Type, Reason, erlang:get_stacktrace()}) 150 | %% end 151 | end}] 152 | end}. 153 | %% [0] If we didn't get as far as starting any nodes then we only have 154 | %% one proplist for initial config, not several per node. So avoid 155 | %% trying to "stop" it - it won't work (and there's nothing to do 156 | %% anyway). 157 | 158 | maybe_print_heading(M, true) -> 159 | io:format(user, "~n~s~n~s~n", [M, string:chars($-, atom_length(M))]); 160 | maybe_print_heading(_M, false) -> 161 | ok. 162 | 163 | apply_config(Things, Cfg) when is_list(Things) -> 164 | lists:foldl(fun apply_config/2, Cfg, Things); 165 | apply_config(F, Cfg) when is_atom(F) -> 166 | apply_config(fun (C) -> rabbit_test_configs:F(C) end, Cfg); 167 | apply_config(F, Cfg) when is_function(F) -> 168 | try 169 | F(Cfg) 170 | catch 171 | Type:Error -> erlang:error({Type, Error, Cfg, erlang:get_stacktrace()}) 172 | end. 173 | 174 | annotate_show_heading(List) -> 175 | annotate_show_heading(List, undefined). 176 | 177 | annotate_show_heading([], _) -> 178 | []; 179 | annotate_show_heading([{M, FWith, F} | Rest], Current) -> 180 | [{M, FWith, F, M =/= Current} | annotate_show_heading(Rest, M)]. 181 | 182 | setup_error_logger(M, F, Base) -> 183 | case error_logger_logfile_filename() of 184 | {error, no_log_file} -> ok; 185 | _ -> ok = error_logger:logfile(close) 186 | end, 187 | FN = rabbit_misc:format("~s/~s:~s.log", [os:getenv("TEST_TMPDIR"), M, F]), 188 | ensure_dir(Base), 189 | ok = error_logger:logfile({open, FN}). 190 | 191 | truncate_function_name(FWith, Length) -> 192 | FName = atom_to_list(FWith), 193 | list_to_atom(string:substr(FName, 1, length(FName) - Length)). 194 | 195 | should_run(_M, _F, "all") -> true; 196 | should_run(M, F, Filter) -> MF = rabbit_misc:format("~s:~s", [M, F]), 197 | case re:run(MF, Filter) of 198 | {match, _} -> true; 199 | nomatch -> false 200 | end. 201 | 202 | ensure_dir(Path) -> 203 | case file:read_file_info(Path) of 204 | {ok, #file_info{type=regular}} -> exit({exists_as_file, Path}); 205 | {ok, #file_info{type=directory}} -> ok; 206 | _ -> file:make_dir(Path) 207 | end. 208 | 209 | modules(RelDir) -> 210 | {ok, Files} = file:list_dir(RelDir), 211 | [M || F <- Files, 212 | M <- case string:tokens(F, ".") of 213 | [MStr, "beam"] -> [list_to_atom(MStr)]; 214 | _ -> [] 215 | end]. 216 | 217 | recursive_delete(Dir) -> 218 | rabbit_test_configs:execute({"rm -rf ~s", [Dir]}). 219 | 220 | name(F, Width) -> 221 | R = atom_to_list(F), 222 | R ++ ":" ++ string:chars($ , Width - length(R)). 223 | 224 | atom_length(A) -> length(atom_to_list(A)). 225 | 226 | basedir() -> filename:join(os:getenv("TEST_TMPDIR"), "nodes"). 227 | 228 | %% reimplement error_logger:logfile(filename) only using 229 | %% gen_event:call/4 instead of gen_event:call/3 with our old friend 230 | %% the 5 second timeout. Grr. 231 | error_logger_logfile_filename() -> 232 | case gen_event:call( 233 | error_logger, error_logger_file_h, filename, infinity) of 234 | {error,_} -> {error, no_log_file}; 235 | Val -> Val 236 | end. 237 | -------------------------------------------------------------------------------- /src/rabbit_test_util.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2017 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | -module(rabbit_test_util). 17 | 18 | -include_lib("amqp_client/include/amqp_client.hrl"). 19 | -import(rabbit_misc, [pget/2]). 20 | 21 | -compile(export_all). 22 | 23 | set_ha_policy(Cfg, Pattern, Policy) -> 24 | set_ha_policy(Cfg, Pattern, Policy, []). 25 | 26 | set_ha_policy(Cfg, Pattern, Policy, Extra) -> 27 | set_policy(Cfg, Pattern, Pattern, <<"queues">>, ha_policy(Policy) ++ Extra). 28 | 29 | ha_policy(<<"all">>) -> [{<<"ha-mode">>, <<"all">>}]; 30 | ha_policy({Mode, Params}) -> [{<<"ha-mode">>, Mode}, 31 | {<<"ha-params">>, Params}]. 32 | 33 | set_policy(Cfg, Name, Pattern, ApplyTo, Definition) -> 34 | ok = rpc:call(pget(node, Cfg), rabbit_policy, set, 35 | [<<"/">>, Name, Pattern, Definition, 0, ApplyTo]). 36 | 37 | clear_policy(Cfg, Name) -> 38 | ok = rpc:call(pget(node, Cfg), rabbit_policy, delete, [<<"/">>, Name]). 39 | 40 | set_param(Cfg, Component, Name, Value) -> 41 | ok = rpc:call(pget(node, Cfg), rabbit_runtime_parameters, set, 42 | [<<"/">>, Component, Name, Value, none]). 43 | 44 | clear_param(Cfg, Component, Name) -> 45 | ok = rpc:call(pget(node, Cfg), rabbit_runtime_parameters, clear, 46 | [<<"/">>, Component, Name]). 47 | 48 | enable_plugin(Cfg, Plugin) -> 49 | plugins_action(enable, Cfg, [Plugin], []). 50 | 51 | disable_plugin(Cfg, Plugin) -> 52 | plugins_action(disable, Cfg, [Plugin], []). 53 | 54 | control_action(Command, Cfg) -> 55 | control_action(Command, Cfg, [], []). 56 | 57 | control_action(Command, Cfg, Args) -> 58 | control_action(Command, Cfg, Args, []). 59 | 60 | control_action(Command, Cfg, Args, Opts) -> 61 | Node = pget(node, Cfg), 62 | rpc:call(Node, rabbit_control_main, action, 63 | [Command, Node, Args, Opts, 64 | fun (F, A) -> 65 | error_logger:info_msg(F ++ "~n", A) 66 | end]). 67 | 68 | plugins_action(Command, Cfg, Args, Opts) -> 69 | PluginsFile = os:getenv("RABBITMQ_ENABLED_PLUGINS_FILE"), 70 | PluginsDir = os:getenv("RABBITMQ_PLUGINS_DIR"), 71 | Node = pget(node, Cfg), 72 | rpc:call(Node, rabbit_plugins_main, action, 73 | [Command, Node, Args, Opts, PluginsFile, PluginsDir]). 74 | 75 | restart_app(Cfg) -> 76 | stop_app(Cfg), 77 | start_app(Cfg). 78 | 79 | stop_app(Cfg) -> 80 | control_action(stop_app, Cfg). 81 | 82 | start_app(Cfg) -> 83 | control_action(start_app, Cfg). 84 | 85 | connect(Cfg) -> 86 | Port = pget(port, Cfg), 87 | {ok, Conn} = amqp_connection:start(#amqp_params_network{port = Port}), 88 | {ok, Ch} = amqp_connection:open_channel(Conn), 89 | {Conn, Ch}. 90 | 91 | %%---------------------------------------------------------------------------- 92 | 93 | kill_after(Time, Cfg, Method) -> 94 | timer:sleep(Time), 95 | kill(Cfg, Method). 96 | 97 | kill(Cfg, Method) -> 98 | kill0(Cfg, Method), 99 | wait_down(pget(node, Cfg)). 100 | 101 | kill0(Cfg, stop) -> rabbit_test_configs:stop_node(Cfg); 102 | kill0(Cfg, sigkill) -> rabbit_test_configs:kill_node(Cfg). 103 | 104 | wait_down(Node) -> 105 | case net_adm:ping(Node) of 106 | pong -> timer:sleep(25), 107 | wait_down(Node); 108 | pang -> ok 109 | end. 110 | 111 | a2b(A) -> list_to_binary(atom_to_list(A)). 112 | 113 | %%---------------------------------------------------------------------------- 114 | 115 | publish(Ch, QName, Count) -> 116 | amqp_channel:call(Ch, #'confirm.select'{}), 117 | [amqp_channel:call(Ch, 118 | #'basic.publish'{routing_key = QName}, 119 | #amqp_msg{props = #'P_basic'{delivery_mode = 2}, 120 | payload = list_to_binary(integer_to_list(I))}) 121 | || I <- lists:seq(1, Count)], 122 | amqp_channel:wait_for_confirms(Ch). 123 | 124 | consume(Ch, QName, Count) -> 125 | amqp_channel:subscribe(Ch, #'basic.consume'{queue = QName, no_ack = true}, 126 | self()), 127 | CTag = receive #'basic.consume_ok'{consumer_tag = C} -> C end, 128 | [begin 129 | Exp = list_to_binary(integer_to_list(I)), 130 | receive {#'basic.deliver'{consumer_tag = CTag}, 131 | #amqp_msg{payload = Exp}} -> 132 | ok 133 | after 500 -> 134 | exit(timeout) 135 | end 136 | end|| I <- lists:seq(1, Count)], 137 | #'queue.declare_ok'{message_count = 0} 138 | = amqp_channel:call(Ch, #'queue.declare'{queue = QName, 139 | durable = true}), 140 | amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = CTag}), 141 | ok. 142 | 143 | fetch(Ch, QName, Count) -> 144 | [{#'basic.get_ok'{}, _} = 145 | amqp_channel:call(Ch, #'basic.get'{queue = QName}) || 146 | _ <- lists:seq(1, Count)], 147 | ok. 148 | -------------------------------------------------------------------------------- /src/rabbitmq_test.app.src: -------------------------------------------------------------------------------- 1 | {application, rabbitmq_test, 2 | [ 3 | {description, ""}, 4 | {vsn, ""}, 5 | {modules, []}, 6 | {registered, []}, 7 | {applications, [ 8 | kernel, 9 | stdlib 10 | ]}, 11 | {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /test/snippets.config: -------------------------------------------------------------------------------- 1 | [ 2 | {1, 3 | "auth_backends.1 = internal", 4 | [{rabbit, [{auth_backends, [rabbit_auth_backend_internal]}]}],[]} 5 | , 6 | {2, 7 | "auth_backends.1 = ldap", 8 | [{rabbit, [{auth_backends, [rabbit_auth_backend_ldap]}]}],[]} 9 | , 10 | 11 | {3, 12 | "auth_backends.1 = ldap 13 | auth_backends.2 = internal", 14 | 15 | [{rabbit, [ 16 | {auth_backends, [rabbit_auth_backend_ldap, rabbit_auth_backend_internal]} 17 | ] 18 | }],[]} 19 | 20 | , 21 | 22 | {4, 23 | "auth_backends.1 = ldap 24 | # uses module name instead of a short alias, \"http\" 25 | auth_backends.2 = rabbit_auth_backend_http", 26 | 27 | [{rabbit, [{auth_backends, [rabbit_auth_backend_ldap, rabbit_auth_backend_http]}]}],[]} 28 | 29 | , 30 | 31 | {5, 32 | "auth_backends.1.authn = internal 33 | # uses module name because this backend is from a 3rd party 34 | auth_backends.1.authz = rabbit_auth_backend_ip_range", 35 | [{rabbit, [{auth_backends, [{rabbit_auth_backend_internal, rabbit_auth_backend_ip_range}]}]}],[]} 36 | , 37 | {6, 38 | "auth_backends.1.authn = ldap 39 | auth_backends.1.authz = internal", 40 | [{rabbit, [{auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal}]}]}],[]} 41 | , 42 | 43 | {7, 44 | "auth_backends.1.authn = ldap 45 | auth_backends.1.authz = internal 46 | auth_backends.2 = internal", 47 | [{rabbit, [ 48 | {auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal}, 49 | rabbit_auth_backend_internal]} 50 | ] 51 | }],[]} 52 | , 53 | 54 | 55 | {8, 56 | "ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 57 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 58 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 59 | ssl_options.verify = verify_peer 60 | ssl_options.fail_if_no_peer_cert = true", 61 | [ 62 | {rabbit, [{ssl_options, [{cacertfile, "../rabbitmq_test/example_files/path/to/cacert.pem"}, 63 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 64 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}, 65 | {verify, verify_peer}, 66 | {fail_if_no_peer_cert, true}]} 67 | ]}],[]} 68 | , 69 | 70 | {9, 71 | "listeners.tcp.default = 5673", 72 | [{rabbit, [{tcp_listeners, [5673]}]}],[]} 73 | , 74 | 75 | {10, 76 | "listeners.ssl = none", 77 | [{rabbit, [{ssl_listeners, []}]}],[]} 78 | , 79 | {11, 80 | "num_acceptors.ssl = 1", 81 | [{rabbit, [{num_ssl_acceptors, 1}]}],[]} 82 | , 83 | {12, 84 | "default_user = guest 85 | default_pass = guest 86 | default_user_tags.administrator = true 87 | default_permissions.configure = .* 88 | default_permissions.read = .* 89 | default_permissions.write = .*", 90 | [{rabbit, [ 91 | {default_user, <<"guest">>}, 92 | {default_pass, <<"guest">>}, 93 | {default_user_tags, [administrator]}, 94 | {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}]}],[]} 95 | , 96 | {13, 97 | "cluster_nodes.disc.1 = rabbit@hostname1 98 | cluster_nodes.disc.2 = rabbit@hostname2", 99 | [{rabbit, [ 100 | {cluster_nodes, {[rabbit@hostname2,rabbit@hostname1], disc}} 101 | ]}],[]} 102 | , 103 | {14, 104 | "tcp_listen_options.backlog = 128 105 | tcp_listen_options.nodelay = true 106 | tcp_listen_options.exit_on_close = false", 107 | [{rabbit, [{tcp_listen_options, [{backlog, 128}, 108 | {nodelay, true}, 109 | {exit_on_close, false}]}]}],[]} 110 | , 111 | {15, 112 | "auth_backends.1.authn = ldap 113 | auth_backends.1.authz = internal 114 | auth_backends.2 = internal", 115 | [{rabbit,[{auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal}, 116 | rabbit_auth_backend_internal]}]}],[]} 117 | , 118 | {16, 119 | "rabbitmq_auth_backend_ldap.servers.1 = some_server 120 | rabbitmq_auth_backend_ldap.servers.2 = some_other_server", 121 | [{rabbitmq_auth_backend_ldap, [{servers, ["some_server", "some_other_server"]}]}], 122 | [rabbitmq_auth_backend_ldap]} 123 | , 124 | {17, 125 | "rabbitmq_auth_backend_ldap.dn_lookup_attribute = userPrincipalName 126 | rabbitmq_auth_backend_ldap.dn_lookup_base = DC=gopivotal,DC=com 127 | rabbitmq_auth_backend_ldap.dn_lookup_bind = as_user", 128 | [{rabbitmq_auth_backend_ldap, [{dn_lookup_attribute, "userPrincipalName"}, 129 | {dn_lookup_base, "DC=gopivotal,DC=com"}, 130 | {dn_lookup_bind, as_user}]}], 131 | [rabbitmq_auth_backend_ldap]} 132 | , 133 | {18, 134 | "rabbitmq_auth_backend_ldap.dn_lookup_bind.user_dn = username 135 | rabbitmq_auth_backend_ldap.dn_lookup_bind.password = password", 136 | [{rabbitmq_auth_backend_ldap, [ 137 | {dn_lookup_bind, {"username", "password"}}]}], 138 | [rabbitmq_auth_backend_ldap]} 139 | , 140 | {19, 141 | "rabbitmq_auth_backend_ldap.other_bind = anon", 142 | [{rabbitmq_auth_backend_ldap, [{other_bind, anon}]}], 143 | [rabbitmq_auth_backend_ldap]} 144 | , 145 | {20, 146 | "rabbitmq_auth_backend_ldap.other_bind = as_user", 147 | [{rabbitmq_auth_backend_ldap, [{other_bind, as_user}]}], 148 | [rabbitmq_auth_backend_ldap]} 149 | , 150 | {21, 151 | "rabbitmq_auth_backend_ldap.other_bind.user_dn = username 152 | rabbitmq_auth_backend_ldap.other_bind.password = password", 153 | [{rabbitmq_auth_backend_ldap, [{other_bind, {"username", "password"}}]}], 154 | [rabbitmq_auth_backend_ldap]} 155 | , 156 | {22, 157 | "listeners.tcp.default = 5672 158 | collect_statistics_interval = 10000 159 | management.http_log_dir = ../rabbitmq_test/example_files/tmp/rabbit-mgmt 160 | management.rates_mode = basic", 161 | [{rabbit, [ {tcp_listeners, [5672]}, 162 | {collect_statistics_interval, 10000}]}, 163 | {rabbitmq_management, [ {http_log_dir, "../rabbitmq_test/example_files/tmp/rabbit-mgmt"}, 164 | {rates_mode, basic}]} 165 | ], 166 | [rabbitmq_management]} 167 | , 168 | {23, 169 | "management.listener.port = 12345", 170 | [{rabbitmq_management, [{listener, [{port, 12345}]}]}], 171 | [rabbitmq_management]} 172 | , 173 | {24, 174 | "management.listener.port = 15671 175 | management.listener.ssl = true 176 | management.listener.ssl_opts.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 177 | management.listener.ssl_opts.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 178 | management.listener.ssl_opts.keyfile = ../rabbitmq_test/example_files/path/to/key.pem", 179 | [{rabbitmq_management, 180 | [{listener, [{port, 15671}, 181 | {ssl, true}, 182 | {ssl_opts, [{cacertfile, "../rabbitmq_test/example_files/path/to/cacert.pem"}, 183 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 184 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}]} 185 | ]} 186 | ]} 187 | ], 188 | [rabbitmq_management]} 189 | , 190 | {25, 191 | "management.sample_retention_policies.global.minute = 5 192 | management.sample_retention_policies.global.hour = 60 193 | management.sample_retention_policies.global.day = 1200 194 | 195 | management.sample_retention_policies.basic.minute = 5 196 | management.sample_retention_policies.basic.hour = 60 197 | 198 | management.sample_retention_policies.detailed.10 = 5", 199 | [{rabbitmq_management,[ 200 | {sample_retention_policies, 201 | %% List of {MaxAgeInSeconds, SampleEveryNSeconds} 202 | [{global, [{60, 5}, {3600, 60}, {86400, 1200}]}, 203 | {basic, [{60, 5}, {3600, 60}]}, 204 | {detailed, [{10, 5}]}]} 205 | ]}], 206 | [rabbitmq_management]} 207 | , 208 | {26, 209 | "vm_memory_high_watermark.absolute = 1073741824", 210 | [{rabbit, [{vm_memory_high_watermark, {absolute, 1073741824}}]}],[]} 211 | , 212 | {27, 213 | "vm_memory_high_watermark.absolute = 1024MB", 214 | [{rabbit, [{vm_memory_high_watermark, {absolute, "1024MB"}}]}],[]} 215 | , 216 | {28, 217 | "vm_memory_high_watermark_paging_ratio = 0.75 218 | vm_memory_high_watermark.relative = 0.4", 219 | [{rabbit, [{vm_memory_high_watermark_paging_ratio, 0.75}, 220 | {vm_memory_high_watermark, 0.4}]}],[]} 221 | , 222 | {29, 223 | "listeners.tcp.default = 5672 224 | mqtt.default_user = guest 225 | mqtt.default_pass = guest 226 | mqtt.allow_anonymous = true 227 | mqtt.vhost = / 228 | mqtt.exchange = amq.topic 229 | mqtt.subscription_ttl = 1800000 230 | mqtt.prefetch = 10 231 | mqtt.listeners.ssl = none 232 | ## Default MQTT with TLS port is 8883 233 | # mqtt.listeners.ssl.default = 8883 234 | mqtt.listeners.tcp.default = 1883 235 | mqtt.tcp_listen_options.backlog = 128 236 | mqtt.tcp_listen_options.nodelay = true", 237 | [{rabbit, [{tcp_listeners, [5672]}]}, 238 | {rabbitmq_mqtt, [{default_user, <<"guest">>}, 239 | {default_pass, <<"guest">>}, 240 | {allow_anonymous, true}, 241 | {vhost, <<"/">>}, 242 | {exchange, <<"amq.topic">>}, 243 | {subscription_ttl, 1800000}, 244 | {prefetch, 10}, 245 | {ssl_listeners, []}, 246 | %% Default MQTT with TLS port is 8883 247 | %% {ssl_listeners, [8883]} 248 | {tcp_listeners, [1883]}, 249 | {tcp_listen_options, [{backlog, 128}, 250 | {nodelay, true}]}]} 251 | ], 252 | [rabbitmq_mqtt]} 253 | , 254 | {30, 255 | "ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 256 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 257 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 258 | ssl_options.verify = verify_peer 259 | ssl_options.fail_if_no_peer_cert = true 260 | 261 | mqtt.listeners.ssl.default = 8883 262 | mqtt.listeners.tcp.default = 1883", 263 | [{rabbit, [ 264 | {ssl_options, [{cacertfile, "../rabbitmq_test/example_files/path/to/cacert.pem"}, 265 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 266 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}, 267 | {verify, verify_peer}, 268 | {fail_if_no_peer_cert, true}]} 269 | ]}, 270 | {rabbitmq_mqtt, [ 271 | {ssl_listeners, [8883]}, 272 | {tcp_listeners, [1883]} 273 | ]} 274 | ], 275 | [rabbitmq_mqtt]} 276 | , 277 | {31, 278 | "mqtt.ssl_cert_login = true", 279 | [{rabbitmq_mqtt, [{ssl_cert_login, true}]}], [rabbitmq_mqtt]} 280 | , 281 | 282 | {32, 283 | "ssl_cert_login_from = common_name", 284 | [{rabbit, [{ssl_cert_login_from, common_name}]}], [rabbitmq_mqtt]} 285 | , 286 | 287 | 288 | {33, 289 | "listeners.tcp.default = 5672 290 | mqtt.default_user = guest 291 | mqtt.default_pass = guest 292 | mqtt.allow_anonymous = true 293 | mqtt.vhost = / 294 | mqtt.exchange = amq.topic 295 | mqtt.subscription_ttl = undefined 296 | mqtt.prefetch = 10", 297 | [{rabbit, [{tcp_listeners, [5672]}]}, 298 | {rabbitmq_mqtt, [{default_user, <<"guest">>}, 299 | {default_pass, <<"guest">>}, 300 | {allow_anonymous, true}, 301 | {vhost, <<"/">>}, 302 | {exchange, <<"amq.topic">>}, 303 | {subscription_ttl, undefined}, 304 | {prefetch, 10}]} 305 | ], 306 | [rabbitmq_mqtt]} 307 | , 308 | {34, 309 | "mqtt.default_user = guest 310 | mqtt.default_pass = guest 311 | mqtt.allow_anonymous = true 312 | mqtt.vhost = / 313 | mqtt.exchange = amq.topic 314 | mqtt.subscription_ttl = 1800000 315 | mqtt.prefetch = 10 316 | ## use DETS (disk-based) store for retained messages 317 | mqtt.retained_message_store = rabbit_mqtt_retained_msg_store_dets 318 | ## only used by DETS store 319 | mqtt.retained_message_store_dets_sync_interval = 2000 320 | 321 | mqtt.listeners.ssl = none 322 | mqtt.listeners.tcp.default = 1883", 323 | [{rabbitmq_mqtt, [{default_user, <<"guest">>}, 324 | {default_pass, <<"guest">>}, 325 | {allow_anonymous, true}, 326 | {vhost, <<"/">>}, 327 | {exchange, <<"amq.topic">>}, 328 | {subscription_ttl, 1800000}, 329 | {prefetch, 10}, 330 | %% use DETS (disk-based) store for retained messages 331 | {retained_message_store, rabbit_mqtt_retained_msg_store_dets}, 332 | %% only used by DETS store 333 | {retained_message_store_dets_sync_interval, 2000}, 334 | {ssl_listeners, []}, 335 | {tcp_listeners, [1883]}]} 336 | ], 337 | [rabbitmq_mqtt]} 338 | , 339 | 340 | {35, 341 | "listeners.tcp.1 = 192.168.1.99:5672", 342 | [ 343 | {rabbit, [ 344 | {tcp_listeners, [{"192.168.1.99", 5672}]} 345 | ]} 346 | ], []} 347 | , 348 | {36, 349 | "listeners.tcp.1 = 127.0.0.1:5672 350 | listeners.tcp.2 = ::1:5672", 351 | [ 352 | {rabbit, [ 353 | {tcp_listeners, [{"127.0.0.1", 5672}, 354 | {"::1", 5672}]} 355 | ]} 356 | ], []} 357 | , 358 | {37, 359 | "listeners.tcp.1 = :::5672", 360 | [ 361 | {rabbit, [ 362 | {tcp_listeners, [{"::", 5672}]} 363 | ]} 364 | ], []} 365 | , 366 | {38, 367 | "listeners.tcp.1 = 192.168.1.99:5672", 368 | [ 369 | {rabbit, [ 370 | {tcp_listeners, [{"192.168.1.99", 5672}]} 371 | ]} 372 | ], []} 373 | , 374 | {39, 375 | "listeners.tcp.1 = fe80::2acf:e9ff:fe17:f97b:5672", 376 | [ 377 | {rabbit, [ 378 | {tcp_listeners, [{"fe80::2acf:e9ff:fe17:f97b", 5672}]} 379 | ]} 380 | ], []} 381 | , 382 | {40, 383 | "tcp_listen_options.backlog = 128 384 | tcp_listen_options.nodelay = true 385 | tcp_listen_options.sndbuf = 196608 386 | tcp_listen_options.recbuf = 196608", 387 | [ 388 | {rabbit, [ 389 | {tcp_listen_options, [ 390 | {backlog, 128}, 391 | {nodelay, true}, 392 | {sndbuf, 196608}, 393 | {recbuf, 196608} 394 | ]} 395 | ]} 396 | ], []} 397 | , 398 | 399 | {42, 400 | "tcp_listen_options.backlog = 4096 401 | tcp_listen_options.nodelay = true", 402 | [ 403 | {kernel, [ 404 | {inet_default_connect_options, [{nodelay, true}]}, 405 | {inet_default_listen_options, [{nodelay, true}]} 406 | ]}] 407 | , 408 | [ 409 | {kernel, [ 410 | {inet_default_connect_options, [{nodelay, true}]}, 411 | {inet_default_listen_options, [{nodelay, true}]} 412 | ]}, 413 | {rabbit, [ 414 | {tcp_listen_options, [ 415 | {backlog, 4096}, 416 | {nodelay, true} 417 | ]} 418 | ]} 419 | ], []} 420 | , 421 | 422 | {43, 423 | "tcp_listen_options.backlog = 4096 424 | tcp_listen_options.nodelay = true", 425 | [ 426 | {rabbit, [ 427 | {tcp_listen_options, [ 428 | {backlog, 4096}, 429 | {nodelay, true} 430 | ]} 431 | ]} 432 | ], []} 433 | , 434 | 435 | {44, 436 | "ssl_handshake_timeout = 10000", 437 | [ 438 | {rabbit, [ 439 | %% 10 seconds 440 | {ssl_handshake_timeout, 10000} 441 | ]} 442 | ], []} 443 | , 444 | 445 | {45, 446 | "cluster_partition_handling = pause_if_all_down 447 | 448 | ## Recover strategy. Can be either 'autoheal' or 'ignore' 449 | cluster_partition_handling.pause_if_all_down.recover = ignore 450 | 451 | ## Node names to check 452 | cluster_partition_handling.pause_if_all_down.nodes.1 = rabbit@myhost1 453 | cluster_partition_handling.pause_if_all_down.nodes.2 = rabbit@myhost2", 454 | [{rabbit, [{cluster_partition_handling, {pause_if_all_down, [rabbit@myhost2, rabbit@myhost1], ignore}}]}], []} 455 | , 456 | {46, 457 | "cluster_partition_handling = autoheal", 458 | [{rabbit, [{cluster_partition_handling, autoheal}]}], []} 459 | , 460 | {47, 461 | "password_hashing_module = rabbit_password_hashing_sha512", 462 | [ 463 | {rabbit, [{password_hashing_module, rabbit_password_hashing_sha512}]} 464 | ],[]} 465 | , 466 | 467 | {48, 468 | "listeners.ssl.1 = 5671 469 | ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 470 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 471 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 472 | ssl_options.verify = verify_peer 473 | ssl_options.fail_if_no_peer_cert = false" 474 | , 475 | [ 476 | {rabbit, [ 477 | {ssl_listeners, [5671]}, 478 | {ssl_options, [{cacertfile,"../rabbitmq_test/example_files/path/to/cacert.pem"}, 479 | {certfile,"../rabbitmq_test/example_files/path/to/cert.pem"}, 480 | {keyfile,"../rabbitmq_test/example_files/path/to/key.pem"}, 481 | {verify,verify_peer}, 482 | {fail_if_no_peer_cert,false}]} 483 | ]} 484 | ],[]} 485 | , 486 | 487 | 488 | {49, 489 | "listeners.ssl.1 = 5671 490 | ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 491 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 492 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 493 | ssl_options.password = t0p$3kRe7", 494 | [ 495 | {rabbit, [ 496 | {ssl_listeners, [5671]}, 497 | {ssl_options, [{cacertfile,"../rabbitmq_test/example_files/path/to/cacert.pem"}, 498 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 499 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}, 500 | {password, "t0p$3kRe7"} 501 | ]} 502 | ]} 503 | ],[]} 504 | , 505 | 506 | {50, 507 | "listeners.ssl.1 = 5671 508 | ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 509 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 510 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 511 | ssl_options.versions.tls1_2 = tlsv1.2 512 | ssl_options.versions.tls1_1 = tlsv1.1 513 | ssl_options.versions.tls1 = tlsv1", 514 | [{ssl, [{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}]}], 515 | [{ssl, [{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}]}, 516 | {rabbit, [ 517 | {ssl_listeners, [5671]}, 518 | {ssl_options, [{cacertfile,"../rabbitmq_test/example_files/path/to/cacert.pem"}, 519 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 520 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}, 521 | {versions, ['tlsv1.2', 'tlsv1.1', tlsv1]} 522 | ]} 523 | ]} 524 | ],[]} 525 | , 526 | {51, 527 | "listeners.ssl.1 = 5671 528 | ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 529 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 530 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 531 | ssl_options.versions.tls1_2 = tlsv1.2 532 | ssl_options.versions.tls1_1 = tlsv1.1", 533 | [{ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]}], 534 | [ 535 | {ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]}, 536 | {rabbit, [ 537 | {ssl_listeners, [5671]}, 538 | {ssl_options, [{cacertfile,"../rabbitmq_test/example_files/path/to/cacert.pem"}, 539 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 540 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}, 541 | {versions, ['tlsv1.2', 'tlsv1.1']} 542 | ]} 543 | ]} 544 | ],[]} 545 | , 546 | {52, 547 | "listeners.ssl.1 = 5671 548 | ssl_allow_poodle_attack = true 549 | ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 550 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 551 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 552 | ssl_options.verify = verify_peer 553 | ssl_options.fail_if_no_peer_cert = false", 554 | [ 555 | {rabbit, [ 556 | {ssl_listeners, [5671]}, 557 | {ssl_allow_poodle_attack, true}, 558 | {ssl_options, [{cacertfile,"../rabbitmq_test/example_files/path/to/cacert.pem"}, 559 | {certfile,"../rabbitmq_test/example_files/path/to/cert.pem"}, 560 | {keyfile,"../rabbitmq_test/example_files/path/to/key.pem"}, 561 | {verify,verify_peer}, 562 | {fail_if_no_peer_cert,false}]} 563 | ]} 564 | ],[]} 565 | , 566 | {53, 567 | "listeners.ssl.1 = 5671 568 | ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 569 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 570 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 571 | ssl_options.depth = 2 572 | ssl_options.verify = verify_peer 573 | ssl_options.fail_if_no_peer_cert = false", 574 | [ 575 | {rabbit, [ 576 | {ssl_listeners, [5671]}, 577 | {ssl_options, [{cacertfile,"../rabbitmq_test/example_files/path/to/cacert.pem"}, 578 | {certfile,"../rabbitmq_test/example_files/path/to/cert.pem"}, 579 | {keyfile,"../rabbitmq_test/example_files/path/to/key.pem"}, 580 | {depth, 2}, 581 | {verify,verify_peer}, 582 | {fail_if_no_peer_cert,false}]} 583 | ]} 584 | ],[]} 585 | , 586 | {54, 587 | "stomp.listeners.tcp.1 = 12345", 588 | [{rabbitmq_stomp, [{tcp_listeners, [12345]}]}],[rabbitmq_stomp]} 589 | , 590 | {55, 591 | "stomp.listeners.tcp.1 = 127.0.0.1:61613 592 | stomp.listeners.tcp.2 = ::1:61613", 593 | [{rabbitmq_stomp, [{tcp_listeners, [{"127.0.0.1", 61613}, 594 | {"::1", 61613}]}]}],[rabbitmq_stomp]} 595 | , 596 | {56, 597 | "ssl_options.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 598 | ssl_options.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 599 | ssl_options.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 600 | ssl_options.verify = verify_peer 601 | ssl_options.fail_if_no_peer_cert = true 602 | 603 | stomp.listeners.tcp.1 = 61613 604 | stomp.listeners.ssl.1 = 61614", 605 | [{rabbit,[ 606 | {ssl_options, [{cacertfile, "../rabbitmq_test/example_files/path/to/cacert.pem"}, 607 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 608 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}, 609 | {verify, verify_peer}, 610 | {fail_if_no_peer_cert, true}]}]}, 611 | {rabbitmq_stomp, [{tcp_listeners, [61613]}, 612 | {ssl_listeners, [61614]}]} 613 | ],[]} 614 | , 615 | 616 | {57, 617 | "stomp.default_user = guest 618 | stomp.default_pass = guest", 619 | [{rabbitmq_stomp, [{default_user, [{login, "guest"},{passcode, "guest"}]}]}], 620 | [rabbitmq_stomp]} 621 | , 622 | {58, 623 | "stomp.ssl_cert_login = true", 624 | [{rabbitmq_stomp, [{ssl_cert_login, true}]}], 625 | [rabbitmq_stomp]} 626 | , 627 | {59, 628 | "ssl_cert_login_from = common_name", 629 | [{rabbit, [{ssl_cert_login_from, common_name}]}], []} 630 | , 631 | {60, 632 | "stomp.default_user = guest 633 | stomp.default_pass = guest 634 | stomp.implicit_connect = true", 635 | [{rabbitmq_stomp, [{default_user,[{login, "guest"}, {passcode, "guest"}]},{implicit_connect, true}]}], 636 | [rabbitmq_stomp]} 637 | , 638 | {61, 639 | "stomp.default_vhost = /", 640 | [{rabbitmq_stomp, [{default_vhost, <<"/">>}]}], 641 | [rabbitmq_stomp]} 642 | , 643 | {62, 644 | "management.listener.port = 15672 645 | management.listener.ip = 127.0.0.1", 646 | [{rabbitmq_management, 647 | [{listener, [{port, 15672}, 648 | {ip, "127.0.0.1"} 649 | ]} 650 | ]} 651 | ], 652 | [rabbitmq_management]} 653 | , 654 | {63, 655 | "management.listener.port = 15672 656 | management.listener.ssl = true 657 | 658 | management.listener.ssl_opts.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 659 | management.listener.ssl_opts.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 660 | management.listener.ssl_opts.keyfile = ../rabbitmq_test/example_files/path/to/key.pem", 661 | [{rabbitmq_management, 662 | [{listener, [{port, 15672}, 663 | {ssl, true}, 664 | {ssl_opts, [{cacertfile, "../rabbitmq_test/example_files/path/to/cacert.pem"}, 665 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 666 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}]} 667 | ]} 668 | ]} 669 | ], 670 | [rabbitmq_management]}, 671 | {64, 672 | "web_stomp.port = 12345", 673 | [{rabbitmq_web_stomp, [{port, 12345}]}], 674 | [rabbitmq_web_stomp]}, 675 | {65, 676 | "web_stomp.ssl.port = 15671 677 | web_stomp.ssl.backlog = 1024 678 | web_stomp.ssl.certfile = ../rabbitmq_test/example_files/path/to/cert.pem 679 | web_stomp.ssl.keyfile = ../rabbitmq_test/example_files/path/to/key.pem 680 | web_stomp.ssl.cacertfile = ../rabbitmq_test/example_files/path/to/cacert.pem 681 | web_stomp.ssl.password = changeme", 682 | [{rabbitmq_web_stomp, 683 | [{ssl_config, [{port, 15671}, 684 | {backlog, 1024}, 685 | {certfile, "../rabbitmq_test/example_files/path/to/cert.pem"}, 686 | {keyfile, "../rabbitmq_test/example_files/path/to/key.pem"}, 687 | {cacertfile, "../rabbitmq_test/example_files/path/to/cacert.pem"}, 688 | {password, "changeme"}]}]}], 689 | [rabbitmq_web_stomp]}, 690 | {66, 691 | "web_stomp.ws_frame = binary", 692 | [{rabbitmq_web_stomp, [{ws_frame, binary}]}], 693 | [rabbitmq_web_stomp]}, 694 | {67, 695 | "web_stomp.cowboy_opts.max_keepalive = 10", 696 | [{rabbitmq_web_stomp,[{cowboy_opts, [{max_keepalive, 10}]}]}], 697 | [rabbitmq_web_stomp]}, 698 | {68, 699 | "web_stomp.sockjs_opts.url = https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js", 700 | [{rabbitmq_web_stomp, 701 | [{sockjs_opts, [{sockjs_url, "https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"}]}]}], 702 | [rabbitmq_web_stomp]}, 703 | {69, 704 | "auth_backends.1 = http 705 | rabbitmq_auth_backend_http.user_path = https://some-server/auth/user 706 | rabbitmq_auth_backend_http.vhost_path = https://some-server/auth/vhost 707 | rabbitmq_auth_backend_http.resource_path = https://some-server/auth/resource", 708 | [{rabbit, [{auth_backends, [rabbit_auth_backend_http]}]}, 709 | {rabbitmq_auth_backend_http, 710 | [{user_path, "https://some-server/auth/user"}, 711 | {vhost_path, "https://some-server/auth/vhost"}, 712 | {resource_path, "https://some-server/auth/resource"}]}], 713 | [rabbitmq_auth_backend_http]} 714 | ]. 715 | -------------------------------------------------------------------------------- /test/src/gm_qc.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(gm_qc). 18 | -ifdef(use_proper_qc). 19 | 20 | -include_lib("proper/include/proper.hrl"). 21 | 22 | -define(GROUP, test_group). 23 | -define(MAX_SIZE, 5). 24 | -define(MSG_TIMEOUT, 1000000). %% micros 25 | 26 | -export([prop_gm_test/0]). 27 | 28 | -behaviour(proper_statem). 29 | -export([initial_state/0, command/1, precondition/2, postcondition/3, 30 | next_state/3]). 31 | 32 | -behaviour(gm). 33 | -export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]). 34 | 35 | %% Helpers 36 | -export([do_join/0, do_leave/1, do_send/1, do_proceed1/1, do_proceed2/2]). 37 | 38 | %% For insertion into gm 39 | -export([call/3, cast/2, monitor/1, demonitor/1, execute_mnesia_transaction/1]). 40 | 41 | -record(state, {seq, %% symbolic and dynamic 42 | instrumented, %% dynamic only 43 | outstanding, %% dynamic only 44 | monitors, %% dynamic only 45 | all_join, %% for symbolic 46 | to_join, %% dynamic only 47 | to_leave %% for symbolic 48 | }). 49 | 50 | prop_gm_test() -> 51 | case ?INSTR_MOD of 52 | ?MODULE -> ok; 53 | _ -> exit(compile_with_INSTRUMENT_FOR_QC) 54 | end, 55 | process_flag(trap_exit, true), 56 | erlang:register(?MODULE, self()), 57 | ?FORALL(Cmds, commands(?MODULE), gm_test(Cmds)). 58 | 59 | gm_test(Cmds) -> 60 | {_H, State, Res} = run_commands(?MODULE, Cmds), 61 | cleanup(State), 62 | ?WHENFAIL( 63 | io:format("Result: ~p~n", [Res]), 64 | aggregate(command_names(Cmds), Res =:= ok)). 65 | 66 | cleanup(S) -> 67 | S2 = ensure_joiners_joined_and_msgs_received(S), 68 | All = gms_joined(S2), 69 | All = gms(S2), %% assertion - none to join 70 | check_stale_members(All), 71 | [gm:leave(GM) || GM <- All], 72 | drain_and_proceed_gms(S2), 73 | [await_death(GM) || GM <- All], 74 | gm:forget_group(?GROUP), 75 | ok. 76 | 77 | check_stale_members(All) -> 78 | GMs = [P || P <- processes(), is_gm_process(?GROUP, P)], 79 | case GMs -- All of 80 | [] -> ok; 81 | Rest -> exit({forgot, Rest}) 82 | end. 83 | 84 | is_gm_process(Group, P) -> 85 | case process_info(P, dictionary) of 86 | undefined -> false; 87 | {dictionary, D} -> {gm, Group} =:= proplists:get_value(process_name, D) 88 | end. 89 | 90 | await_death(P) -> 91 | MRef = erlang:monitor(process, P), 92 | await_death(MRef, P). 93 | 94 | await_death(MRef, P) -> 95 | receive 96 | {'DOWN', MRef, process, P, _} -> ok; 97 | {'DOWN', _, _, _, _} -> await_death(MRef, P); 98 | {'EXIT', _, normal} -> await_death(MRef, P); 99 | {'EXIT', _, Reason} -> exit(Reason); 100 | {joined, _GM} -> await_death(MRef, P); 101 | {left, _GM} -> await_death(MRef, P); 102 | Anything -> exit({stray_msg, Anything}) 103 | end. 104 | 105 | %% --------------------------------------------------------------------------- 106 | %% proper_statem 107 | %% --------------------------------------------------------------------------- 108 | 109 | initial_state() -> #state{seq = 1, 110 | outstanding = dict:new(), 111 | instrumented = dict:new(), 112 | monitors = dict:new(), 113 | all_join = sets:new(), 114 | to_join = sets:new(), 115 | to_leave = sets:new()}. 116 | 117 | command(S) -> 118 | case {length(gms_symb_not_left(S)), length(gms_symb(S))} of 119 | {0, 0} -> qc_join(S); 120 | {0, _} -> frequency([{1, qc_join(S)}, 121 | {3, qc_proceed1(S)}, 122 | {5, qc_proceed2(S)}]); 123 | _ -> frequency([{1, qc_join(S)}, 124 | {1, qc_leave(S)}, 125 | {10, qc_send(S)}, 126 | {5, qc_proceed1(S)}, 127 | {15, qc_proceed2(S)}]) 128 | end. 129 | 130 | qc_join(_S) -> {call,?MODULE,do_join, []}. 131 | qc_leave(S) -> {call,?MODULE,do_leave,[oneof(gms_symb_not_left(S))]}. 132 | qc_send(S) -> {call,?MODULE,do_send, [oneof(gms_symb_not_left(S))]}. 133 | qc_proceed1(S) -> {call,?MODULE,do_proceed1, [oneof(gms_symb(S))]}. 134 | qc_proceed2(S) -> {call,?MODULE,do_proceed2, [oneof(gms_symb(S)), 135 | oneof(gms_symb(S))]}. 136 | 137 | precondition(S, {call, ?MODULE, do_join, []}) -> 138 | length(gms_symb(S)) < ?MAX_SIZE; 139 | 140 | precondition(_S, {call, ?MODULE, do_leave, [_GM]}) -> 141 | true; 142 | 143 | precondition(_S, {call, ?MODULE, do_send, [_GM]}) -> 144 | true; 145 | 146 | precondition(_S, {call, ?MODULE, do_proceed1, [_GM]}) -> 147 | true; 148 | 149 | precondition(_S, {call, ?MODULE, do_proceed2, [GM1, GM2]}) -> 150 | GM1 =/= GM2. 151 | 152 | postcondition(_S, {call, _M, _F, _A}, _Res) -> 153 | true. 154 | 155 | next_state(S = #state{to_join = ToSet, 156 | all_join = AllSet}, GM, {call, ?MODULE, do_join, []}) -> 157 | S#state{to_join = sets:add_element(GM, ToSet), 158 | all_join = sets:add_element(GM, AllSet)}; 159 | 160 | next_state(S = #state{to_leave = Set}, _Res, {call, ?MODULE, do_leave, [GM]}) -> 161 | S#state{to_leave = sets:add_element(GM, Set)}; 162 | 163 | next_state(S = #state{seq = Seq, 164 | outstanding = Outstanding}, _Res, 165 | {call, ?MODULE, do_send, [GM]}) -> 166 | case is_pid(GM) andalso lists:member(GM, gms_joined(S)) of 167 | true -> 168 | %% Dynamic state, i.e. runtime 169 | Msg = [{sequence, Seq}, 170 | {sent_to, GM}, 171 | {dests, gms_joined(S)}], 172 | gm:broadcast(GM, Msg), 173 | Outstanding1 = dict:map( 174 | fun (_GM, Set) -> 175 | gb_sets:add_element(Msg, Set) 176 | end, Outstanding), 177 | drain(S#state{seq = Seq + 1, 178 | outstanding = Outstanding1}); 179 | false -> 180 | S 181 | end; 182 | 183 | next_state(S, _Res, {call, ?MODULE, do_proceed1, [Pid]}) -> 184 | proceed(Pid, S); 185 | 186 | next_state(S, _Res, {call, ?MODULE, do_proceed2, [From, To]}) -> 187 | proceed({From, To}, S). 188 | 189 | proceed(K, S = #state{instrumented = Msgs}) -> 190 | case dict:find(K, Msgs) of 191 | {ok, Q} -> case queue:out(Q) of 192 | {{value, Thing}, Q2} -> 193 | S2 = proceed(K, Thing, S), 194 | S2#state{instrumented = dict:store(K, Q2, Msgs)}; 195 | {empty, _} -> 196 | S 197 | end; 198 | error -> S 199 | end. 200 | 201 | %% --------------------------------------------------------------------------- 202 | %% GM 203 | %% --------------------------------------------------------------------------- 204 | 205 | joined(Pid, _Members) -> Pid ! {joined, self()}, 206 | ok. 207 | members_changed(_Pid, _Bs, _Ds) -> ok. 208 | handle_msg(Pid, _From, Msg) -> Pid ! {gm, self(), Msg}, ok. 209 | handle_terminate(Pid, _Reason) -> Pid ! {left, self()}. 210 | 211 | %% --------------------------------------------------------------------------- 212 | %% Helpers 213 | %% --------------------------------------------------------------------------- 214 | 215 | do_join() -> 216 | {ok, GM} = gm:start_link(?GROUP, ?MODULE, self(), 217 | fun execute_mnesia_transaction/1), 218 | GM. 219 | 220 | do_leave(GM) -> 221 | gm:leave(GM), 222 | GM. 223 | 224 | %% We need to update the state, so do the work in next_state 225 | do_send( _GM) -> ok. 226 | do_proceed1(_Pid) -> ok. 227 | do_proceed2(_From, _To) -> ok. 228 | 229 | %% All GMs, joined and to join 230 | gms(#state{outstanding = Outstanding, 231 | to_join = ToJoin}) -> 232 | dict:fetch_keys(Outstanding) ++ sets:to_list(ToJoin). 233 | 234 | %% All GMs, joined and to join 235 | gms_joined(#state{outstanding = Outstanding}) -> 236 | dict:fetch_keys(Outstanding). 237 | 238 | %% All GMs including those that have left (symbolic) 239 | gms_symb(#state{all_join = AllJoin}) -> 240 | sets:to_list(AllJoin). 241 | 242 | %% All GMs not including those that have left (symbolic) 243 | gms_symb_not_left(#state{all_join = AllJoin, 244 | to_leave = ToLeave}) -> 245 | sets:to_list(sets:subtract(AllJoin, ToLeave)). 246 | 247 | drain(S) -> 248 | receive 249 | Msg -> drain(handle_msg(Msg, S)) 250 | after 10 -> S 251 | end. 252 | 253 | drain_and_proceed_gms(S0) -> 254 | S = #state{instrumented = Msgs} = drain(S0), 255 | case dict:size(Msgs) of 256 | 0 -> S; 257 | _ -> S1 = dict:fold( 258 | fun (Key, Q, Si) -> 259 | lists:foldl( 260 | fun (Msg, Sij) -> 261 | proceed(Key, Msg, Sij) 262 | end, Si, queue:to_list(Q)) 263 | end, S, Msgs), 264 | drain_and_proceed_gms(S1#state{instrumented = dict:new()}) 265 | end. 266 | 267 | handle_msg({gm, GM, Msg}, S = #state{outstanding = Outstanding}) -> 268 | case dict:find(GM, Outstanding) of 269 | {ok, Set} -> 270 | Set2 = gb_sets:del_element(Msg, Set), 271 | S#state{outstanding = dict:store(GM, Set2, Outstanding)}; 272 | error -> 273 | %% Message from GM that has already died. OK. 274 | S 275 | end; 276 | handle_msg({instrumented, Key, Thing}, S = #state{instrumented = Msgs}) -> 277 | Q1 = case dict:find(Key, Msgs) of 278 | {ok, Q} -> queue:in(Thing, Q); 279 | error -> queue:from_list([Thing]) 280 | end, 281 | S#state{instrumented = dict:store(Key, Q1, Msgs)}; 282 | handle_msg({joined, GM}, S = #state{outstanding = Outstanding, 283 | to_join = ToJoin}) -> 284 | S#state{outstanding = dict:store(GM, gb_sets:empty(), Outstanding), 285 | to_join = sets:del_element(GM, ToJoin)}; 286 | handle_msg({left, GM}, S = #state{outstanding = Outstanding, 287 | to_join = ToJoin}) -> 288 | true = dict:is_key(GM, Outstanding) orelse sets:is_element(GM, ToJoin), 289 | S#state{outstanding = dict:erase(GM, Outstanding), 290 | to_join = sets:del_element(GM, ToJoin)}; 291 | handle_msg({'DOWN', MRef, _, From, _} = Msg, S = #state{monitors = Mons}) -> 292 | To = dict:fetch(MRef, Mons), 293 | handle_msg({instrumented, {From, To}, {info, Msg}}, 294 | S#state{monitors = dict:erase(MRef, Mons)}); 295 | handle_msg({'EXIT', _From, normal}, S) -> 296 | S; 297 | handle_msg({'EXIT', _From, Reason}, _S) -> 298 | %% We just trapped exits to get nicer SASL logging. 299 | exit(Reason). 300 | 301 | proceed({_From, To}, {cast, Msg}, S) -> gen_server2:cast(To, Msg), S; 302 | proceed({_From, To}, {info, Msg}, S) -> To ! Msg, S; 303 | proceed({From, _To}, {wait, Ref}, S) -> From ! {proceed, Ref}, S; 304 | proceed({From, To}, {mon, Ref}, S) -> add_monitor(From, To, Ref, S); 305 | proceed(_Pid, {demon, MRef}, S) -> erlang:demonitor(MRef), S; 306 | proceed(Pid, {wait, Ref}, S) -> Pid ! {proceed, Ref}, S. 307 | 308 | %% NB From here is To in handle_msg/DOWN above, since the msg is going 309 | %% the other way 310 | add_monitor(From, To, Ref, S = #state{monitors = Mons}) -> 311 | MRef = erlang:monitor(process, To), 312 | From ! {mref, Ref, MRef}, 313 | S#state{monitors = dict:store(MRef, From, Mons)}. 314 | 315 | %% ---------------------------------------------------------------------------- 316 | %% Assertions 317 | %% ---------------------------------------------------------------------------- 318 | 319 | ensure_joiners_joined_and_msgs_received(S0) -> 320 | S = drain_and_proceed_gms(S0), 321 | case outstanding_joiners(S) of 322 | true -> ensure_joiners_joined_and_msgs_received(S); 323 | false -> case outstanding_msgs(S) of 324 | [] -> S; 325 | Out -> exit({outstanding_msgs, Out}) 326 | end 327 | end. 328 | 329 | outstanding_joiners(#state{to_join = ToJoin}) -> 330 | sets:size(ToJoin) > 0. 331 | 332 | outstanding_msgs(#state{outstanding = Outstanding}) -> 333 | dict:fold(fun (GM, Set, OS) -> 334 | case gb_sets:is_empty(Set) of 335 | true -> OS; 336 | false -> [{GM, gb_sets:to_list(Set)} | OS] 337 | end 338 | end, [], Outstanding). 339 | 340 | %% --------------------------------------------------------------------------- 341 | %% For insertion into GM 342 | %% --------------------------------------------------------------------------- 343 | 344 | call(Pid, Msg, infinity) -> 345 | Ref = make_ref(), 346 | whereis(?MODULE) ! {instrumented, {self(), Pid}, {wait, Ref}}, 347 | receive 348 | {proceed, Ref} -> ok 349 | end, 350 | gen_server2:call(Pid, Msg, infinity). 351 | 352 | cast(Pid, Msg) -> 353 | whereis(?MODULE) ! {instrumented, {self(), Pid}, {cast, Msg}}, 354 | ok. 355 | 356 | monitor(Pid) -> 357 | Ref = make_ref(), 358 | whereis(?MODULE) ! {instrumented, {self(), Pid}, {mon, Ref}}, 359 | receive 360 | {mref, Ref, MRef} -> MRef 361 | end. 362 | 363 | demonitor(MRef) -> 364 | whereis(?MODULE) ! {instrumented, self(), {demon, MRef}}, 365 | true. 366 | 367 | execute_mnesia_transaction(Fun) -> 368 | Ref = make_ref(), 369 | whereis(?MODULE) ! {instrumented, self(), {wait, Ref}}, 370 | receive 371 | {proceed, Ref} -> ok 372 | end, 373 | rabbit_misc:execute_mnesia_transaction(Fun). 374 | 375 | -else. 376 | 377 | -export([prop_disabled/0]). 378 | 379 | prop_disabled() -> 380 | exit({compiled_without_proper, 381 | "PropEr was not present during compilation of the test module. " 382 | "Hence all tests are disabled."}). 383 | 384 | -endif. 385 | -------------------------------------------------------------------------------- /test/src/gm_soak_test.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License at 4 | %% https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 8 | %% License for the specific language governing rights and limitations 9 | %% under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(gm_soak_test). 18 | 19 | -export([test/0]). 20 | -export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]). 21 | 22 | -behaviour(gm). 23 | 24 | -include("gm_specs.hrl"). 25 | 26 | %% --------------------------------------------------------------------------- 27 | %% Soak test 28 | %% --------------------------------------------------------------------------- 29 | 30 | get_state() -> 31 | get(state). 32 | 33 | with_state(Fun) -> 34 | put(state, Fun(get_state())). 35 | 36 | inc() -> 37 | case 1 + get(count) of 38 | 100000 -> Now = erlang:monotonic_time(), 39 | Start = put(ts, Now), 40 | Diff = erlang:convert_time_unit(Now - Start, 41 | native, 42 | micro_seconds), 43 | Rate = 100000 / (Diff / 1000000), 44 | io:format("~p seeing ~p msgs/sec~n", [self(), Rate]), 45 | put(count, 0); 46 | N -> put(count, N) 47 | end. 48 | 49 | joined([], Members) -> 50 | io:format("Joined ~p (~p members)~n", [self(), length(Members)]), 51 | put(state, dict:from_list([{Member, empty} || Member <- Members])), 52 | put(count, 0), 53 | put(ts, erlang:monotonic_time()), 54 | ok. 55 | 56 | members_changed([], Births, Deaths) -> 57 | with_state( 58 | fun (State) -> 59 | State1 = 60 | lists:foldl( 61 | fun (Born, StateN) -> 62 | false = dict:is_key(Born, StateN), 63 | dict:store(Born, empty, StateN) 64 | end, State, Births), 65 | lists:foldl( 66 | fun (Died, StateN) -> 67 | true = dict:is_key(Died, StateN), 68 | dict:store(Died, died, StateN) 69 | end, State1, Deaths) 70 | end), 71 | ok. 72 | 73 | handle_msg([], From, {test_msg, Num}) -> 74 | inc(), 75 | with_state( 76 | fun (State) -> 77 | ok = case dict:find(From, State) of 78 | {ok, died} -> 79 | exit({{from, From}, 80 | {received_posthumous_delivery, Num}}); 81 | {ok, empty} -> ok; 82 | {ok, Num} -> ok; 83 | {ok, Num1} when Num < Num1 -> 84 | exit({{from, From}, 85 | {duplicate_delivery_of, Num}, 86 | {expecting, Num1}}); 87 | {ok, Num1} -> 88 | exit({{from, From}, 89 | {received_early, Num}, 90 | {expecting, Num1}}); 91 | error -> 92 | exit({{from, From}, 93 | {received_premature_delivery, Num}}) 94 | end, 95 | dict:store(From, Num + 1, State) 96 | end), 97 | ok. 98 | 99 | handle_terminate([], Reason) -> 100 | io:format("Left ~p (~p)~n", [self(), Reason]), 101 | ok. 102 | 103 | spawn_member() -> 104 | spawn_link( 105 | fun () -> 106 | random:seed(erlang:phash2([node()]), 107 | erlang:monotonic_time(), 108 | erlang:unique_integer()), 109 | %% start up delay of no more than 10 seconds 110 | timer:sleep(random:uniform(10000)), 111 | {ok, Pid} = gm:start_link( 112 | ?MODULE, ?MODULE, [], 113 | fun rabbit_misc:execute_mnesia_transaction/1), 114 | Start = random:uniform(10000), 115 | send_loop(Pid, Start, Start + random:uniform(10000)), 116 | gm:leave(Pid), 117 | spawn_more() 118 | end). 119 | 120 | spawn_more() -> 121 | [spawn_member() || _ <- lists:seq(1, 4 - random:uniform(4))]. 122 | 123 | send_loop(_Pid, Target, Target) -> 124 | ok; 125 | send_loop(Pid, Count, Target) when Target > Count -> 126 | case random:uniform(3) of 127 | 3 -> gm:confirmed_broadcast(Pid, {test_msg, Count}); 128 | _ -> gm:broadcast(Pid, {test_msg, Count}) 129 | end, 130 | timer:sleep(random:uniform(5) - 1), %% sleep up to 4 ms 131 | send_loop(Pid, Count + 1, Target). 132 | 133 | test() -> 134 | ok = gm:create_tables(), 135 | spawn_member(), 136 | spawn_member(). 137 | -------------------------------------------------------------------------------- /test/src/gm_speed_test.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License at 4 | %% https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 8 | %% License for the specific language governing rights and limitations 9 | %% under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(gm_speed_test). 18 | 19 | -export([test/3]). 20 | -export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]). 21 | -export([wile_e_coyote/2]). 22 | 23 | -behaviour(gm). 24 | 25 | -include("gm_specs.hrl"). 26 | 27 | %% callbacks 28 | 29 | joined(Owner, _Members) -> 30 | Owner ! joined, 31 | ok. 32 | 33 | members_changed(_Owner, _Births, _Deaths) -> 34 | ok. 35 | 36 | handle_msg(Owner, _From, ping) -> 37 | Owner ! ping, 38 | ok. 39 | 40 | handle_terminate(Owner, _Reason) -> 41 | Owner ! terminated, 42 | ok. 43 | 44 | %% other 45 | 46 | wile_e_coyote(Time, WriteUnit) -> 47 | {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), 48 | fun rabbit_misc:execute_mnesia_transaction/1), 49 | receive joined -> ok end, 50 | timer:sleep(1000), %% wait for all to join 51 | timer:send_after(Time, stop), 52 | Start = erlang:monotonic_time(), 53 | {Sent, Received} = loop(Pid, WriteUnit, 0, 0), 54 | End = erlang:monotonic_time(), 55 | ok = gm:leave(Pid), 56 | receive terminated -> ok end, 57 | Elapsed = erlang:convert_time_unit(End - Start, 58 | native, 59 | micro_seconds) / 1000000, 60 | io:format("Sending rate: ~p msgs/sec~nReceiving rate: ~p msgs/sec~n~n", 61 | [Sent/Elapsed, Received/Elapsed]), 62 | ok. 63 | 64 | loop(Pid, WriteUnit, Sent, Received) -> 65 | case read(Received) of 66 | {stop, Received1} -> {Sent, Received1}; 67 | {ok, Received1} -> ok = write(Pid, WriteUnit), 68 | loop(Pid, WriteUnit, Sent + WriteUnit, Received1) 69 | end. 70 | 71 | read(Count) -> 72 | receive 73 | ping -> read(Count + 1); 74 | stop -> {stop, Count} 75 | after 5 -> 76 | {ok, Count} 77 | end. 78 | 79 | write(_Pid, 0) -> ok; 80 | write(Pid, N) -> ok = gm:broadcast(Pid, ping), 81 | write(Pid, N - 1). 82 | 83 | test(Time, WriteUnit, Nodes) -> 84 | ok = gm:create_tables(), 85 | [spawn(Node, ?MODULE, wile_e_coyote, [Time, WriteUnit]) || Node <- Nodes]. 86 | -------------------------------------------------------------------------------- /test/src/rabbit_backing_queue_qc.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at https://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_backing_queue_qc). 18 | -ifdef(use_proper_qc). 19 | -include("rabbit.hrl"). 20 | -include("rabbit_framing.hrl"). 21 | -include_lib("proper/include/proper.hrl"). 22 | 23 | -behaviour(proper_statem). 24 | 25 | -define(BQMOD, rabbit_variable_queue). 26 | -define(QUEUE_MAXLEN, 10000). 27 | -define(TIMEOUT_LIMIT, 100). 28 | 29 | -define(RECORD_INDEX(Key, Record), 30 | proplists:get_value( 31 | Key, lists:zip(record_info(fields, Record), 32 | lists:seq(2, record_info(size, Record))))). 33 | 34 | -export([initial_state/0, command/1, precondition/2, postcondition/3, 35 | next_state/3]). 36 | 37 | -export([prop_backing_queue_test/0, publish_multiple/1, 38 | timeout/2, bump_credit/1]). 39 | 40 | -record(state, {bqstate, 41 | len, %% int 42 | next_seq_id, %% int 43 | messages, %% gb_trees of seqid => {msg_props, basic_msg} 44 | acks, %% [{acktag, {seqid, {msg_props, basic_msg}}}] 45 | confirms, %% set of msgid 46 | publishing}).%% int 47 | 48 | %% Initialise model 49 | 50 | initial_state() -> 51 | #state{bqstate = qc_variable_queue_init(qc_test_queue()), 52 | len = 0, 53 | next_seq_id = 0, 54 | messages = gb_trees:empty(), 55 | acks = [], 56 | confirms = gb_sets:new(), 57 | publishing = 0}. 58 | 59 | %% Property 60 | 61 | prop_backing_queue_test() -> 62 | ?FORALL(Cmds, commands(?MODULE, initial_state()), 63 | backing_queue_test(Cmds)). 64 | 65 | backing_queue_test(Cmds) -> 66 | {ok, FileSizeLimit} = 67 | application:get_env(rabbit, msg_store_file_size_limit), 68 | application:set_env(rabbit, msg_store_file_size_limit, 512, 69 | infinity), 70 | {ok, MaxJournal} = 71 | application:get_env(rabbit, queue_index_max_journal_entries), 72 | application:set_env(rabbit, queue_index_max_journal_entries, 128, 73 | infinity), 74 | 75 | {_H, #state{bqstate = BQ}, Res} = run_commands(?MODULE, Cmds), 76 | 77 | application:set_env(rabbit, msg_store_file_size_limit, 78 | FileSizeLimit, infinity), 79 | application:set_env(rabbit, queue_index_max_journal_entries, 80 | MaxJournal, infinity), 81 | 82 | ?BQMOD:delete_and_terminate(shutdown, BQ), 83 | ?WHENFAIL( 84 | io:format("Result: ~p~n", [Res]), 85 | aggregate(command_names(Cmds), Res =:= ok)). 86 | 87 | %% Commands 88 | 89 | %% Command frequencies are tuned so that queues are normally 90 | %% reasonably short, but they may sometimes exceed 91 | %% ?QUEUE_MAXLEN. Publish-multiple and purging cause extreme queue 92 | %% lengths, so these have lower probabilities. Fetches/drops are 93 | %% sufficiently frequent so that commands that need acktags get decent 94 | %% coverage. 95 | 96 | command(S) -> 97 | frequency([{10, qc_publish(S)}, 98 | {1, qc_publish_delivered(S)}, 99 | {1, qc_publish_multiple(S)}, %% very slow 100 | {9, qc_fetch(S)}, %% needed for ack and requeue 101 | {6, qc_drop(S)}, %% 102 | {15, qc_ack(S)}, 103 | {15, qc_requeue(S)}, 104 | {3, qc_set_ram_duration_target(S)}, 105 | {1, qc_ram_duration(S)}, 106 | {1, qc_drain_confirmed(S)}, 107 | {1, qc_dropwhile(S)}, 108 | {1, qc_is_empty(S)}, 109 | {1, qc_timeout(S)}, 110 | {1, qc_bump_credit(S)}, 111 | {1, qc_purge(S)}, 112 | {1, qc_fold(S)}]). 113 | 114 | qc_publish(#state{bqstate = BQ}) -> 115 | {call, ?BQMOD, publish, 116 | [qc_message(), 117 | #message_properties{needs_confirming = frequency([{1, true}, 118 | {20, false}]), 119 | expiry = oneof([undefined | lists:seq(1, 10)]), 120 | size = 10}, 121 | false, self(), noflow, BQ]}. 122 | 123 | qc_publish_multiple(#state{}) -> 124 | {call, ?MODULE, publish_multiple, [resize(?QUEUE_MAXLEN, pos_integer())]}. 125 | 126 | qc_publish_delivered(#state{bqstate = BQ}) -> 127 | {call, ?BQMOD, publish_delivered, 128 | [qc_message(), #message_properties{size = 10}, self(), noflow, BQ]}. 129 | 130 | qc_fetch(#state{bqstate = BQ}) -> 131 | {call, ?BQMOD, fetch, [boolean(), BQ]}. 132 | 133 | qc_drop(#state{bqstate = BQ}) -> 134 | {call, ?BQMOD, drop, [boolean(), BQ]}. 135 | 136 | qc_ack(#state{bqstate = BQ, acks = Acks}) -> 137 | {call, ?BQMOD, ack, [rand_choice(proplists:get_keys(Acks)), BQ]}. 138 | 139 | qc_requeue(#state{bqstate = BQ, acks = Acks}) -> 140 | {call, ?BQMOD, requeue, [rand_choice(proplists:get_keys(Acks)), BQ]}. 141 | 142 | qc_set_ram_duration_target(#state{bqstate = BQ}) -> 143 | {call, ?BQMOD, set_ram_duration_target, 144 | [oneof([0, 1, 2, resize(1000, pos_integer()), infinity]), BQ]}. 145 | 146 | qc_ram_duration(#state{bqstate = BQ}) -> 147 | {call, ?BQMOD, ram_duration, [BQ]}. 148 | 149 | qc_drain_confirmed(#state{bqstate = BQ}) -> 150 | {call, ?BQMOD, drain_confirmed, [BQ]}. 151 | 152 | qc_dropwhile(#state{bqstate = BQ}) -> 153 | {call, ?BQMOD, dropwhile, [fun dropfun/1, BQ]}. 154 | 155 | qc_is_empty(#state{bqstate = BQ}) -> 156 | {call, ?BQMOD, is_empty, [BQ]}. 157 | 158 | qc_timeout(#state{bqstate = BQ}) -> 159 | {call, ?MODULE, timeout, [BQ, ?TIMEOUT_LIMIT]}. 160 | 161 | qc_bump_credit(#state{bqstate = BQ}) -> 162 | {call, ?MODULE, bump_credit, [BQ]}. 163 | 164 | qc_purge(#state{bqstate = BQ}) -> 165 | {call, ?BQMOD, purge, [BQ]}. 166 | 167 | qc_fold(#state{bqstate = BQ}) -> 168 | {call, ?BQMOD, fold, [makefoldfun(pos_integer()), foldacc(), BQ]}. 169 | 170 | %% Preconditions 171 | 172 | %% Create long queues by only allowing publishing 173 | precondition(#state{publishing = Count}, {call, _Mod, Fun, _Arg}) 174 | when Count > 0, Fun /= publish -> 175 | false; 176 | precondition(#state{acks = Acks}, {call, ?BQMOD, Fun, _Arg}) 177 | when Fun =:= ack; Fun =:= requeue -> 178 | length(Acks) > 0; 179 | precondition(#state{messages = Messages}, 180 | {call, ?BQMOD, publish_delivered, _Arg}) -> 181 | gb_trees:is_empty(Messages); 182 | precondition(_S, {call, ?BQMOD, _Fun, _Arg}) -> 183 | true; 184 | precondition(_S, {call, ?MODULE, timeout, _Arg}) -> 185 | true; 186 | precondition(_S, {call, ?MODULE, bump_credit, _Arg}) -> 187 | true; 188 | precondition(#state{len = Len}, {call, ?MODULE, publish_multiple, _Arg}) -> 189 | Len < ?QUEUE_MAXLEN. 190 | 191 | %% Model updates 192 | 193 | next_state(S, BQ, {call, ?BQMOD, publish, [Msg, MsgProps, _Del, _Pid, _Flow, _BQ]}) -> 194 | #state{len = Len, 195 | messages = Messages, 196 | confirms = Confirms, 197 | publishing = PublishCount, 198 | next_seq_id = NextSeq} = S, 199 | MsgId = {call, erlang, element, [?RECORD_INDEX(id, basic_message), Msg]}, 200 | NeedsConfirm = 201 | {call, erlang, element, 202 | [?RECORD_INDEX(needs_confirming, message_properties), MsgProps]}, 203 | S#state{bqstate = BQ, 204 | len = Len + 1, 205 | next_seq_id = NextSeq + 1, 206 | messages = gb_trees:insert(NextSeq, {MsgProps, Msg}, Messages), 207 | publishing = {call, erlang, max, [0, {call, erlang, '-', 208 | [PublishCount, 1]}]}, 209 | confirms = case eval(NeedsConfirm) of 210 | true -> gb_sets:add(MsgId, Confirms); 211 | _ -> Confirms 212 | end}; 213 | 214 | next_state(S, _BQ, {call, ?MODULE, publish_multiple, [PublishCount]}) -> 215 | S#state{publishing = PublishCount}; 216 | 217 | next_state(S, Res, 218 | {call, ?BQMOD, publish_delivered, 219 | [Msg, MsgProps, _Pid, _Flow, _BQ]}) -> 220 | #state{confirms = Confirms, acks = Acks, next_seq_id = NextSeq} = S, 221 | AckTag = {call, erlang, element, [1, Res]}, 222 | BQ1 = {call, erlang, element, [2, Res]}, 223 | MsgId = {call, erlang, element, [?RECORD_INDEX(id, basic_message), Msg]}, 224 | NeedsConfirm = 225 | {call, erlang, element, 226 | [?RECORD_INDEX(needs_confirming, message_properties), MsgProps]}, 227 | S#state{bqstate = BQ1, 228 | next_seq_id = NextSeq + 1, 229 | confirms = case eval(NeedsConfirm) of 230 | true -> gb_sets:add(MsgId, Confirms); 231 | _ -> Confirms 232 | end, 233 | acks = [{AckTag, {NextSeq, {MsgProps, Msg}}}|Acks] 234 | }; 235 | 236 | next_state(S, Res, {call, ?BQMOD, fetch, [AckReq, _BQ]}) -> 237 | next_state_fetch_and_drop(S, Res, AckReq, 3); 238 | 239 | next_state(S, Res, {call, ?BQMOD, drop, [AckReq, _BQ]}) -> 240 | next_state_fetch_and_drop(S, Res, AckReq, 2); 241 | 242 | next_state(S, Res, {call, ?BQMOD, ack, [AcksArg, _BQ]}) -> 243 | #state{acks = AcksState} = S, 244 | BQ1 = {call, erlang, element, [2, Res]}, 245 | S#state{bqstate = BQ1, 246 | acks = lists:foldl(fun proplists:delete/2, AcksState, AcksArg)}; 247 | 248 | next_state(S, Res, {call, ?BQMOD, requeue, [AcksArg, _V]}) -> 249 | #state{messages = Messages, acks = AcksState} = S, 250 | BQ1 = {call, erlang, element, [2, Res]}, 251 | Messages1 = lists:foldl(fun (AckTag, Msgs) -> 252 | {SeqId, MsgPropsMsg} = 253 | proplists:get_value(AckTag, AcksState), 254 | gb_trees:insert(SeqId, MsgPropsMsg, Msgs) 255 | end, Messages, AcksArg), 256 | S#state{bqstate = BQ1, 257 | len = gb_trees:size(Messages1), 258 | messages = Messages1, 259 | acks = lists:foldl(fun proplists:delete/2, AcksState, AcksArg)}; 260 | 261 | next_state(S, BQ, {call, ?BQMOD, set_ram_duration_target, _Args}) -> 262 | S#state{bqstate = BQ}; 263 | 264 | next_state(S, Res, {call, ?BQMOD, ram_duration, _Args}) -> 265 | BQ1 = {call, erlang, element, [2, Res]}, 266 | S#state{bqstate = BQ1}; 267 | 268 | next_state(S, Res, {call, ?BQMOD, drain_confirmed, _Args}) -> 269 | BQ1 = {call, erlang, element, [2, Res]}, 270 | S#state{bqstate = BQ1}; 271 | 272 | next_state(S, Res, {call, ?BQMOD, dropwhile, _Args}) -> 273 | BQ = {call, erlang, element, [2, Res]}, 274 | #state{messages = Messages} = S, 275 | Msgs1 = drop_messages(Messages), 276 | S#state{bqstate = BQ, len = gb_trees:size(Msgs1), messages = Msgs1}; 277 | 278 | next_state(S, _Res, {call, ?BQMOD, is_empty, _Args}) -> 279 | S; 280 | 281 | next_state(S, BQ, {call, ?MODULE, timeout, _Args}) -> 282 | S#state{bqstate = BQ}; 283 | next_state(S, BQ, {call, ?MODULE, bump_credit, _Args}) -> 284 | S#state{bqstate = BQ}; 285 | 286 | next_state(S, Res, {call, ?BQMOD, purge, _Args}) -> 287 | BQ1 = {call, erlang, element, [2, Res]}, 288 | S#state{bqstate = BQ1, len = 0, messages = gb_trees:empty()}; 289 | 290 | next_state(S, Res, {call, ?BQMOD, fold, _Args}) -> 291 | BQ1 = {call, erlang, element, [2, Res]}, 292 | S#state{bqstate = BQ1}. 293 | 294 | %% Postconditions 295 | 296 | postcondition(S, {call, ?BQMOD, fetch, _Args}, Res) -> 297 | #state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S, 298 | case Res of 299 | {{MsgFetched, _IsDelivered, AckTag}, _BQ} -> 300 | {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), 301 | MsgFetched =:= Msg andalso 302 | not proplists:is_defined(AckTag, Acks) andalso 303 | not gb_sets:is_element(AckTag, Confrms) andalso 304 | Len =/= 0; 305 | {empty, _BQ} -> 306 | Len =:= 0 307 | end; 308 | 309 | postcondition(S, {call, ?BQMOD, drop, _Args}, Res) -> 310 | #state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S, 311 | case Res of 312 | {{MsgIdFetched, AckTag}, _BQ} -> 313 | {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), 314 | MsgId = eval({call, erlang, element, 315 | [?RECORD_INDEX(id, basic_message), Msg]}), 316 | MsgIdFetched =:= MsgId andalso 317 | not proplists:is_defined(AckTag, Acks) andalso 318 | not gb_sets:is_element(AckTag, Confrms) andalso 319 | Len =/= 0; 320 | {empty, _BQ} -> 321 | Len =:= 0 322 | end; 323 | 324 | postcondition(S, {call, ?BQMOD, publish_delivered, _Args}, {AckTag, _BQ}) -> 325 | #state{acks = Acks, confirms = Confrms} = S, 326 | not proplists:is_defined(AckTag, Acks) andalso 327 | not gb_sets:is_element(AckTag, Confrms); 328 | 329 | postcondition(#state{len = Len}, {call, ?BQMOD, purge, _Args}, Res) -> 330 | {PurgeCount, _BQ} = Res, 331 | Len =:= PurgeCount; 332 | 333 | postcondition(#state{len = Len}, {call, ?BQMOD, is_empty, _Args}, Res) -> 334 | (Len =:= 0) =:= Res; 335 | 336 | postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) -> 337 | #state{confirms = Confirms} = S, 338 | {ReportedConfirmed, _BQ} = Res, 339 | lists:all(fun (M) -> gb_sets:is_element(M, Confirms) end, 340 | ReportedConfirmed); 341 | 342 | postcondition(S, {call, ?BQMOD, fold, [FoldFun, Acc0, _BQ0]}, {Res, _BQ1}) -> 343 | #state{messages = Messages} = S, 344 | {_, Model} = lists:foldl(fun ({_SeqId, {_MsgProps, _Msg}}, {stop, Acc}) -> 345 | {stop, Acc}; 346 | ({_SeqId, {MsgProps, Msg}}, {cont, Acc}) -> 347 | FoldFun(Msg, MsgProps, false, Acc) 348 | end, {cont, Acc0}, gb_trees:to_list(Messages)), 349 | true = Model =:= Res; 350 | 351 | postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) -> 352 | ?BQMOD:len(BQ) =:= Len. 353 | 354 | %% Helpers 355 | 356 | publish_multiple(_C) -> 357 | ok. 358 | 359 | timeout(BQ, 0) -> 360 | BQ; 361 | timeout(BQ, AtMost) -> 362 | case ?BQMOD:needs_timeout(BQ) of 363 | false -> BQ; 364 | _ -> timeout(?BQMOD:timeout(BQ), AtMost - 1) 365 | end. 366 | 367 | bump_credit(BQ) -> 368 | case credit_flow:blocked() of 369 | false -> BQ; 370 | true -> receive 371 | {bump_credit, Msg} -> 372 | credit_flow:handle_bump_msg(Msg), 373 | ?BQMOD:resume(BQ) 374 | end 375 | end. 376 | 377 | qc_message_payload() -> ?SIZED(Size, resize(Size * Size, binary())). 378 | 379 | qc_routing_key() -> noshrink(binary(10)). 380 | 381 | qc_delivery_mode() -> oneof([1, 2]). 382 | 383 | qc_message() -> qc_message(qc_delivery_mode()). 384 | 385 | qc_message(DeliveryMode) -> 386 | {call, rabbit_basic, message, [qc_default_exchange(), 387 | qc_routing_key(), 388 | #'P_basic'{delivery_mode = DeliveryMode}, 389 | qc_message_payload()]}. 390 | 391 | qc_default_exchange() -> 392 | {call, rabbit_misc, r, [<<>>, exchange, <<>>]}. 393 | 394 | qc_variable_queue_init(Q) -> 395 | {call, ?BQMOD, init, 396 | [Q, new, function(2, {ok, []})]}. 397 | 398 | qc_test_q() -> {call, rabbit_misc, r, [<<"/">>, queue, noshrink(binary(16))]}. 399 | 400 | qc_test_queue() -> qc_test_queue(boolean()). 401 | 402 | qc_test_queue(Durable) -> 403 | #amqqueue{name = qc_test_q(), 404 | durable = Durable, 405 | auto_delete = false, 406 | arguments = [], 407 | pid = self()}. 408 | 409 | rand_choice([]) -> []; 410 | rand_choice(List) -> rand_choice(List, [], random:uniform(length(List))). 411 | 412 | rand_choice(_List, Selection, 0) -> 413 | Selection; 414 | rand_choice(List, Selection, N) -> 415 | Picked = lists:nth(random:uniform(length(List)), List), 416 | rand_choice(List -- [Picked], [Picked | Selection], 417 | N - 1). 418 | 419 | makefoldfun(Size) -> 420 | fun (Msg, _MsgProps, Unacked, Acc) -> 421 | case {length(Acc) > Size, Unacked} of 422 | {false, false} -> {cont, [Msg | Acc]}; 423 | {false, true} -> {cont, Acc}; 424 | {true, _} -> {stop, Acc} 425 | end 426 | end. 427 | foldacc() -> []. 428 | 429 | dropfun(Props) -> 430 | Expiry = eval({call, erlang, element, 431 | [?RECORD_INDEX(expiry, message_properties), Props]}), 432 | Expiry =/= 1. 433 | 434 | drop_messages(Messages) -> 435 | case gb_trees:is_empty(Messages) of 436 | true -> 437 | Messages; 438 | false -> {_Seq, MsgProps_Msg, M2} = gb_trees:take_smallest(Messages), 439 | MsgProps = {call, erlang, element, [1, MsgProps_Msg]}, 440 | case dropfun(MsgProps) of 441 | true -> drop_messages(M2); 442 | false -> Messages 443 | end 444 | end. 445 | 446 | next_state_fetch_and_drop(S, Res, AckReq, AckTagIdx) -> 447 | #state{len = Len, messages = Messages, acks = Acks} = S, 448 | ResultInfo = {call, erlang, element, [1, Res]}, 449 | BQ1 = {call, erlang, element, [2, Res]}, 450 | AckTag = {call, erlang, element, [AckTagIdx, ResultInfo]}, 451 | S1 = S#state{bqstate = BQ1}, 452 | case gb_trees:is_empty(Messages) of 453 | true -> S1; 454 | false -> {SeqId, MsgProp_Msg, M2} = gb_trees:take_smallest(Messages), 455 | S2 = S1#state{len = Len - 1, messages = M2}, 456 | case AckReq of 457 | true -> 458 | S2#state{acks = [{AckTag, {SeqId, MsgProp_Msg}}|Acks]}; 459 | false -> 460 | S2 461 | end 462 | end. 463 | 464 | -else. 465 | 466 | -export([prop_disabled/0]). 467 | 468 | prop_disabled() -> 469 | exit({compiled_without_proper, 470 | "PropEr was not present during compilation of the test module. " 471 | "Hence all tests are disabled."}). 472 | 473 | -endif. 474 | --------------------------------------------------------------------------------