From f7bae6ba615919b1661cfa6bb142a45890b3544e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 1 Sep 2021 11:43:42 +0800 Subject: [PATCH 001/479] Initial commit --- .gitignore | 15 ++++ LICENSE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 3 files changed, 218 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..66fd13c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 00000000..4ca557b1 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# HttpBoomer +HttpRunner + Boomer From 97abc197a4baf3c03517a7fd56292fad6d82b511 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 19 Sep 2021 13:20:22 +0800 Subject: [PATCH 002/479] =?UTF-8?q?init=20HttpBommer=20=E2=9C=A8?= =?UTF-8?q?=F0=9F=8D=B0=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 5 ++ LICENSE | 2 +- boomer.go | 44 +++++++++++++ boomer_test.go | 22 +++++++ extract.go | 25 +++++++ go.mod | 19 ++++++ go.sum | 44 +++++++++++++ models.go | 72 ++++++++++++++++++++ request.go | 148 ++++++++++++++++++++++++++++++++++++++++++ request_test.go | 79 ++++++++++++++++++++++ response.go | 1 + runner.go | 34 ++++++++++ runner_test.go | 37 +++++++++++ step.go | 5 ++ validate.go | 25 +++++++ 15 files changed, 561 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 boomer.go create mode 100644 boomer_test.go create mode 100644 extract.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 models.go create mode 100644 request.go create mode 100644 request_test.go create mode 100644 response.go create mode 100644 runner.go create mode 100644 runner_test.go create mode 100644 step.go create mode 100644 validate.go diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..77c6ed97 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "debugtalk" + ] +} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 261eeb9e..186ade81 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2021 debugtalk Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/boomer.go b/boomer.go new file mode 100644 index 00000000..5e7cd8ba --- /dev/null +++ b/boomer.go @@ -0,0 +1,44 @@ +package httpboomer + +import ( + "time" + + "github.com/myzhan/boomer" +) + +func HttpBoomer() *Boomer { + return &Boomer{} +} + +type Boomer struct { +} + +func (b *Boomer) Run(testcases ...*TestCase) { + var taskSlice []*boomer.Task + for _, testcase := range testcases { + task := convertBoomerTask(testcase) + taskSlice = append(taskSlice, task) + } + boomer.Run(taskSlice...) +} + +func convertBoomerTask(testcase *TestCase) *boomer.Task { + return &boomer.Task{ + Name: testcase.Config.Name, + Weight: testcase.Config.Weight, + Fn: func() { + for _, step := range testcase.TestSteps { + start := time.Now() + err := step.Run() + elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) + + tStep := step.ToStruct() + if err == nil { + boomer.RecordSuccess(string(tStep.Request.Method), tStep.Name, elapsed, int64(0)) + } else { + boomer.RecordFailure(string(tStep.Request.Method), tStep.Name, elapsed, err.Error()) + } + } + }, + } +} diff --git a/boomer_test.go b/boomer_test.go new file mode 100644 index 00000000..6f0a3066 --- /dev/null +++ b/boomer_test.go @@ -0,0 +1,22 @@ +package httpboomer + +import ( + "testing" +) + +func TestHttpBoomer(t *testing.T) { + testcase1 := &TestCase{ + Config: TConfig{ + Name: "TestCase1", + Weight: 2, + }, + } + testcase2 := &TestCase{ + Config: TConfig{ + Name: "TestCase2", + Weight: 3, + }, + } + + HttpBoomer().Run(testcase1, testcase2) +} diff --git a/extract.go b/extract.go new file mode 100644 index 00000000..a35cc765 --- /dev/null +++ b/extract.go @@ -0,0 +1,25 @@ +package httpboomer + +// implements IStep interface +type StepRequestExtraction struct { + *TStep +} + +func (req *StepRequestExtraction) WithJmesPath(jmesPath string, varName string) *StepRequestExtraction { + req.TStep.Extract[varName] = jmesPath + return req +} + +func (req *StepRequestExtraction) Validate() *StepRequestValidation { + return &StepRequestValidation{ + TStep: req.TStep, + } +} + +func (req *StepRequestExtraction) ToStruct() *TStep { + return req.TStep +} + +func (req *StepRequestExtraction) Run() error { + return req.TStep.Run() +} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..e39aff3f --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module github.com/httprunner/httpboomer + +go 1.16 + +require ( + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/myzhan/boomer v1.6.0 + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/shirou/gopsutil v3.21.8+incompatible // indirect + github.com/stretchr/testify v1.7.0 // indirect + github.com/tklauser/go-sysconf v0.3.9 // indirect + github.com/ugorji/go v1.2.6 // indirect + github.com/zeromq/goczmq v4.1.0+incompatible // indirect + github.com/zeromq/gomq v0.0.0-20201031135124-cef4e507bb8e // indirect + github.com/zeromq/gomq/zmtp v0.0.0-20201031135124-cef4e507bb8e // indirect + golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..4943d537 --- /dev/null +++ b/go.sum @@ -0,0 +1,44 @@ +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM= +github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0= +github.com/myzhan/boomer v1.6.0/go.mod h1:Ma68Td5C5EAc1M9XA7yjC/tXg9u5qviNujytnX099ZQ= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shirou/gopsutil v3.21.8+incompatible h1:sh0foI8tMRlCidUJR+KzqWYWxrkuuPIGiO6Vp+KXdCU= +github.com/shirou/gopsutil v3.21.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= +github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= +github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= +github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/zeromq/goczmq v4.1.0+incompatible h1:cGVQaU6kIwwrGso0Pgbl84tzAz/h7FJ3wYQjSonjFFc= +github.com/zeromq/goczmq v4.1.0+incompatible/go.mod h1:1uZybAJoSRCvZMH2rZxEwWBSmC4T7CB/xQOfChwPEzg= +github.com/zeromq/gomq v0.0.0-20201031135124-cef4e507bb8e h1:vGjfCnWv/zWeO1ivv4+OUPgTzG/WV1iGfZwVdtUpLkM= +github.com/zeromq/gomq v0.0.0-20201031135124-cef4e507bb8e/go.mod h1:SkCxcSQ7BQEA9FvDzbj+3hV6EMhSywyxWnHwUXVIyLY= +github.com/zeromq/gomq/zmtp v0.0.0-20201031135124-cef4e507bb8e h1:pjp04/sSr2TYuaPdt+u6Cc1M38Aocp+3er0akr3auFg= +github.com/zeromq/gomq/zmtp v0.0.0-20201031135124-cef4e507bb8e/go.mod h1:LBjWEodY/ESvKRwLw3bc7mhn49oiI8qlXUqeqLn0pcU= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/models.go b/models.go new file mode 100644 index 00000000..e714e49e --- /dev/null +++ b/models.go @@ -0,0 +1,72 @@ +package httpboomer + +type Variables map[string]interface{} +type Params map[string]interface{} +type Headers map[string]string +type Cookies map[string]string + +type enumHTTPMethod string + +const ( + GET enumHTTPMethod = "GET" + HEAD enumHTTPMethod = "HEAD" + POST enumHTTPMethod = "POST" + PUT enumHTTPMethod = "PUT" + DELETE enumHTTPMethod = "DELETE" + OPTIONS enumHTTPMethod = "OPTIONS" + PATCH enumHTTPMethod = "PATCH" +) + +type TConfig struct { + Name string `json:"name"` + Verify bool `json:"verify"` + BaseURL string `json:"base_url"` + Variables Variables `json:"variables"` + Parameters Variables `json:"parameters"` + Export []string `json:"export"` + Weight int `json:"weight"` +} + +type TRequest struct { + Method enumHTTPMethod `json:"method"` + URL string `json:"url"` + Params Params `json:"params"` + Headers Headers `json:"headers"` + Cookies Cookies `json:"cookies"` + Data interface{} `json:"data"` + JSON interface{} `json:"json"` + Timeout float32 `json:"timeout"` + AllowRedirects bool `json:"allow_redirects"` + Verify bool `json:"verify"` +} + +type TValidator struct { + Check string // get value with jmespath + Comparator string + Expect interface{} + Message string +} + +type TStep struct { + Name string `json:"name"` + Request TRequest `json:"request"` + Variables Variables `json:"variables"` + SetupHooks []string `json:"setup_hooks"` + TeardownHooks []string `json:"teardown_hooks"` + Extract map[string]string `json:"extract"` + Validators []TValidator `json:"validators"` + Export []string `json:"export"` +} + +// interface for all types of steps +type IStep interface { + ToStruct() *TStep + Run() error +} + +type TestCase struct { + Config TConfig `json:"config"` + TestSteps []IStep `json:"teststeps"` +} + +type TestCaseSummary struct{} diff --git a/request.go b/request.go new file mode 100644 index 00000000..b7366396 --- /dev/null +++ b/request.go @@ -0,0 +1,148 @@ +package httpboomer + +func RunRequest(name string) *Request { + return &Request{ + TStep: &TStep{ + Name: name, + Variables: make(Variables), + }, + } +} + +type Request struct { + *TStep +} + +func (req *Request) WithVariables(variables Variables) *Request { + req.TStep.Variables = variables + return req +} + +func (req *Request) GET(url string) *RequestWithOptionalArgs { + req.TStep.Request.Method = GET + req.TStep.Request.URL = url + return &RequestWithOptionalArgs{ + TStep: req.TStep, + } +} + +func (req *Request) HEAD(url string) *RequestWithOptionalArgs { + req.TStep.Request.Method = HEAD + req.TStep.Request.URL = url + return &RequestWithOptionalArgs{ + TStep: req.TStep, + } +} + +func (req *Request) POST(url string) *RequestWithOptionalArgs { + req.TStep.Request.Method = POST + req.TStep.Request.URL = url + return &RequestWithOptionalArgs{ + TStep: req.TStep, + } +} + +func (req *Request) PUT(url string) *RequestWithOptionalArgs { + req.TStep.Request.Method = PUT + req.TStep.Request.URL = url + return &RequestWithOptionalArgs{ + TStep: req.TStep, + } +} + +func (req *Request) DELETE(url string) *RequestWithOptionalArgs { + req.TStep.Request.Method = DELETE + req.TStep.Request.URL = url + return &RequestWithOptionalArgs{ + TStep: req.TStep, + } +} + +func (req *Request) OPTIONS(url string) *RequestWithOptionalArgs { + req.TStep.Request.Method = OPTIONS + req.TStep.Request.URL = url + return &RequestWithOptionalArgs{ + TStep: req.TStep, + } +} + +func (req *Request) PATCH(url string) *RequestWithOptionalArgs { + req.TStep.Request.Method = PATCH + req.TStep.Request.URL = url + return &RequestWithOptionalArgs{ + TStep: req.TStep, + } +} + +func (req *Request) Run() error { + return req.TStep.Run() +} + +// implements IStep interface +type RequestWithOptionalArgs struct { + *TStep +} + +func (req *RequestWithOptionalArgs) SetVerify(verify bool) *RequestWithOptionalArgs { + req.TStep.Request.Verify = verify + return req +} + +func (req *RequestWithOptionalArgs) SetTimeout(timeout float32) *RequestWithOptionalArgs { + req.TStep.Request.Timeout = timeout + return req +} + +func (req *RequestWithOptionalArgs) SetProxies(proxies map[string]string) *RequestWithOptionalArgs { + // TODO + return req +} + +func (req *RequestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *RequestWithOptionalArgs { + req.TStep.Request.AllowRedirects = allowRedirects + return req +} + +func (req *RequestWithOptionalArgs) SetAuth(auth map[string]string) *RequestWithOptionalArgs { + // TODO + return req +} + +func (req *RequestWithOptionalArgs) WithParams(params Params) *RequestWithOptionalArgs { + req.TStep.Request.Params = params + return req +} + +func (req *RequestWithOptionalArgs) WithHeaders(headers Headers) *RequestWithOptionalArgs { + req.TStep.Request.Headers = headers + return req +} + +func (req *RequestWithOptionalArgs) WithCookies(cookies Cookies) *RequestWithOptionalArgs { + req.TStep.Request.Cookies = cookies + return req +} + +func (req *RequestWithOptionalArgs) WithData(data interface{}) *RequestWithOptionalArgs { + req.TStep.Request.Data = data + return req +} + +func (req *RequestWithOptionalArgs) WithJSON(json interface{}) *RequestWithOptionalArgs { + req.TStep.Request.JSON = json + return req +} + +func (req *RequestWithOptionalArgs) Validate() *StepRequestValidation { + return &StepRequestValidation{ + TStep: req.TStep, + } +} + +func (req *RequestWithOptionalArgs) ToStruct() *TStep { + return req.TStep +} + +func (req *RequestWithOptionalArgs) Run() error { + return req.TStep.Run() +} diff --git a/request_test.go b/request_test.go new file mode 100644 index 00000000..ff2eb5cb --- /dev/null +++ b/request_test.go @@ -0,0 +1,79 @@ +package httpboomer + +import ( + "testing" +) + +var ( + tStepGET = RunRequest("get with params"). + GET("/get"). + WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). + WithHeaders(Headers{"User-Agent": "HttpBoomer"}). + WithCookies(Cookies{"user": "debugtalk"}). + Validate(). + AssertEqual("status_code", 200, "check status code") + tStepPOSTData = RunRequest("post form data"). + POST("/post"). + WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). + WithHeaders(Headers{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). + WithData("a=1&b=2"). + WithCookies(Cookies{"user": "debugtalk"}). + Validate(). + AssertEqual("status_code", 200, "check status code") +) + +func TestRunRequestGetToStruct(t *testing.T) { + tStep := tStepGET.ToStruct() + if tStep.Request.Method != GET { + t.Fatalf("tStep.Request.Method != GET") + } + if tStep.Request.URL != "/get" { + t.Fatalf("tStep.Request.URL != '/get'") + } + if tStep.Request.Params["foo1"] != "bar1" || tStep.Request.Params["foo2"] != "bar2" { + t.Fatalf("tStep.Request.Params mismatch") + } + if tStep.Request.Headers["User-Agent"] != "HttpBoomer" { + t.Fatalf("tStep.Request.Headers mismatch") + } + if tStep.Request.Cookies["user"] != "debugtalk" { + t.Fatalf("tStep.Request.Cookies mismatch") + } + if tStep.Validators[0].Check != "status_code" || tStep.Validators[0].Expect != 200 { + t.Fatalf("tStep.Validators mismatch") + } +} + +func TestRunRequestPostDataToStruct(t *testing.T) { + tStep := tStepPOSTData.ToStruct() + if tStep.Request.Method != POST { + t.Fatalf("tStep.Request.Method != POST") + } + if tStep.Request.URL != "/post" { + t.Fatalf("tStep.Request.URL != '/post'") + } + if tStep.Request.Params["foo1"] != "bar1" || tStep.Request.Params["foo2"] != "bar2" { + t.Fatalf("tStep.Request.Params mismatch") + } + if tStep.Request.Headers["User-Agent"] != "HttpBoomer" { + t.Fatalf("tStep.Request.Headers mismatch") + } + if tStep.Request.Cookies["user"] != "debugtalk" { + t.Fatalf("tStep.Request.Cookies mismatch") + } + if tStep.Request.Data != "a=1&b=2" { + t.Fatalf("tStep.Request.Data mismatch") + } + if tStep.Validators[0].Check != "status_code" || tStep.Validators[0].Expect != 200 { + t.Fatalf("tStep.Validators mismatch") + } +} + +func TestRunRequestRun(t *testing.T) { + if err := tStepGET.Run(); err != nil { + t.Fatalf("tStep.Run() error: %s", err) + } + if err := tStepPOSTData.Run(); err != nil { + t.Fatalf("tStepPOSTData.Run() error: %s", err) + } +} diff --git a/response.go b/response.go new file mode 100644 index 00000000..eb187cb3 --- /dev/null +++ b/response.go @@ -0,0 +1 @@ +package httpboomer diff --git a/runner.go b/runner.go new file mode 100644 index 00000000..eba8ccf6 --- /dev/null +++ b/runner.go @@ -0,0 +1,34 @@ +package httpboomer + +func HttpRunner() *Runner { + return &Runner{} +} + +type Runner struct { +} + +func (r *Runner) Run(testcases ...*TestCase) error { + for _, testcase := range testcases { + if err := r.runCase(testcase); err != nil { + return err + } + } + return nil +} + +func (r *Runner) runCase(testcase *TestCase) error { + for _, step := range testcase.TestSteps { + if err := r.runStep(step); err != nil { + return err + } + } + return nil +} + +func (r *Runner) runStep(req IStep) error { + return req.Run() +} + +func (r *Runner) GetSummary() *TestCaseSummary { + return &TestCaseSummary{} +} diff --git a/runner_test.go b/runner_test.go new file mode 100644 index 00000000..57220114 --- /dev/null +++ b/runner_test.go @@ -0,0 +1,37 @@ +package httpboomer + +import ( + "testing" +) + +func TestHttpRunner(t *testing.T) { + testcase1 := &TestCase{ + Config: TConfig{ + Name: "TestCase1", + BaseURL: "http://httpbin.org", + }, + TestSteps: []IStep{ + RunRequest("headers"). + GET("/headers"). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertEqual("headers.Host", "httpbin.org", "check http response host"), + RunRequest("user-agent"). + GET("/user-agent"). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertEqual("body.\"user-agent\"", "python-requests", "check User-Agent"), + }, + } + testcase2 := &TestCase{ + Config: TConfig{ + Name: "TestCase2", + Weight: 3, + }, + } + + err := HttpRunner().Run(testcase1, testcase2) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/step.go b/step.go new file mode 100644 index 00000000..7d9aa793 --- /dev/null +++ b/step.go @@ -0,0 +1,5 @@ +package httpboomer + +func (step *TStep) Run() error { + return nil +} diff --git a/validate.go b/validate.go new file mode 100644 index 00000000..a415f231 --- /dev/null +++ b/validate.go @@ -0,0 +1,25 @@ +package httpboomer + +// implements IStep interface +type StepRequestValidation struct { + *TStep +} + +func (req *StepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *StepRequestValidation { + validator := TValidator{ + Check: jmesPath, + Comparator: "equals", + Expect: expected, + Message: msg, + } + req.TStep.Validators = append(req.TStep.Validators, validator) + return req +} + +func (req *StepRequestValidation) ToStruct() *TStep { + return req.TStep +} + +func (req *StepRequestValidation) Run() error { + return req.TStep.Run() +} From 20eaab5a5f8cc56f2f59984b5a4bf089a66b1822 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 20 Sep 2021 09:42:42 +0800 Subject: [PATCH 003/479] docs: add description --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ca557b1..51a1edcf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # HttpBoomer -HttpRunner + Boomer + +> HttpBoomer = [HttpRunner] + [Boomer] + +HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will be fully compatible with HttpRunner, including testcase format and usage. What's more, HttpBoomer will integrate Boomer natively to a better load generator for [locust]. + +[HttpRunner]: https://github.com/httprunner/httprunner +[Boomer]: https://github.com/myzhan/boomer +[locust]: https://github.com/locustio/locust \ No newline at end of file From a3a205ae498a7e55dddd336f52331e9db5a3dd92 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 20 Sep 2021 10:36:02 +0800 Subject: [PATCH 004/479] fix: remove unused method --- request.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/request.go b/request.go index b7366396..a755faa9 100644 --- a/request.go +++ b/request.go @@ -74,10 +74,6 @@ func (req *Request) PATCH(url string) *RequestWithOptionalArgs { } } -func (req *Request) Run() error { - return req.TStep.Run() -} - // implements IStep interface type RequestWithOptionalArgs struct { *TStep From fd2cbbb79ed28532aac400dfe769c050a0ca1efb Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 20 Sep 2021 10:47:21 +0800 Subject: [PATCH 005/479] change: use pointer type for TStep.Request --- models.go | 2 +- request.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/models.go b/models.go index e714e49e..502ca9db 100644 --- a/models.go +++ b/models.go @@ -49,7 +49,7 @@ type TValidator struct { type TStep struct { Name string `json:"name"` - Request TRequest `json:"request"` + Request *TRequest `json:"request"` Variables Variables `json:"variables"` SetupHooks []string `json:"setup_hooks"` TeardownHooks []string `json:"teardown_hooks"` diff --git a/request.go b/request.go index a755faa9..adb4a7b5 100644 --- a/request.go +++ b/request.go @@ -4,6 +4,7 @@ func RunRequest(name string) *Request { return &Request{ TStep: &TStep{ Name: name, + Request: &TRequest{}, Variables: make(Variables), }, } From 82d2981c112663a3abf457541d2fccb092066730 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 20 Sep 2021 10:59:37 +0800 Subject: [PATCH 006/479] feat: support nested testcase in teststep --- models.go | 1 + runner_test.go | 1 + testcase.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 testcase.go diff --git a/models.go b/models.go index 502ca9db..696b8f2c 100644 --- a/models.go +++ b/models.go @@ -50,6 +50,7 @@ type TValidator struct { type TStep struct { Name string `json:"name"` Request *TRequest `json:"request"` + TestCase *TestCase `json:"testcase"` Variables Variables `json:"variables"` SetupHooks []string `json:"setup_hooks"` TeardownHooks []string `json:"teardown_hooks"` diff --git a/runner_test.go b/runner_test.go index 57220114..eba1565d 100644 --- a/runner_test.go +++ b/runner_test.go @@ -21,6 +21,7 @@ func TestHttpRunner(t *testing.T) { Validate(). AssertEqual("status_code", 200, "check status code"). AssertEqual("body.\"user-agent\"", "python-requests", "check User-Agent"), + RunTestCase("TestCase3").WithVariables(Variables{"var1": "value1"}), }, } testcase2 := &TestCase{ diff --git a/testcase.go b/testcase.go new file mode 100644 index 00000000..894ef6f2 --- /dev/null +++ b/testcase.go @@ -0,0 +1,29 @@ +package httpboomer + +func RunTestCase(name string) *TestCase { + return &TestCase{ + Config: TConfig{ + Name: name, + }, + } +} + +func (tc *TestCase) WithVariables(variables Variables) *TestCase { + tc.Config.Variables = variables + return tc +} + +func (tc *TestCase) ToStruct() *TStep { + return &TStep{ + TestCase: tc, + } +} + +func (tc *TestCase) Run() error { + for _, step := range tc.TestSteps { + if err := step.Run(); err != nil { + return err + } + } + return nil +} From 6d2e8abbde756e66fb0b81f129542bcc89b03674 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 20 Sep 2021 11:09:21 +0800 Subject: [PATCH 007/479] docs: fix typo --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 51a1edcf..f569bd85 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ > HttpBoomer = [HttpRunner] + [Boomer] -HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will be fully compatible with HttpRunner, including testcase format and usage. What's more, HttpBoomer will integrate Boomer natively to a better load generator for [locust]. +HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will be fully compatible with HttpRunner, including testcase format and usage. What's more, HttpBoomer will integrate Boomer natively to be a better load generator for [locust]. + [HttpRunner]: https://github.com/httprunner/httprunner [Boomer]: https://github.com/myzhan/boomer From f19454d0505b5d5dfbab3a525116ad8df92b2ae8 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 12:03:36 +0800 Subject: [PATCH 008/479] change: rename variables --- extract.go | 18 ++++---- request.go | 130 ++++++++++++++++++++++++++-------------------------- validate.go | 14 +++--- 3 files changed, 81 insertions(+), 81 deletions(-) diff --git a/extract.go b/extract.go index a35cc765..d28be191 100644 --- a/extract.go +++ b/extract.go @@ -5,21 +5,21 @@ type StepRequestExtraction struct { *TStep } -func (req *StepRequestExtraction) WithJmesPath(jmesPath string, varName string) *StepRequestExtraction { - req.TStep.Extract[varName] = jmesPath - return req +func (step *StepRequestExtraction) WithJmesPath(jmesPath string, varName string) *StepRequestExtraction { + step.TStep.Extract[varName] = jmesPath + return step } -func (req *StepRequestExtraction) Validate() *StepRequestValidation { +func (step *StepRequestExtraction) Validate() *StepRequestValidation { return &StepRequestValidation{ - TStep: req.TStep, + TStep: step.TStep, } } -func (req *StepRequestExtraction) ToStruct() *TStep { - return req.TStep +func (step *StepRequestExtraction) ToStruct() *TStep { + return step.TStep } -func (req *StepRequestExtraction) Run() error { - return req.TStep.Run() +func (step *StepRequestExtraction) Run() error { + return step.TStep.Run() } diff --git a/request.go b/request.go index adb4a7b5..d171828f 100644 --- a/request.go +++ b/request.go @@ -14,64 +14,64 @@ type Request struct { *TStep } -func (req *Request) WithVariables(variables Variables) *Request { - req.TStep.Variables = variables - return req +func (r *Request) WithVariables(variables Variables) *Request { + r.TStep.Variables = variables + return r } -func (req *Request) GET(url string) *RequestWithOptionalArgs { - req.TStep.Request.Method = GET - req.TStep.Request.URL = url +func (r *Request) GET(url string) *RequestWithOptionalArgs { + r.TStep.Request.Method = GET + r.TStep.Request.URL = url return &RequestWithOptionalArgs{ - TStep: req.TStep, + TStep: r.TStep, } } -func (req *Request) HEAD(url string) *RequestWithOptionalArgs { - req.TStep.Request.Method = HEAD - req.TStep.Request.URL = url +func (r *Request) HEAD(url string) *RequestWithOptionalArgs { + r.TStep.Request.Method = HEAD + r.TStep.Request.URL = url return &RequestWithOptionalArgs{ - TStep: req.TStep, + TStep: r.TStep, } } -func (req *Request) POST(url string) *RequestWithOptionalArgs { - req.TStep.Request.Method = POST - req.TStep.Request.URL = url +func (r *Request) POST(url string) *RequestWithOptionalArgs { + r.TStep.Request.Method = POST + r.TStep.Request.URL = url return &RequestWithOptionalArgs{ - TStep: req.TStep, + TStep: r.TStep, } } -func (req *Request) PUT(url string) *RequestWithOptionalArgs { - req.TStep.Request.Method = PUT - req.TStep.Request.URL = url +func (r *Request) PUT(url string) *RequestWithOptionalArgs { + r.TStep.Request.Method = PUT + r.TStep.Request.URL = url return &RequestWithOptionalArgs{ - TStep: req.TStep, + TStep: r.TStep, } } -func (req *Request) DELETE(url string) *RequestWithOptionalArgs { - req.TStep.Request.Method = DELETE - req.TStep.Request.URL = url +func (r *Request) DELETE(url string) *RequestWithOptionalArgs { + r.TStep.Request.Method = DELETE + r.TStep.Request.URL = url return &RequestWithOptionalArgs{ - TStep: req.TStep, + TStep: r.TStep, } } -func (req *Request) OPTIONS(url string) *RequestWithOptionalArgs { - req.TStep.Request.Method = OPTIONS - req.TStep.Request.URL = url +func (r *Request) OPTIONS(url string) *RequestWithOptionalArgs { + r.TStep.Request.Method = OPTIONS + r.TStep.Request.URL = url return &RequestWithOptionalArgs{ - TStep: req.TStep, + TStep: r.TStep, } } -func (req *Request) PATCH(url string) *RequestWithOptionalArgs { - req.TStep.Request.Method = PATCH - req.TStep.Request.URL = url +func (r *Request) PATCH(url string) *RequestWithOptionalArgs { + r.TStep.Request.Method = PATCH + r.TStep.Request.URL = url return &RequestWithOptionalArgs{ - TStep: req.TStep, + TStep: r.TStep, } } @@ -80,66 +80,66 @@ type RequestWithOptionalArgs struct { *TStep } -func (req *RequestWithOptionalArgs) SetVerify(verify bool) *RequestWithOptionalArgs { - req.TStep.Request.Verify = verify - return req +func (r *RequestWithOptionalArgs) SetVerify(verify bool) *RequestWithOptionalArgs { + r.TStep.Request.Verify = verify + return r } -func (req *RequestWithOptionalArgs) SetTimeout(timeout float32) *RequestWithOptionalArgs { - req.TStep.Request.Timeout = timeout - return req +func (r *RequestWithOptionalArgs) SetTimeout(timeout float32) *RequestWithOptionalArgs { + r.TStep.Request.Timeout = timeout + return r } -func (req *RequestWithOptionalArgs) SetProxies(proxies map[string]string) *RequestWithOptionalArgs { +func (r *RequestWithOptionalArgs) SetProxies(proxies map[string]string) *RequestWithOptionalArgs { // TODO - return req + return r } -func (req *RequestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *RequestWithOptionalArgs { - req.TStep.Request.AllowRedirects = allowRedirects - return req +func (r *RequestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *RequestWithOptionalArgs { + r.TStep.Request.AllowRedirects = allowRedirects + return r } -func (req *RequestWithOptionalArgs) SetAuth(auth map[string]string) *RequestWithOptionalArgs { +func (r *RequestWithOptionalArgs) SetAuth(auth map[string]string) *RequestWithOptionalArgs { // TODO - return req + return r } -func (req *RequestWithOptionalArgs) WithParams(params Params) *RequestWithOptionalArgs { - req.TStep.Request.Params = params - return req +func (r *RequestWithOptionalArgs) WithParams(params Params) *RequestWithOptionalArgs { + r.TStep.Request.Params = params + return r } -func (req *RequestWithOptionalArgs) WithHeaders(headers Headers) *RequestWithOptionalArgs { - req.TStep.Request.Headers = headers - return req +func (r *RequestWithOptionalArgs) WithHeaders(headers Headers) *RequestWithOptionalArgs { + r.TStep.Request.Headers = headers + return r } -func (req *RequestWithOptionalArgs) WithCookies(cookies Cookies) *RequestWithOptionalArgs { - req.TStep.Request.Cookies = cookies - return req +func (r *RequestWithOptionalArgs) WithCookies(cookies Cookies) *RequestWithOptionalArgs { + r.TStep.Request.Cookies = cookies + return r } -func (req *RequestWithOptionalArgs) WithData(data interface{}) *RequestWithOptionalArgs { - req.TStep.Request.Data = data - return req +func (r *RequestWithOptionalArgs) WithData(data interface{}) *RequestWithOptionalArgs { + r.TStep.Request.Data = data + return r } -func (req *RequestWithOptionalArgs) WithJSON(json interface{}) *RequestWithOptionalArgs { - req.TStep.Request.JSON = json - return req +func (r *RequestWithOptionalArgs) WithJSON(json interface{}) *RequestWithOptionalArgs { + r.TStep.Request.JSON = json + return r } -func (req *RequestWithOptionalArgs) Validate() *StepRequestValidation { +func (r *RequestWithOptionalArgs) Validate() *StepRequestValidation { return &StepRequestValidation{ - TStep: req.TStep, + TStep: r.TStep, } } -func (req *RequestWithOptionalArgs) ToStruct() *TStep { - return req.TStep +func (r *RequestWithOptionalArgs) ToStruct() *TStep { + return r.TStep } -func (req *RequestWithOptionalArgs) Run() error { - return req.TStep.Run() +func (r *RequestWithOptionalArgs) Run() error { + return r.TStep.Run() } diff --git a/validate.go b/validate.go index a415f231..48243761 100644 --- a/validate.go +++ b/validate.go @@ -5,21 +5,21 @@ type StepRequestValidation struct { *TStep } -func (req *StepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *StepRequestValidation { +func (step *StepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *StepRequestValidation { validator := TValidator{ Check: jmesPath, Comparator: "equals", Expect: expected, Message: msg, } - req.TStep.Validators = append(req.TStep.Validators, validator) - return req + step.TStep.Validators = append(step.TStep.Validators, validator) + return step } -func (req *StepRequestValidation) ToStruct() *TStep { - return req.TStep +func (step *StepRequestValidation) ToStruct() *TStep { + return step.TStep } -func (req *StepRequestValidation) Run() error { - return req.TStep.Run() +func (step *StepRequestValidation) Run() error { + return step.TStep.Run() } From 5281ad7c25316d01e86ed02b68bdb85a9767f777 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 14:23:32 +0800 Subject: [PATCH 009/479] feat: add http request with github.com/imroc/req --- go.mod | 1 + go.sum | 2 ++ request_test.go | 4 ++-- runner.go | 29 +++++++++++++++++++++++++++++ step.go | 5 ----- 5 files changed, 34 insertions(+), 7 deletions(-) delete mode 100644 step.go diff --git a/go.mod b/go.mod index e39aff3f..0b5ada3b 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect github.com/google/uuid v1.3.0 // indirect + github.com/imroc/req v0.3.0 // indirect github.com/myzhan/boomer v1.6.0 github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/shirou/gopsutil v3.21.8+incompatible // indirect diff --git a/go.sum b/go.sum index 4943d537..ce775214 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U= +github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0= diff --git a/request_test.go b/request_test.go index ff2eb5cb..7d66508c 100644 --- a/request_test.go +++ b/request_test.go @@ -6,14 +6,14 @@ import ( var ( tStepGET = RunRequest("get with params"). - GET("/get"). + GET("https://postman-echo.com/get"). WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). WithHeaders(Headers{"User-Agent": "HttpBoomer"}). WithCookies(Cookies{"user": "debugtalk"}). Validate(). AssertEqual("status_code", 200, "check status code") tStepPOSTData = RunRequest("post form data"). - POST("/post"). + POST("https://postman-echo.com/post"). WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). WithHeaders(Headers{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). WithData("a=1&b=2"). diff --git a/runner.go b/runner.go index eba8ccf6..a054cf15 100644 --- a/runner.go +++ b/runner.go @@ -1,5 +1,11 @@ package httpboomer +import ( + "net/http" + + "github.com/imroc/req" +) + func HttpRunner() *Runner { return &Runner{} } @@ -17,6 +23,7 @@ func (r *Runner) Run(testcases ...*TestCase) error { } func (r *Runner) runCase(testcase *TestCase) error { + // config := testcase.Config for _, step := range testcase.TestSteps { if err := r.runStep(step); err != nil { return err @@ -32,3 +39,25 @@ func (r *Runner) runStep(req IStep) error { func (r *Runner) GetSummary() *TestCaseSummary { return &TestCaseSummary{} } + +func (step *TStep) Run() error { + + var v []interface{} + v = append(v, req.Header(step.Request.Headers)) + v = append(v, req.Param(step.Request.Params)) + + for cookieName, cookieValue := range step.Request.Cookies { + v = append(v, &http.Cookie{ + Name: cookieName, + Value: cookieValue, + }) + } + + req.Debug = true + resp, err := req.Do(string(step.Request.Method), step.Request.URL, v...) + if err != nil { + return err + } + resp.Response().Body.Close() + return nil +} diff --git a/step.go b/step.go deleted file mode 100644 index 7d9aa793..00000000 --- a/step.go +++ /dev/null @@ -1,5 +0,0 @@ -package httpboomer - -func (step *TStep) Run() error { - return nil -} From 57a1daf105ff018aac50a6fc46d31ada7735a399 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 17:55:39 +0800 Subject: [PATCH 010/479] refactor: run step with runner --- extract.go | 25 ++-- request.go | 145 ---------------------- runner.go | 27 ++-- runner_test.go | 6 +- step.go | 211 ++++++++++++++++++++++++++++++++ request_test.go => step_test.go | 32 ++--- testcase.go | 29 ----- validate.go | 19 +-- 8 files changed, 266 insertions(+), 228 deletions(-) delete mode 100644 request.go create mode 100644 step.go rename request_test.go => step_test.go (71%) delete mode 100644 testcase.go diff --git a/extract.go b/extract.go index d28be191..d30f7de3 100644 --- a/extract.go +++ b/extract.go @@ -1,25 +1,26 @@ package httpboomer // implements IStep interface -type StepRequestExtraction struct { - *TStep +type stepRequestExtraction struct { + runner *Runner + step *TStep } -func (step *StepRequestExtraction) WithJmesPath(jmesPath string, varName string) *StepRequestExtraction { - step.TStep.Extract[varName] = jmesPath - return step +func (s *stepRequestExtraction) WithJmesPath(jmesPath string, varName string) *stepRequestExtraction { + s.step.Extract[varName] = jmesPath + return s } -func (step *StepRequestExtraction) Validate() *StepRequestValidation { - return &StepRequestValidation{ - TStep: step.TStep, +func (s *stepRequestExtraction) Validate() *stepRequestValidation { + return &stepRequestValidation{ + TStep: s.step, } } -func (step *StepRequestExtraction) ToStruct() *TStep { - return step.TStep +func (s *stepRequestExtraction) ToStruct() *TStep { + return s.step } -func (step *StepRequestExtraction) Run() error { - return step.TStep.Run() +func (s *stepRequestExtraction) Run() error { + return s.runner.runStep(s.step) } diff --git a/request.go b/request.go deleted file mode 100644 index d171828f..00000000 --- a/request.go +++ /dev/null @@ -1,145 +0,0 @@ -package httpboomer - -func RunRequest(name string) *Request { - return &Request{ - TStep: &TStep{ - Name: name, - Request: &TRequest{}, - Variables: make(Variables), - }, - } -} - -type Request struct { - *TStep -} - -func (r *Request) WithVariables(variables Variables) *Request { - r.TStep.Variables = variables - return r -} - -func (r *Request) GET(url string) *RequestWithOptionalArgs { - r.TStep.Request.Method = GET - r.TStep.Request.URL = url - return &RequestWithOptionalArgs{ - TStep: r.TStep, - } -} - -func (r *Request) HEAD(url string) *RequestWithOptionalArgs { - r.TStep.Request.Method = HEAD - r.TStep.Request.URL = url - return &RequestWithOptionalArgs{ - TStep: r.TStep, - } -} - -func (r *Request) POST(url string) *RequestWithOptionalArgs { - r.TStep.Request.Method = POST - r.TStep.Request.URL = url - return &RequestWithOptionalArgs{ - TStep: r.TStep, - } -} - -func (r *Request) PUT(url string) *RequestWithOptionalArgs { - r.TStep.Request.Method = PUT - r.TStep.Request.URL = url - return &RequestWithOptionalArgs{ - TStep: r.TStep, - } -} - -func (r *Request) DELETE(url string) *RequestWithOptionalArgs { - r.TStep.Request.Method = DELETE - r.TStep.Request.URL = url - return &RequestWithOptionalArgs{ - TStep: r.TStep, - } -} - -func (r *Request) OPTIONS(url string) *RequestWithOptionalArgs { - r.TStep.Request.Method = OPTIONS - r.TStep.Request.URL = url - return &RequestWithOptionalArgs{ - TStep: r.TStep, - } -} - -func (r *Request) PATCH(url string) *RequestWithOptionalArgs { - r.TStep.Request.Method = PATCH - r.TStep.Request.URL = url - return &RequestWithOptionalArgs{ - TStep: r.TStep, - } -} - -// implements IStep interface -type RequestWithOptionalArgs struct { - *TStep -} - -func (r *RequestWithOptionalArgs) SetVerify(verify bool) *RequestWithOptionalArgs { - r.TStep.Request.Verify = verify - return r -} - -func (r *RequestWithOptionalArgs) SetTimeout(timeout float32) *RequestWithOptionalArgs { - r.TStep.Request.Timeout = timeout - return r -} - -func (r *RequestWithOptionalArgs) SetProxies(proxies map[string]string) *RequestWithOptionalArgs { - // TODO - return r -} - -func (r *RequestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *RequestWithOptionalArgs { - r.TStep.Request.AllowRedirects = allowRedirects - return r -} - -func (r *RequestWithOptionalArgs) SetAuth(auth map[string]string) *RequestWithOptionalArgs { - // TODO - return r -} - -func (r *RequestWithOptionalArgs) WithParams(params Params) *RequestWithOptionalArgs { - r.TStep.Request.Params = params - return r -} - -func (r *RequestWithOptionalArgs) WithHeaders(headers Headers) *RequestWithOptionalArgs { - r.TStep.Request.Headers = headers - return r -} - -func (r *RequestWithOptionalArgs) WithCookies(cookies Cookies) *RequestWithOptionalArgs { - r.TStep.Request.Cookies = cookies - return r -} - -func (r *RequestWithOptionalArgs) WithData(data interface{}) *RequestWithOptionalArgs { - r.TStep.Request.Data = data - return r -} - -func (r *RequestWithOptionalArgs) WithJSON(json interface{}) *RequestWithOptionalArgs { - r.TStep.Request.JSON = json - return r -} - -func (r *RequestWithOptionalArgs) Validate() *StepRequestValidation { - return &StepRequestValidation{ - TStep: r.TStep, - } -} - -func (r *RequestWithOptionalArgs) ToStruct() *TStep { - return r.TStep -} - -func (r *RequestWithOptionalArgs) Run() error { - return r.TStep.Run() -} diff --git a/runner.go b/runner.go index a054cf15..5cc44bba 100644 --- a/runner.go +++ b/runner.go @@ -6,11 +6,16 @@ import ( "github.com/imroc/req" ) +var defaultRunner = HttpRunner() + func HttpRunner() *Runner { - return &Runner{} + return &Runner{ + Client: req.New(), + } } type Runner struct { + Client *req.Req } func (r *Runner) Run(testcases ...*TestCase) error { @@ -25,23 +30,14 @@ func (r *Runner) Run(testcases ...*TestCase) error { func (r *Runner) runCase(testcase *TestCase) error { // config := testcase.Config for _, step := range testcase.TestSteps { - if err := r.runStep(step); err != nil { + if err := step.Run(); err != nil { return err } } return nil } -func (r *Runner) runStep(req IStep) error { - return req.Run() -} - -func (r *Runner) GetSummary() *TestCaseSummary { - return &TestCaseSummary{} -} - -func (step *TStep) Run() error { - +func (r *Runner) runStep(step *TStep) error { var v []interface{} v = append(v, req.Header(step.Request.Headers)) v = append(v, req.Param(step.Request.Params)) @@ -53,11 +49,14 @@ func (step *TStep) Run() error { }) } - req.Debug = true - resp, err := req.Do(string(step.Request.Method), step.Request.URL, v...) + resp, err := r.Client.Do(string(step.Request.Method), step.Request.URL, v...) if err != nil { return err } resp.Response().Body.Close() return nil } + +func (r *Runner) GetSummary() *TestCaseSummary { + return &TestCaseSummary{} +} diff --git a/runner_test.go b/runner_test.go index eba1565d..acb969c9 100644 --- a/runner_test.go +++ b/runner_test.go @@ -11,17 +11,17 @@ func TestHttpRunner(t *testing.T) { BaseURL: "http://httpbin.org", }, TestSteps: []IStep{ - RunRequest("headers"). + Step("headers"). GET("/headers"). Validate(). AssertEqual("status_code", 200, "check status code"). AssertEqual("headers.Host", "httpbin.org", "check http response host"), - RunRequest("user-agent"). + Step("user-agent"). GET("/user-agent"). Validate(). AssertEqual("status_code", 200, "check status code"). AssertEqual("body.\"user-agent\"", "python-requests", "check User-Agent"), - RunTestCase("TestCase3").WithVariables(Variables{"var1": "value1"}), + Step("TestCase3").CallRefCase(&TestCase{}), }, } testcase2 := &TestCase{ diff --git a/step.go b/step.go new file mode 100644 index 00000000..5a0c277d --- /dev/null +++ b/step.go @@ -0,0 +1,211 @@ +package httpboomer + +func Step(name string) *step { + return &step{ + runner: defaultRunner, + TStep: &TStep{ + Name: name, + Request: &TRequest{}, + Variables: make(Variables), + }, + } +} + +type step struct { + runner *Runner + *TStep +} + +func (s *step) WithRunner(runner *Runner) *step { + s.runner = runner + return s +} + +func (s *step) WithVariables(variables Variables) *step { + s.TStep.Variables = variables + return s +} + +func (s *step) SetupHook(hook string) *step { + s.TStep.SetupHooks = append(s.TStep.SetupHooks, hook) + return s +} + +func (s *step) GET(url string) *requestWithOptionalArgs { + s.TStep.Request.Method = GET + s.TStep.Request.URL = url + return &requestWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +func (s *step) HEAD(url string) *requestWithOptionalArgs { + s.TStep.Request.Method = HEAD + s.TStep.Request.URL = url + return &requestWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +func (s *step) POST(url string) *requestWithOptionalArgs { + s.TStep.Request.Method = POST + s.TStep.Request.URL = url + return &requestWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +func (s *step) PUT(url string) *requestWithOptionalArgs { + s.TStep.Request.Method = PUT + s.TStep.Request.URL = url + return &requestWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +func (s *step) DELETE(url string) *requestWithOptionalArgs { + s.TStep.Request.Method = DELETE + s.TStep.Request.URL = url + return &requestWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +func (s *step) OPTIONS(url string) *requestWithOptionalArgs { + s.TStep.Request.Method = OPTIONS + s.TStep.Request.URL = url + return &requestWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +func (s *step) PATCH(url string) *requestWithOptionalArgs { + s.TStep.Request.Method = PATCH + s.TStep.Request.URL = url + return &requestWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +// call referenced testcase +func (s *step) CallRefCase(tc *TestCase) *testcaseWithOptionalArgs { + s.TStep.TestCase = tc + return &testcaseWithOptionalArgs{ + runner: s.runner, + step: s.TStep, + } +} + +// implements IStep interface +type requestWithOptionalArgs struct { + runner *Runner + step *TStep +} + +func (s *requestWithOptionalArgs) SetVerify(verify bool) *requestWithOptionalArgs { + s.step.Request.Verify = verify + return s +} + +func (s *requestWithOptionalArgs) SetTimeout(timeout float32) *requestWithOptionalArgs { + s.step.Request.Timeout = timeout + return s +} + +func (s *requestWithOptionalArgs) SetProxies(proxies map[string]string) *requestWithOptionalArgs { + // TODO + return s +} + +func (s *requestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *requestWithOptionalArgs { + s.step.Request.AllowRedirects = allowRedirects + return s +} + +func (s *requestWithOptionalArgs) SetAuth(auth map[string]string) *requestWithOptionalArgs { + // TODO + return s +} + +func (s *requestWithOptionalArgs) WithParams(params Params) *requestWithOptionalArgs { + s.step.Request.Params = params + return s +} + +func (s *requestWithOptionalArgs) WithHeaders(headers Headers) *requestWithOptionalArgs { + s.step.Request.Headers = headers + return s +} + +func (s *requestWithOptionalArgs) WithCookies(cookies Cookies) *requestWithOptionalArgs { + s.step.Request.Cookies = cookies + return s +} + +func (s *requestWithOptionalArgs) WithData(data interface{}) *requestWithOptionalArgs { + s.step.Request.Data = data + return s +} + +func (s *requestWithOptionalArgs) WithJSON(json interface{}) *requestWithOptionalArgs { + s.step.Request.JSON = json + return s +} + +func (s *requestWithOptionalArgs) TeardownHook(hook string) *requestWithOptionalArgs { + s.step.TeardownHooks = append(s.step.TeardownHooks, hook) + return s +} + +func (s *requestWithOptionalArgs) Validate() *stepRequestValidation { + return &stepRequestValidation{ + runner: s.runner, + TStep: s.step, + } +} + +func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction { + return &stepRequestExtraction{ + runner: s.runner, + step: s.step, + } +} + +func (s *requestWithOptionalArgs) ToStruct() *TStep { + return s.step +} + +func (s *requestWithOptionalArgs) Run() error { + return s.runner.runStep(s.step) +} + +// implements IStep interface +type testcaseWithOptionalArgs struct { + runner *Runner + step *TStep +} + +func (s *testcaseWithOptionalArgs) TeardownHook(hook string) *testcaseWithOptionalArgs { + s.step.TeardownHooks = append(s.step.TeardownHooks, hook) + return s +} + +func (s *testcaseWithOptionalArgs) Export(names ...string) *testcaseWithOptionalArgs { + s.step.Export = append(s.step.Export, names...) + return s +} + +func (s *testcaseWithOptionalArgs) ToStruct() *TStep { + return s.step +} + +func (s *testcaseWithOptionalArgs) Run() error { + return s.runner.runCase(s.step.TestCase) +} diff --git a/request_test.go b/step_test.go similarity index 71% rename from request_test.go rename to step_test.go index 7d66508c..9d643801 100644 --- a/request_test.go +++ b/step_test.go @@ -5,14 +5,14 @@ import ( ) var ( - tStepGET = RunRequest("get with params"). - GET("https://postman-echo.com/get"). - WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). - WithHeaders(Headers{"User-Agent": "HttpBoomer"}). - WithCookies(Cookies{"user": "debugtalk"}). - Validate(). - AssertEqual("status_code", 200, "check status code") - tStepPOSTData = RunRequest("post form data"). + stepGET = Step("get with params"). + GET("https://postman-echo.com/get"). + WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). + WithHeaders(Headers{"User-Agent": "HttpBoomer"}). + WithCookies(Cookies{"user": "debugtalk"}). + Validate(). + AssertEqual("status_code", 200, "check status code") + stepPOSTData = Step("post form data"). POST("https://postman-echo.com/post"). WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). WithHeaders(Headers{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). @@ -23,12 +23,12 @@ var ( ) func TestRunRequestGetToStruct(t *testing.T) { - tStep := tStepGET.ToStruct() + tStep := stepGET.ToStruct() if tStep.Request.Method != GET { t.Fatalf("tStep.Request.Method != GET") } - if tStep.Request.URL != "/get" { - t.Fatalf("tStep.Request.URL != '/get'") + if tStep.Request.URL != "https://postman-echo.com/get" { + t.Fatalf("tStep.Request.URL != 'https://postman-echo.com/get'") } if tStep.Request.Params["foo1"] != "bar1" || tStep.Request.Params["foo2"] != "bar2" { t.Fatalf("tStep.Request.Params mismatch") @@ -45,12 +45,12 @@ func TestRunRequestGetToStruct(t *testing.T) { } func TestRunRequestPostDataToStruct(t *testing.T) { - tStep := tStepPOSTData.ToStruct() + tStep := stepPOSTData.ToStruct() if tStep.Request.Method != POST { t.Fatalf("tStep.Request.Method != POST") } - if tStep.Request.URL != "/post" { - t.Fatalf("tStep.Request.URL != '/post'") + if tStep.Request.URL != "https://postman-echo.com/post" { + t.Fatalf("tStep.Request.URL != 'https://postman-echo.com/post'") } if tStep.Request.Params["foo1"] != "bar1" || tStep.Request.Params["foo2"] != "bar2" { t.Fatalf("tStep.Request.Params mismatch") @@ -70,10 +70,10 @@ func TestRunRequestPostDataToStruct(t *testing.T) { } func TestRunRequestRun(t *testing.T) { - if err := tStepGET.Run(); err != nil { + if err := stepGET.Run(); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := tStepPOSTData.Run(); err != nil { + if err := stepPOSTData.Run(); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } diff --git a/testcase.go b/testcase.go deleted file mode 100644 index 894ef6f2..00000000 --- a/testcase.go +++ /dev/null @@ -1,29 +0,0 @@ -package httpboomer - -func RunTestCase(name string) *TestCase { - return &TestCase{ - Config: TConfig{ - Name: name, - }, - } -} - -func (tc *TestCase) WithVariables(variables Variables) *TestCase { - tc.Config.Variables = variables - return tc -} - -func (tc *TestCase) ToStruct() *TStep { - return &TStep{ - TestCase: tc, - } -} - -func (tc *TestCase) Run() error { - for _, step := range tc.TestSteps { - if err := step.Run(); err != nil { - return err - } - } - return nil -} diff --git a/validate.go b/validate.go index 48243761..065266fa 100644 --- a/validate.go +++ b/validate.go @@ -1,25 +1,26 @@ package httpboomer // implements IStep interface -type StepRequestValidation struct { - *TStep +type stepRequestValidation struct { + runner *Runner + TStep *TStep } -func (step *StepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *StepRequestValidation { +func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { validator := TValidator{ Check: jmesPath, Comparator: "equals", Expect: expected, Message: msg, } - step.TStep.Validators = append(step.TStep.Validators, validator) - return step + s.TStep.Validators = append(s.TStep.Validators, validator) + return s } -func (step *StepRequestValidation) ToStruct() *TStep { - return step.TStep +func (s *stepRequestValidation) ToStruct() *TStep { + return s.TStep } -func (step *StepRequestValidation) Run() error { - return step.TStep.Run() +func (s *stepRequestValidation) Run() error { + return s.runner.runStep(s.TStep) } From fe6cca362c59aca5b4128c55cda615b0bfe5d761 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 18:41:28 +0800 Subject: [PATCH 011/479] refactor: IStep interface --- boomer.go | 5 ++--- extract.go | 12 +++++++++--- models.go | 3 ++- step.go | 20 +++++++++++++++----- step_test.go | 4 ++-- validate.go | 16 +++++++++++----- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/boomer.go b/boomer.go index 5e7cd8ba..e15bc92b 100644 --- a/boomer.go +++ b/boomer.go @@ -32,11 +32,10 @@ func convertBoomerTask(testcase *TestCase) *boomer.Task { err := step.Run() elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) - tStep := step.ToStruct() if err == nil { - boomer.RecordSuccess(string(tStep.Request.Method), tStep.Name, elapsed, int64(0)) + boomer.RecordSuccess(step.Type(), step.Name(), elapsed, int64(0)) } else { - boomer.RecordFailure(string(tStep.Request.Method), tStep.Name, elapsed, err.Error()) + boomer.RecordFailure(step.Type(), step.Name(), elapsed, err.Error()) } } }, diff --git a/extract.go b/extract.go index d30f7de3..aff300be 100644 --- a/extract.go +++ b/extract.go @@ -1,5 +1,7 @@ package httpboomer +import "fmt" + // implements IStep interface type stepRequestExtraction struct { runner *Runner @@ -13,12 +15,16 @@ func (s *stepRequestExtraction) WithJmesPath(jmesPath string, varName string) *s func (s *stepRequestExtraction) Validate() *stepRequestValidation { return &stepRequestValidation{ - TStep: s.step, + step: s.step, } } -func (s *stepRequestExtraction) ToStruct() *TStep { - return s.step +func (s *stepRequestExtraction) Name() string { + return s.step.Name +} + +func (s *stepRequestExtraction) Type() string { + return fmt.Sprintf("request-%v", s.step.Request.Method) } func (s *stepRequestExtraction) Run() error { diff --git a/models.go b/models.go index 696b8f2c..c7b9a870 100644 --- a/models.go +++ b/models.go @@ -61,7 +61,8 @@ type TStep struct { // interface for all types of steps type IStep interface { - ToStruct() *TStep + Name() string + Type() string Run() error } diff --git a/step.go b/step.go index 5a0c277d..4a945fd6 100644 --- a/step.go +++ b/step.go @@ -1,5 +1,7 @@ package httpboomer +import "fmt" + func Step(name string) *step { return &step{ runner: defaultRunner, @@ -167,7 +169,7 @@ func (s *requestWithOptionalArgs) TeardownHook(hook string) *requestWithOptional func (s *requestWithOptionalArgs) Validate() *stepRequestValidation { return &stepRequestValidation{ runner: s.runner, - TStep: s.step, + step: s.step, } } @@ -178,8 +180,12 @@ func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction { } } -func (s *requestWithOptionalArgs) ToStruct() *TStep { - return s.step +func (s *requestWithOptionalArgs) Name() string { + return s.step.Name +} + +func (s *requestWithOptionalArgs) Type() string { + return fmt.Sprintf("request-%v", s.step.Request.Method) } func (s *requestWithOptionalArgs) Run() error { @@ -202,8 +208,12 @@ func (s *testcaseWithOptionalArgs) Export(names ...string) *testcaseWithOptional return s } -func (s *testcaseWithOptionalArgs) ToStruct() *TStep { - return s.step +func (s *testcaseWithOptionalArgs) Name() string { + return s.step.Name +} + +func (s *testcaseWithOptionalArgs) Type() string { + return "testcase" } func (s *testcaseWithOptionalArgs) Run() error { diff --git a/step_test.go b/step_test.go index 9d643801..88b10c72 100644 --- a/step_test.go +++ b/step_test.go @@ -23,7 +23,7 @@ var ( ) func TestRunRequestGetToStruct(t *testing.T) { - tStep := stepGET.ToStruct() + tStep := stepGET.step if tStep.Request.Method != GET { t.Fatalf("tStep.Request.Method != GET") } @@ -45,7 +45,7 @@ func TestRunRequestGetToStruct(t *testing.T) { } func TestRunRequestPostDataToStruct(t *testing.T) { - tStep := stepPOSTData.ToStruct() + tStep := stepPOSTData.step if tStep.Request.Method != POST { t.Fatalf("tStep.Request.Method != POST") } diff --git a/validate.go b/validate.go index 065266fa..9b68ade4 100644 --- a/validate.go +++ b/validate.go @@ -1,9 +1,11 @@ package httpboomer +import "fmt" + // implements IStep interface type stepRequestValidation struct { runner *Runner - TStep *TStep + step *TStep } func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { @@ -13,14 +15,18 @@ func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{ Expect: expected, Message: msg, } - s.TStep.Validators = append(s.TStep.Validators, validator) + s.step.Validators = append(s.step.Validators, validator) return s } -func (s *stepRequestValidation) ToStruct() *TStep { - return s.TStep +func (s *stepRequestValidation) Name() string { + return s.step.Name +} + +func (s *stepRequestValidation) Type() string { + return fmt.Sprintf("request-%v", s.step.Request.Method) } func (s *stepRequestValidation) Run() error { - return s.runner.runStep(s.TStep) + return s.runner.runStep(s.step) } From 3f9b8be720e44387987b7aed3382ce69578df4eb Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 19:44:17 +0800 Subject: [PATCH 012/479] refactor: simplify api --- boomer.go | 8 +++++++- boomer_test.go | 2 +- runner.go | 8 ++++++-- runner_test.go | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/boomer.go b/boomer.go index e15bc92b..0161a008 100644 --- a/boomer.go +++ b/boomer.go @@ -6,7 +6,13 @@ import ( "github.com/myzhan/boomer" ) -func HttpBoomer() *Boomer { +var defaultBoomer = NewBoomer() + +func Run(testcases ...*TestCase) { + defaultBoomer.Run(testcases...) +} + +func NewBoomer() *Boomer { return &Boomer{} } diff --git a/boomer_test.go b/boomer_test.go index 6f0a3066..20aa39f4 100644 --- a/boomer_test.go +++ b/boomer_test.go @@ -18,5 +18,5 @@ func TestHttpBoomer(t *testing.T) { }, } - HttpBoomer().Run(testcase1, testcase2) + Run(testcase1, testcase2) } diff --git a/runner.go b/runner.go index 5cc44bba..7872c1df 100644 --- a/runner.go +++ b/runner.go @@ -6,9 +6,13 @@ import ( "github.com/imroc/req" ) -var defaultRunner = HttpRunner() +var defaultRunner = NewRunner() -func HttpRunner() *Runner { +func Test(testcases ...*TestCase) error { + return defaultRunner.Run(testcases...) +} + +func NewRunner() *Runner { return &Runner{ Client: req.New(), } diff --git a/runner_test.go b/runner_test.go index acb969c9..c21c5321 100644 --- a/runner_test.go +++ b/runner_test.go @@ -31,7 +31,7 @@ func TestHttpRunner(t *testing.T) { }, } - err := HttpRunner().Run(testcase1, testcase2) + err := Test(testcase1, testcase2) if err != nil { t.Fatalf("run testcase error: %v", err) } From 4c2d7673faa81faacba68f2647897d5e36bda6ae Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 19:58:50 +0800 Subject: [PATCH 013/479] refactor: use raw models --- models.go | 57 ++++++++++++++++++++++++---------------------------- step.go | 10 ++++----- step_test.go | 12 +++++------ 3 files changed, 37 insertions(+), 42 deletions(-) diff --git a/models.go b/models.go index c7b9a870..8dc7b8b4 100644 --- a/models.go +++ b/models.go @@ -1,10 +1,5 @@ package httpboomer -type Variables map[string]interface{} -type Params map[string]interface{} -type Headers map[string]string -type Cookies map[string]string - type enumHTTPMethod string const ( @@ -18,26 +13,26 @@ const ( ) type TConfig struct { - Name string `json:"name"` - Verify bool `json:"verify"` - BaseURL string `json:"base_url"` - Variables Variables `json:"variables"` - Parameters Variables `json:"parameters"` - Export []string `json:"export"` - Weight int `json:"weight"` + Name string `json:"name"` + Verify bool `json:"verify"` + BaseURL string `json:"base_url"` + Variables map[string]interface{} `json:"variables"` + Parameters map[string]interface{} `json:"parameters"` + Export []string `json:"export"` + Weight int `json:"weight"` } type TRequest struct { - Method enumHTTPMethod `json:"method"` - URL string `json:"url"` - Params Params `json:"params"` - Headers Headers `json:"headers"` - Cookies Cookies `json:"cookies"` - Data interface{} `json:"data"` - JSON interface{} `json:"json"` - Timeout float32 `json:"timeout"` - AllowRedirects bool `json:"allow_redirects"` - Verify bool `json:"verify"` + Method enumHTTPMethod `json:"method"` + URL string `json:"url"` + Params map[string]interface{} `json:"params"` + Headers map[string]string `json:"headers"` + Cookies map[string]string `json:"cookies"` + Data interface{} `json:"data"` + JSON interface{} `json:"json"` + Timeout float32 `json:"timeout"` + AllowRedirects bool `json:"allow_redirects"` + Verify bool `json:"verify"` } type TValidator struct { @@ -48,15 +43,15 @@ type TValidator struct { } type TStep struct { - Name string `json:"name"` - Request *TRequest `json:"request"` - TestCase *TestCase `json:"testcase"` - Variables Variables `json:"variables"` - SetupHooks []string `json:"setup_hooks"` - TeardownHooks []string `json:"teardown_hooks"` - Extract map[string]string `json:"extract"` - Validators []TValidator `json:"validators"` - Export []string `json:"export"` + Name string `json:"name"` + Request *TRequest `json:"request"` + TestCase *TestCase `json:"testcase"` + Variables map[string]interface{} `json:"variables"` + SetupHooks []string `json:"setup_hooks"` + TeardownHooks []string `json:"teardown_hooks"` + Extract map[string]string `json:"extract"` + Validators []TValidator `json:"validators"` + Export []string `json:"export"` } // interface for all types of steps diff --git a/step.go b/step.go index 4a945fd6..56d0bddf 100644 --- a/step.go +++ b/step.go @@ -8,7 +8,7 @@ func Step(name string) *step { TStep: &TStep{ Name: name, Request: &TRequest{}, - Variables: make(Variables), + Variables: make(map[string]interface{}), }, } } @@ -23,7 +23,7 @@ func (s *step) WithRunner(runner *Runner) *step { return s } -func (s *step) WithVariables(variables Variables) *step { +func (s *step) WithVariables(variables map[string]interface{}) *step { s.TStep.Variables = variables return s } @@ -136,17 +136,17 @@ func (s *requestWithOptionalArgs) SetAuth(auth map[string]string) *requestWithOp return s } -func (s *requestWithOptionalArgs) WithParams(params Params) *requestWithOptionalArgs { +func (s *requestWithOptionalArgs) WithParams(params map[string]interface{}) *requestWithOptionalArgs { s.step.Request.Params = params return s } -func (s *requestWithOptionalArgs) WithHeaders(headers Headers) *requestWithOptionalArgs { +func (s *requestWithOptionalArgs) WithHeaders(headers map[string]string) *requestWithOptionalArgs { s.step.Request.Headers = headers return s } -func (s *requestWithOptionalArgs) WithCookies(cookies Cookies) *requestWithOptionalArgs { +func (s *requestWithOptionalArgs) WithCookies(cookies map[string]string) *requestWithOptionalArgs { s.step.Request.Cookies = cookies return s } diff --git a/step_test.go b/step_test.go index 88b10c72..1cfc6b4e 100644 --- a/step_test.go +++ b/step_test.go @@ -7,17 +7,17 @@ import ( var ( stepGET = Step("get with params"). GET("https://postman-echo.com/get"). - WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). - WithHeaders(Headers{"User-Agent": "HttpBoomer"}). - WithCookies(Cookies{"user": "debugtalk"}). + WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + WithCookies(map[string]string{"user": "debugtalk"}). Validate(). AssertEqual("status_code", 200, "check status code") stepPOSTData = Step("post form data"). POST("https://postman-echo.com/post"). - WithParams(Params{"foo1": "bar1", "foo2": "bar2"}). - WithHeaders(Headers{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). + WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). WithData("a=1&b=2"). - WithCookies(Cookies{"user": "debugtalk"}). + WithCookies(map[string]string{"user": "debugtalk"}). Validate(). AssertEqual("status_code", 200, "check status code") ) From 9fb19da2acfb54df2ab823210ff0b628e5de56a0 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 20:12:33 +0800 Subject: [PATCH 014/479] refactor: Run with config --- boomer.go | 3 ++- extract.go | 2 +- models.go | 2 +- runner.go | 4 ++-- step.go | 5 +++-- step_test.go | 5 +++-- validate.go | 2 +- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/boomer.go b/boomer.go index 0161a008..c2b600c5 100644 --- a/boomer.go +++ b/boomer.go @@ -33,9 +33,10 @@ func convertBoomerTask(testcase *TestCase) *boomer.Task { Name: testcase.Config.Name, Weight: testcase.Config.Weight, Fn: func() { + config := &testcase.Config for _, step := range testcase.TestSteps { start := time.Now() - err := step.Run() + err := step.Run(config) elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) if err == nil { diff --git a/extract.go b/extract.go index aff300be..a63cf64d 100644 --- a/extract.go +++ b/extract.go @@ -27,6 +27,6 @@ func (s *stepRequestExtraction) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *stepRequestExtraction) Run() error { +func (s *stepRequestExtraction) Run(config *TConfig) error { return s.runner.runStep(s.step) } diff --git a/models.go b/models.go index 8dc7b8b4..f96b6f40 100644 --- a/models.go +++ b/models.go @@ -58,7 +58,7 @@ type TStep struct { type IStep interface { Name() string Type() string - Run() error + Run(config *TConfig) error } type TestCase struct { diff --git a/runner.go b/runner.go index 7872c1df..a5efa4f0 100644 --- a/runner.go +++ b/runner.go @@ -32,9 +32,9 @@ func (r *Runner) Run(testcases ...*TestCase) error { } func (r *Runner) runCase(testcase *TestCase) error { - // config := testcase.Config + config := &testcase.Config for _, step := range testcase.TestSteps { - if err := step.Run(); err != nil { + if err := step.Run(config); err != nil { return err } } diff --git a/step.go b/step.go index 56d0bddf..ca9875ca 100644 --- a/step.go +++ b/step.go @@ -15,6 +15,7 @@ func Step(name string) *step { type step struct { runner *Runner + config *TConfig *TStep } @@ -188,7 +189,7 @@ func (s *requestWithOptionalArgs) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *requestWithOptionalArgs) Run() error { +func (s *requestWithOptionalArgs) Run(config *TConfig) error { return s.runner.runStep(s.step) } @@ -216,6 +217,6 @@ func (s *testcaseWithOptionalArgs) Type() string { return "testcase" } -func (s *testcaseWithOptionalArgs) Run() error { +func (s *testcaseWithOptionalArgs) Run(config *TConfig) error { return s.runner.runCase(s.step.TestCase) } diff --git a/step_test.go b/step_test.go index 1cfc6b4e..76b40807 100644 --- a/step_test.go +++ b/step_test.go @@ -70,10 +70,11 @@ func TestRunRequestPostDataToStruct(t *testing.T) { } func TestRunRequestRun(t *testing.T) { - if err := stepGET.Run(); err != nil { + config := &TConfig{} + if err := stepGET.Run(config); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := stepPOSTData.Run(); err != nil { + if err := stepPOSTData.Run(config); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } diff --git a/validate.go b/validate.go index 9b68ade4..b27fab7a 100644 --- a/validate.go +++ b/validate.go @@ -27,6 +27,6 @@ func (s *stepRequestValidation) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *stepRequestValidation) Run() error { +func (s *stepRequestValidation) Run(config *TConfig) error { return s.runner.runStep(s.step) } From 102b8a94a30b1d043d2c3bbfef2a9fdcb30c2e13 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 22 Sep 2021 21:25:28 +0800 Subject: [PATCH 015/479] refactor: runStep --- boomer.go | 4 +++- extract.go | 7 +++---- models.go | 2 +- runner.go | 14 +++++++++++++- step.go | 52 ++++++++++++++++------------------------------------ step_test.go | 20 +++++++++++--------- validate.go | 7 +++---- 7 files changed, 50 insertions(+), 56 deletions(-) diff --git a/boomer.go b/boomer.go index c2b600c5..eed95aff 100644 --- a/boomer.go +++ b/boomer.go @@ -29,6 +29,7 @@ func (b *Boomer) Run(testcases ...*TestCase) { } func convertBoomerTask(testcase *TestCase) *boomer.Task { + runner := NewRunner() return &boomer.Task{ Name: testcase.Config.Name, Weight: testcase.Config.Weight, @@ -36,7 +37,8 @@ func convertBoomerTask(testcase *TestCase) *boomer.Task { config := &testcase.Config for _, step := range testcase.TestSteps { start := time.Now() - err := step.Run(config) + tStep := parseStep(step, config) + err := runner.runStep(tStep) elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) if err == nil { diff --git a/extract.go b/extract.go index a63cf64d..97669990 100644 --- a/extract.go +++ b/extract.go @@ -4,8 +4,7 @@ import "fmt" // implements IStep interface type stepRequestExtraction struct { - runner *Runner - step *TStep + step *TStep } func (s *stepRequestExtraction) WithJmesPath(jmesPath string, varName string) *stepRequestExtraction { @@ -27,6 +26,6 @@ func (s *stepRequestExtraction) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *stepRequestExtraction) Run(config *TConfig) error { - return s.runner.runStep(s.step) +func (s *stepRequestExtraction) ToStruct() *TStep { + return s.step } diff --git a/models.go b/models.go index f96b6f40..9d8ae60c 100644 --- a/models.go +++ b/models.go @@ -58,7 +58,7 @@ type TStep struct { type IStep interface { Name() string Type() string - Run(config *TConfig) error + ToStruct() *TStep } type TestCase struct { diff --git a/runner.go b/runner.go index a5efa4f0..4196d74f 100644 --- a/runner.go +++ b/runner.go @@ -1,6 +1,7 @@ package httpboomer import ( + "log" "net/http" "github.com/imroc/req" @@ -33,15 +34,24 @@ func (r *Runner) Run(testcases ...*TestCase) error { func (r *Runner) runCase(testcase *TestCase) error { config := &testcase.Config + log.Printf("Start to run testcase: %v", config.Name) for _, step := range testcase.TestSteps { - if err := step.Run(config); err != nil { + tStep := parseStep(step, config) + if err := r.runStep(tStep); err != nil { return err } } return nil } +func parseStep(step IStep, config *TConfig) *TStep { + tStep := step.ToStruct() + tStep.Request.URL = config.BaseURL + tStep.Request.URL + return tStep +} + func (r *Runner) runStep(step *TStep) error { + log.Printf("run step begin: %v >>>>>>", step.Name) var v []interface{} v = append(v, req.Header(step.Request.Headers)) v = append(v, req.Param(step.Request.Params)) @@ -53,11 +63,13 @@ func (r *Runner) runStep(step *TStep) error { }) } + req.Debug = true resp, err := r.Client.Do(string(step.Request.Method), step.Request.URL, v...) if err != nil { return err } resp.Response().Body.Close() + log.Printf("run step end: %v <<<<<<\n", step.Name) return nil } diff --git a/step.go b/step.go index ca9875ca..d64b2b36 100644 --- a/step.go +++ b/step.go @@ -4,7 +4,6 @@ import "fmt" func Step(name string) *step { return &step{ - runner: defaultRunner, TStep: &TStep{ Name: name, Request: &TRequest{}, @@ -14,16 +13,9 @@ func Step(name string) *step { } type step struct { - runner *Runner - config *TConfig *TStep } -func (s *step) WithRunner(runner *Runner) *step { - s.runner = runner - return s -} - func (s *step) WithVariables(variables map[string]interface{}) *step { s.TStep.Variables = variables return s @@ -38,8 +30,7 @@ func (s *step) GET(url string) *requestWithOptionalArgs { s.TStep.Request.Method = GET s.TStep.Request.URL = url return &requestWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } @@ -47,8 +38,7 @@ func (s *step) HEAD(url string) *requestWithOptionalArgs { s.TStep.Request.Method = HEAD s.TStep.Request.URL = url return &requestWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } @@ -56,8 +46,7 @@ func (s *step) POST(url string) *requestWithOptionalArgs { s.TStep.Request.Method = POST s.TStep.Request.URL = url return &requestWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } @@ -65,8 +54,7 @@ func (s *step) PUT(url string) *requestWithOptionalArgs { s.TStep.Request.Method = PUT s.TStep.Request.URL = url return &requestWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } @@ -74,8 +62,7 @@ func (s *step) DELETE(url string) *requestWithOptionalArgs { s.TStep.Request.Method = DELETE s.TStep.Request.URL = url return &requestWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } @@ -83,8 +70,7 @@ func (s *step) OPTIONS(url string) *requestWithOptionalArgs { s.TStep.Request.Method = OPTIONS s.TStep.Request.URL = url return &requestWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } @@ -92,8 +78,7 @@ func (s *step) PATCH(url string) *requestWithOptionalArgs { s.TStep.Request.Method = PATCH s.TStep.Request.URL = url return &requestWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } @@ -101,15 +86,13 @@ func (s *step) PATCH(url string) *requestWithOptionalArgs { func (s *step) CallRefCase(tc *TestCase) *testcaseWithOptionalArgs { s.TStep.TestCase = tc return &testcaseWithOptionalArgs{ - runner: s.runner, - step: s.TStep, + step: s.TStep, } } // implements IStep interface type requestWithOptionalArgs struct { - runner *Runner - step *TStep + step *TStep } func (s *requestWithOptionalArgs) SetVerify(verify bool) *requestWithOptionalArgs { @@ -169,15 +152,13 @@ func (s *requestWithOptionalArgs) TeardownHook(hook string) *requestWithOptional func (s *requestWithOptionalArgs) Validate() *stepRequestValidation { return &stepRequestValidation{ - runner: s.runner, - step: s.step, + step: s.step, } } func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction { return &stepRequestExtraction{ - runner: s.runner, - step: s.step, + step: s.step, } } @@ -189,14 +170,13 @@ func (s *requestWithOptionalArgs) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *requestWithOptionalArgs) Run(config *TConfig) error { - return s.runner.runStep(s.step) +func (s *requestWithOptionalArgs) ToStruct() *TStep { + return s.step } // implements IStep interface type testcaseWithOptionalArgs struct { - runner *Runner - step *TStep + step *TStep } func (s *testcaseWithOptionalArgs) TeardownHook(hook string) *testcaseWithOptionalArgs { @@ -217,6 +197,6 @@ func (s *testcaseWithOptionalArgs) Type() string { return "testcase" } -func (s *testcaseWithOptionalArgs) Run(config *TConfig) error { - return s.runner.runCase(s.step.TestCase) +func (s *testcaseWithOptionalArgs) ToStruct() *TStep { + return s.step } diff --git a/step_test.go b/step_test.go index 76b40807..d1c55366 100644 --- a/step_test.go +++ b/step_test.go @@ -6,14 +6,14 @@ import ( var ( stepGET = Step("get with params"). - GET("https://postman-echo.com/get"). + GET("/get"). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). WithCookies(map[string]string{"user": "debugtalk"}). Validate(). AssertEqual("status_code", 200, "check status code") stepPOSTData = Step("post form data"). - POST("https://postman-echo.com/post"). + POST("/post"). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). WithData("a=1&b=2"). @@ -27,8 +27,8 @@ func TestRunRequestGetToStruct(t *testing.T) { if tStep.Request.Method != GET { t.Fatalf("tStep.Request.Method != GET") } - if tStep.Request.URL != "https://postman-echo.com/get" { - t.Fatalf("tStep.Request.URL != 'https://postman-echo.com/get'") + if tStep.Request.URL != "/get" { + t.Fatalf("tStep.Request.URL != '/get'") } if tStep.Request.Params["foo1"] != "bar1" || tStep.Request.Params["foo2"] != "bar2" { t.Fatalf("tStep.Request.Params mismatch") @@ -49,8 +49,8 @@ func TestRunRequestPostDataToStruct(t *testing.T) { if tStep.Request.Method != POST { t.Fatalf("tStep.Request.Method != POST") } - if tStep.Request.URL != "https://postman-echo.com/post" { - t.Fatalf("tStep.Request.URL != 'https://postman-echo.com/post'") + if tStep.Request.URL != "/post" { + t.Fatalf("tStep.Request.URL != '/post'") } if tStep.Request.Params["foo1"] != "bar1" || tStep.Request.Params["foo2"] != "bar2" { t.Fatalf("tStep.Request.Params mismatch") @@ -70,11 +70,13 @@ func TestRunRequestPostDataToStruct(t *testing.T) { } func TestRunRequestRun(t *testing.T) { - config := &TConfig{} - if err := stepGET.Run(config); err != nil { + config := &TConfig{ + BaseURL: "https://postman-echo.com", + } + if err := defaultRunner.runStep(parseStep(stepGET, config)); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := stepPOSTData.Run(config); err != nil { + if err := defaultRunner.runStep(parseStep(stepPOSTData, config)); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } diff --git a/validate.go b/validate.go index b27fab7a..f385f3c0 100644 --- a/validate.go +++ b/validate.go @@ -4,8 +4,7 @@ import "fmt" // implements IStep interface type stepRequestValidation struct { - runner *Runner - step *TStep + step *TStep } func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { @@ -27,6 +26,6 @@ func (s *stepRequestValidation) Type() string { return fmt.Sprintf("request-%v", s.step.Request.Method) } -func (s *stepRequestValidation) Run(config *TConfig) error { - return s.runner.runStep(s.step) +func (s *stepRequestValidation) ToStruct() *TStep { + return s.step } From cc386767cab1af9d7454bd044a9ad7e68bb2772b Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 23 Sep 2021 11:23:26 +0800 Subject: [PATCH 016/479] feat: buildURL --- parser.go | 29 +++++++++++++++++++++++++++++ parser_test.go | 29 +++++++++++++++++++++++++++++ runner.go | 6 ------ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 parser.go create mode 100644 parser_test.go diff --git a/parser.go b/parser.go new file mode 100644 index 00000000..48ec28d7 --- /dev/null +++ b/parser.go @@ -0,0 +1,29 @@ +package httpboomer + +import ( + "log" + "net/url" +) + +func parseStep(step IStep, config *TConfig) *TStep { + tStep := step.ToStruct() + tStep.Request.URL = buildURL(config.BaseURL, tStep.Request.URL) + return tStep +} + +func buildURL(baseURL, stepURL string) string { + uConfig, err := url.Parse(baseURL) + if err != nil { + log.Fatalf("[buildURL] baseURL: %v, error: %v", baseURL, err) + return "" + } + + uStep, err := uConfig.Parse(stepURL) + if err != nil { + log.Fatalf("[buildURL] baseURL: %v, error: %v", baseURL, err) + return "" + } + + // base url missed + return uStep.String() +} diff --git a/parser_test.go b/parser_test.go new file mode 100644 index 00000000..5e1b67ac --- /dev/null +++ b/parser_test.go @@ -0,0 +1,29 @@ +package httpboomer + +import ( + "testing" +) + +func TestBuildURL(t *testing.T) { + var url string + url = buildURL("https://postman-echo.com", "/get") + if url != "https://postman-echo.com/get" { + t.Fatalf("buildURL error, %s != 'https://postman-echo.com/get'", url) + } + + url = buildURL("https://postman-echo.com/abc/", "/get?a=1&b=2") + if url != "https://postman-echo.com/get?a=1&b=2" { + t.Fatalf("buildURL error, %s != 'https://postman-echo.com/get'", url) + } + + url = buildURL("", "https://postman-echo.com/get") + if url != "https://postman-echo.com/get" { + t.Fatalf("buildURL error, %s != 'https://postman-echo.com/get'", url) + } + + // notice: step request url > config base url + url = buildURL("https://postman-echo.com", "https://httpbin.org/get") + if url != "https://httpbin.org/get" { + t.Fatalf("buildURL error, %s != 'https://httpbin.org/get'", url) + } +} diff --git a/runner.go b/runner.go index 4196d74f..2362ab3a 100644 --- a/runner.go +++ b/runner.go @@ -44,12 +44,6 @@ func (r *Runner) runCase(testcase *TestCase) error { return nil } -func parseStep(step IStep, config *TConfig) *TStep { - tStep := step.ToStruct() - tStep.Request.URL = config.BaseURL + tStep.Request.URL - return tStep -} - func (r *Runner) runStep(step *TStep) error { log.Printf("run step begin: %v >>>>>>", step.Name) var v []interface{} From c2effd3f75c0198bc70bca15f31ee88374218e3c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 23 Sep 2021 11:50:17 +0800 Subject: [PATCH 017/479] fix: init step request --- .gitignore | 2 ++ step.go | 43 ++++++++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 66fd13c9..d6993ff2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +__debug_bin \ No newline at end of file diff --git a/step.go b/step.go index d64b2b36..e5920b50 100644 --- a/step.go +++ b/step.go @@ -6,7 +6,6 @@ func Step(name string) *step { return &step{ TStep: &TStep{ Name: name, - Request: &TRequest{}, Variables: make(map[string]interface{}), }, } @@ -27,56 +26,70 @@ func (s *step) SetupHook(hook string) *step { } func (s *step) GET(url string) *requestWithOptionalArgs { - s.TStep.Request.Method = GET - s.TStep.Request.URL = url + s.TStep.Request = &TRequest{ + Method: GET, + URL: url, + } return &requestWithOptionalArgs{ step: s.TStep, } } func (s *step) HEAD(url string) *requestWithOptionalArgs { - s.TStep.Request.Method = HEAD - s.TStep.Request.URL = url + s.TStep.Request = &TRequest{ + Method: HEAD, + URL: url, + } return &requestWithOptionalArgs{ step: s.TStep, } } func (s *step) POST(url string) *requestWithOptionalArgs { - s.TStep.Request.Method = POST - s.TStep.Request.URL = url + s.TStep.Request = &TRequest{ + Method: POST, + URL: url, + } return &requestWithOptionalArgs{ step: s.TStep, } } func (s *step) PUT(url string) *requestWithOptionalArgs { - s.TStep.Request.Method = PUT - s.TStep.Request.URL = url + s.TStep.Request = &TRequest{ + Method: PUT, + URL: url, + } return &requestWithOptionalArgs{ step: s.TStep, } } func (s *step) DELETE(url string) *requestWithOptionalArgs { - s.TStep.Request.Method = DELETE - s.TStep.Request.URL = url + s.TStep.Request = &TRequest{ + Method: DELETE, + URL: url, + } return &requestWithOptionalArgs{ step: s.TStep, } } func (s *step) OPTIONS(url string) *requestWithOptionalArgs { - s.TStep.Request.Method = OPTIONS - s.TStep.Request.URL = url + s.TStep.Request = &TRequest{ + Method: OPTIONS, + URL: url, + } return &requestWithOptionalArgs{ step: s.TStep, } } func (s *step) PATCH(url string) *requestWithOptionalArgs { - s.TStep.Request.Method = PATCH - s.TStep.Request.URL = url + s.TStep.Request = &TRequest{ + Method: PATCH, + URL: url, + } return &requestWithOptionalArgs{ step: s.TStep, } From cad10bc8b6e37de1b860be2001c235e27b796016 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 23 Sep 2021 11:59:52 +0800 Subject: [PATCH 018/479] feat: run referenced testcase --- runner.go | 15 ++++++++++++--- runner_test.go | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/runner.go b/runner.go index 2362ab3a..6a21ca34 100644 --- a/runner.go +++ b/runner.go @@ -36,9 +36,18 @@ func (r *Runner) runCase(testcase *TestCase) error { config := &testcase.Config log.Printf("Start to run testcase: %v", config.Name) for _, step := range testcase.TestSteps { - tStep := parseStep(step, config) - if err := r.runStep(tStep); err != nil { - return err + if tc, ok := step.(*testcaseWithOptionalArgs); ok { + // run referenced testcase + log.Printf("run referenced testcase: %v", tc.step.Name) + if err := r.runCase(tc.step.TestCase); err != nil { + return err + } + } else { + // run request + tStep := parseStep(step, config) + if err := r.runStep(tStep); err != nil { + return err + } } } return nil diff --git a/runner_test.go b/runner_test.go index c21c5321..842a8bbe 100644 --- a/runner_test.go +++ b/runner_test.go @@ -21,7 +21,7 @@ func TestHttpRunner(t *testing.T) { Validate(). AssertEqual("status_code", 200, "check status code"). AssertEqual("body.\"user-agent\"", "python-requests", "check User-Agent"), - Step("TestCase3").CallRefCase(&TestCase{}), + Step("TestCase3").CallRefCase(&TestCase{Config: TConfig{Name: "TestCase3"}}), }, } testcase2 := &TestCase{ From e95cc1a05ca2db8202703bc912c0189ad6fae0c2 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 23 Sep 2021 21:37:14 +0800 Subject: [PATCH 019/479] feat: support post data --- examples/postman_echo/hardcode_test.go | 36 ++++++++++++++++++++++++++ runner.go | 1 + 2 files changed, 37 insertions(+) create mode 100644 examples/postman_echo/hardcode_test.go diff --git a/examples/postman_echo/hardcode_test.go b/examples/postman_echo/hardcode_test.go new file mode 100644 index 00000000..6e8340b8 --- /dev/null +++ b/examples/postman_echo/hardcode_test.go @@ -0,0 +1,36 @@ +package postman_echo + +import ( + "testing" + + "github.com/httprunner/httpboomer" +) + +func TestCaseHardcode(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "request methods testcase in hardcode", + BaseURL: "https://postman-echo.com", + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + Validate(). + AssertEqual("status_code", 200, "check status code"), + httpboomer.Step("post raw text"). + POST("/post"). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "text/plain"}). + WithData("This is expected to be sent back as part of response body."). + Validate(). + AssertEqual("status_code", 200, "check status code"), + }, + } + + err := httpboomer.Test(testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/runner.go b/runner.go index 6a21ca34..4419619a 100644 --- a/runner.go +++ b/runner.go @@ -58,6 +58,7 @@ func (r *Runner) runStep(step *TStep) error { var v []interface{} v = append(v, req.Header(step.Request.Headers)) v = append(v, req.Param(step.Request.Params)) + v = append(v, step.Request.Data) for cookieName, cookieValue := range step.Request.Cookies { v = append(v, &http.Cookie{ From 2614d22003484dd388c71879bdac7f733009ea55 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 23 Sep 2021 21:42:42 +0800 Subject: [PATCH 020/479] examples: add post form data and put request --- examples/postman_echo/hardcode_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/postman_echo/hardcode_test.go b/examples/postman_echo/hardcode_test.go index 6e8340b8..e9dbd4aa 100644 --- a/examples/postman_echo/hardcode_test.go +++ b/examples/postman_echo/hardcode_test.go @@ -26,6 +26,18 @@ func TestCaseHardcode(t *testing.T) { WithData("This is expected to be sent back as part of response body."). Validate(). AssertEqual("status_code", 200, "check status code"), + httpboomer.Step("post form data"). + POST("/post"). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). + WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). + Validate(). + AssertEqual("status_code", 200, "check status code"), + httpboomer.Step("put request"). + PUT("/put"). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "text/plain"}). + WithData("This is expected to be sent back as part of response body."). + Validate(). + AssertEqual("status_code", 200, "check status code"), }, } From c446f3bf810d4f8dd71ce97c458703c0019e77fe Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 23 Sep 2021 21:53:30 +0800 Subject: [PATCH 021/479] feat: support post json --- examples/postman_echo/hardcode_test.go | 6 ++++++ runner.go | 1 + 2 files changed, 7 insertions(+) diff --git a/examples/postman_echo/hardcode_test.go b/examples/postman_echo/hardcode_test.go index e9dbd4aa..66837d74 100644 --- a/examples/postman_echo/hardcode_test.go +++ b/examples/postman_echo/hardcode_test.go @@ -32,6 +32,12 @@ func TestCaseHardcode(t *testing.T) { WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). Validate(). AssertEqual("status_code", 200, "check status code"), + httpboomer.Step("post json data"). + POST("/post"). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + WithJSON(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). + Validate(). + AssertEqual("status_code", 200, "check status code"), httpboomer.Step("put request"). PUT("/put"). WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "text/plain"}). diff --git a/runner.go b/runner.go index 4419619a..f88f6891 100644 --- a/runner.go +++ b/runner.go @@ -59,6 +59,7 @@ func (r *Runner) runStep(step *TStep) error { v = append(v, req.Header(step.Request.Headers)) v = append(v, req.Param(step.Request.Params)) v = append(v, step.Request.Data) + v = append(v, req.BodyJSON(step.Request.JSON)) for cookieName, cookieValue := range step.Request.Cookies { v = append(v, &http.Cookie{ From c20f9b07adc8ae047a14e9309ecc0c95b8e6d79d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 24 Sep 2021 11:24:13 +0800 Subject: [PATCH 022/479] feat: runStepRequest --- boomer.go | 3 +-- runner.go | 40 +++++++++++++++++++++++----------------- step_test.go | 4 ++-- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/boomer.go b/boomer.go index eed95aff..9fc7e5ab 100644 --- a/boomer.go +++ b/boomer.go @@ -37,8 +37,7 @@ func convertBoomerTask(testcase *TestCase) *boomer.Task { config := &testcase.Config for _, step := range testcase.TestSteps { start := time.Now() - tStep := parseStep(step, config) - err := runner.runStep(tStep) + err := runner.runStep(step, config) elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) if err == nil { diff --git a/runner.go b/runner.go index f88f6891..35332be8 100644 --- a/runner.go +++ b/runner.go @@ -36,25 +36,32 @@ func (r *Runner) runCase(testcase *TestCase) error { config := &testcase.Config log.Printf("Start to run testcase: %v", config.Name) for _, step := range testcase.TestSteps { - if tc, ok := step.(*testcaseWithOptionalArgs); ok { - // run referenced testcase - log.Printf("run referenced testcase: %v", tc.step.Name) - if err := r.runCase(tc.step.TestCase); err != nil { - return err - } - } else { - // run request - tStep := parseStep(step, config) - if err := r.runStep(tStep); err != nil { - return err - } - } + r.runStep(step, config) } return nil } -func (r *Runner) runStep(step *TStep) error { - log.Printf("run step begin: %v >>>>>>", step.Name) +func (r *Runner) runStep(step IStep, config *TConfig) error { + log.Printf("run step begin: %v >>>>>>", step.Name()) + if tc, ok := step.(*testcaseWithOptionalArgs); ok { + // run referenced testcase + log.Printf("run referenced testcase: %v", tc.step.Name) + // TODO: override testcase config + if err := r.runCase(tc.step.TestCase); err != nil { + return err + } + } else { + // run request + tStep := parseStep(step, config) + if err := r.runStepRequest(tStep); err != nil { + return err + } + } + log.Printf("run step end: %v <<<<<<\n", step.Name()) + return nil +} + +func (r *Runner) runStepRequest(step *TStep) error { var v []interface{} v = append(v, req.Header(step.Request.Headers)) v = append(v, req.Param(step.Request.Params)) @@ -73,8 +80,7 @@ func (r *Runner) runStep(step *TStep) error { if err != nil { return err } - resp.Response().Body.Close() - log.Printf("run step end: %v <<<<<<\n", step.Name) + defer resp.Response().Body.Close() return nil } diff --git a/step_test.go b/step_test.go index d1c55366..c2d6337b 100644 --- a/step_test.go +++ b/step_test.go @@ -73,10 +73,10 @@ func TestRunRequestRun(t *testing.T) { config := &TConfig{ BaseURL: "https://postman-echo.com", } - if err := defaultRunner.runStep(parseStep(stepGET, config)); err != nil { + if err := defaultRunner.runStep(stepGET, config); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := defaultRunner.runStep(parseStep(stepPOSTData, config)); err != nil { + if err := defaultRunner.runStep(stepPOSTData, config); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } From df7366af5527996e8b87bc1daa00cff9e196f60c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 24 Sep 2021 11:31:34 +0800 Subject: [PATCH 023/479] feat: runStepTestCase --- runner.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runner.go b/runner.go index 35332be8..6fb50604 100644 --- a/runner.go +++ b/runner.go @@ -47,7 +47,7 @@ func (r *Runner) runStep(step IStep, config *TConfig) error { // run referenced testcase log.Printf("run referenced testcase: %v", tc.step.Name) // TODO: override testcase config - if err := r.runCase(tc.step.TestCase); err != nil { + if err := r.runStepTestCase(tc.step); err != nil { return err } } else { @@ -84,6 +84,11 @@ func (r *Runner) runStepRequest(step *TStep) error { return nil } +func (r *Runner) runStepTestCase(step *TStep) error { + testcase := step.TestCase + return r.runCase(testcase) +} + func (r *Runner) GetSummary() *TestCaseSummary { return &TestCaseSummary{} } From 9f59dbf340a5c4e38b19a38cb76fe03287e58068 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 26 Sep 2021 16:28:18 +0800 Subject: [PATCH 024/479] feat: validate response object --- examples/postman_echo/hardcode_test.go | 2 +- go.mod | 1 + go.sum | 4 + models.go | 8 +- response.go | 109 +++++++++++++++++++++++++ runner.go | 22 ++++- runner_test.go | 6 +- step_test.go | 10 ++- validate.go | 12 +-- 9 files changed, 156 insertions(+), 18 deletions(-) diff --git a/examples/postman_echo/hardcode_test.go b/examples/postman_echo/hardcode_test.go index 66837d74..bc74ae2f 100644 --- a/examples/postman_echo/hardcode_test.go +++ b/examples/postman_echo/hardcode_test.go @@ -47,7 +47,7 @@ func TestCaseHardcode(t *testing.T) { }, } - err := httpboomer.Test(testcase) + err := httpboomer.Test(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/go.mod b/go.mod index 0b5ada3b..df60a9f1 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect github.com/google/uuid v1.3.0 // indirect github.com/imroc/req v0.3.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/myzhan/boomer v1.6.0 github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/shirou/gopsutil v3.21.8+incompatible // indirect diff --git a/go.sum b/go.sum index ce775214..8c81c378 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,9 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U= github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0= @@ -42,5 +45,6 @@ golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/models.go b/models.go index 9d8ae60c..3c48d2e3 100644 --- a/models.go +++ b/models.go @@ -36,10 +36,10 @@ type TRequest struct { } type TValidator struct { - Check string // get value with jmespath - Comparator string - Expect interface{} - Message string + Check string // get value with jmespath + Assert string + Expect interface{} + Message string } type TStep struct { diff --git a/response.go b/response.go index eb187cb3..5d4cba91 100644 --- a/response.go +++ b/response.go @@ -1 +1,110 @@ package httpboomer + +import ( + "encoding/json" + "log" + "testing" + + "github.com/imroc/req" + "github.com/jmespath/go-jmespath" + "github.com/stretchr/testify/assert" +) + +var assertFunctionsMap = map[string]func(t assert.TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool{ + "equals": assert.EqualValues, + "equal": assert.EqualValues, // alias for equals + "greater_than": assert.Greater, + "less_than": assert.Less, + "greater_or_equals": assert.GreaterOrEqual, + "less_or_equals": assert.LessOrEqual, + "not_equal": assert.NotEqual, + "contains": assert.Contains, + "regex_match": assert.Regexp, +} + +func NewResponseObject(t *testing.T, resp *req.Resp) *ResponseObject { + // prepare response headers + headers := make(map[string]string) + for k, v := range resp.Response().Header { + if len(v) > 0 { + headers[k] = v[0] + } + } + + // prepare response cookies + cookies := make(map[string]string) + for _, cookie := range resp.Response().Cookies() { + cookies[cookie.Name] = cookie.Value + } + + // parse response body + var body interface{} + if err := json.Unmarshal(resp.Bytes(), &body); err != nil { + log.Fatalf("[NewResponseObject] json.Unmarshal response body err: %v, body: %v", + err, string(resp.Bytes())) + return nil + } + + respObjMeta := respObjMeta{ + StatusCode: resp.Response().StatusCode, + Headers: headers, + Cookies: cookies, + Body: body, + } + + // convert respObjMeta to interface{} + respObjMetaBytes, _ := json.Marshal(respObjMeta) + var data interface{} + if err := json.Unmarshal(respObjMetaBytes, &data); err != nil { + log.Fatalf("[NewResponseObject] json.Unmarshal respObjMeta err: %v, respObjMetaBytes: %v", + err, string(respObjMetaBytes)) + return nil + } + + return &ResponseObject{ + t: t, + respObjMeta: data, + } +} + +type respObjMeta struct { + StatusCode int `json:"status_code"` + Headers map[string]string `json:"headers"` + Cookies map[string]string `json:"cookies"` + Body interface{} `json:"body"` +} + +type ResponseObject struct { + t *testing.T + respObjMeta interface{} + validationResults map[string]interface{} +} + +func (v *ResponseObject) Validate(validators []TValidator) error { + for _, validator := range validators { + // parse check value + checkItem := validator.Check + checkValue := v.searchJmespath(checkItem) + // get assert method + assertMethod := validator.Assert + assertFunc := assertFunctionsMap[assertMethod] + // parse expected value + expectValue := validator.Expect + // do assertion + result := assertFunc(v.t, expectValue, checkValue) + log.Printf("assert %s %s %v => %v", checkItem, assertMethod, expectValue, result) + if !result { + v.t.Fail() + } + } + return nil +} + +func (v *ResponseObject) searchJmespath(expr string) interface{} { + checkValue, err := jmespath.Search(expr, v.respObjMeta) + if err != nil { + log.Printf("[searchJmespath] jmespath.Search error: %v", err) + return nil + } + return checkValue +} diff --git a/runner.go b/runner.go index 6fb50604..d0bb60f8 100644 --- a/runner.go +++ b/runner.go @@ -3,26 +3,34 @@ package httpboomer import ( "log" "net/http" + "testing" "github.com/imroc/req" ) var defaultRunner = NewRunner() -func Test(testcases ...*TestCase) error { - return defaultRunner.Run(testcases...) +func Test(t *testing.T, testcases ...*TestCase) error { + return defaultRunner.WithTestingT(t).Run(testcases...) } func NewRunner() *Runner { return &Runner{ + t: &testing.T{}, Client: req.New(), } } type Runner struct { + t *testing.T Client *req.Req } +func (r *Runner) WithTestingT(t *testing.T) *Runner { + r.t = t + return r +} + func (r *Runner) Run(testcases ...*TestCase) error { for _, testcase := range testcases { if err := r.runCase(testcase); err != nil { @@ -62,6 +70,7 @@ func (r *Runner) runStep(step IStep, config *TConfig) error { } func (r *Runner) runStepRequest(step *TStep) error { + // prepare request args var v []interface{} v = append(v, req.Header(step.Request.Headers)) v = append(v, req.Param(step.Request.Params)) @@ -75,12 +84,21 @@ func (r *Runner) runStepRequest(step *TStep) error { }) } + // do request action req.Debug = true resp, err := r.Client.Do(string(step.Request.Method), step.Request.URL, v...) if err != nil { return err } defer resp.Response().Body.Close() + + // validate response + respObj := NewResponseObject(r.t, resp) + err = respObj.Validate(step.Validators) + if err != nil { + return err + } + return nil } diff --git a/runner_test.go b/runner_test.go index 842a8bbe..8a490e23 100644 --- a/runner_test.go +++ b/runner_test.go @@ -15,12 +15,12 @@ func TestHttpRunner(t *testing.T) { GET("/headers"). Validate(). AssertEqual("status_code", 200, "check status code"). - AssertEqual("headers.Host", "httpbin.org", "check http response host"), + AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), Step("user-agent"). GET("/user-agent"). Validate(). AssertEqual("status_code", 200, "check status code"). - AssertEqual("body.\"user-agent\"", "python-requests", "check User-Agent"), + AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), Step("TestCase3").CallRefCase(&TestCase{Config: TConfig{Name: "TestCase3"}}), }, } @@ -31,7 +31,7 @@ func TestHttpRunner(t *testing.T) { }, } - err := Test(testcase1, testcase2) + err := Test(t, testcase1, testcase2) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/step_test.go b/step_test.go index c2d6337b..08f0d922 100644 --- a/step_test.go +++ b/step_test.go @@ -11,7 +11,11 @@ var ( WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). WithCookies(map[string]string{"user": "debugtalk"}). Validate(). - AssertEqual("status_code", 200, "check status code") + AssertEqual("status_code", 200, "check status code"). + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). + AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). + AssertEqual("body.args.foo1", "bar1", "check param foo1"). + AssertEqual("body.args.foo2", "bar2", "check param foo2") stepPOSTData = Step("post form data"). POST("/post"). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). @@ -73,10 +77,10 @@ func TestRunRequestRun(t *testing.T) { config := &TConfig{ BaseURL: "https://postman-echo.com", } - if err := defaultRunner.runStep(stepGET, config); err != nil { + if err := defaultRunner.WithTestingT(t).runStep(stepGET, config); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := defaultRunner.runStep(stepPOSTData, config); err != nil { + if err := defaultRunner.WithTestingT(t).runStep(stepPOSTData, config); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } diff --git a/validate.go b/validate.go index f385f3c0..8bc1f922 100644 --- a/validate.go +++ b/validate.go @@ -1,6 +1,8 @@ package httpboomer -import "fmt" +import ( + "fmt" +) // implements IStep interface type stepRequestValidation struct { @@ -9,10 +11,10 @@ type stepRequestValidation struct { func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { validator := TValidator{ - Check: jmesPath, - Comparator: "equals", - Expect: expected, - Message: msg, + Check: jmesPath, + Assert: "equals", + Expect: expected, + Message: msg, } s.step.Validators = append(s.step.Validators, validator) return s From 33c5917412cb29925999370f7c5ec4e69743c7a2 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 26 Sep 2021 16:50:59 +0800 Subject: [PATCH 025/479] fix: post data --- examples/postman_echo/hardcode_test.go | 20 +++++++++++++++----- runner.go | 16 ++++++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/examples/postman_echo/hardcode_test.go b/examples/postman_echo/hardcode_test.go index bc74ae2f..f061d146 100644 --- a/examples/postman_echo/hardcode_test.go +++ b/examples/postman_echo/hardcode_test.go @@ -19,31 +19,41 @@ func TestCaseHardcode(t *testing.T) { WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). Validate(). - AssertEqual("status_code", 200, "check status code"), + AssertEqual("status_code", 200, "check status code"). + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). + AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). + AssertEqual("body.args.foo1", "bar1", "check args foo1"). + AssertEqual("body.args.foo2", "bar2", "check args foo2"), httpboomer.Step("post raw text"). POST("/post"). WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "text/plain"}). WithData("This is expected to be sent back as part of response body."). Validate(). - AssertEqual("status_code", 200, "check status code"), + AssertEqual("status_code", 200, "check status code"). + AssertEqual("body.data", "This is expected to be sent back as part of response body.", "check data"), httpboomer.Step("post form data"). POST("/post"). WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "application/x-www-form-urlencoded"}). WithParams(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). Validate(). - AssertEqual("status_code", 200, "check status code"), + AssertEqual("status_code", 200, "check status code"). + AssertEqual("body.form.foo1", "bar1", "check form foo1"). + AssertEqual("body.form.foo2", "bar2", "check form foo2"), httpboomer.Step("post json data"). POST("/post"). WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). WithJSON(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). Validate(). - AssertEqual("status_code", 200, "check status code"), + AssertEqual("status_code", 200, "check status code"). + AssertEqual("body.json.foo1", "bar1", "check json foo1"). + AssertEqual("body.json.foo2", "bar2", "check json foo2"), httpboomer.Step("put request"). PUT("/put"). WithHeaders(map[string]string{"User-Agent": "HttpBoomer", "Content-Type": "text/plain"}). WithData("This is expected to be sent back as part of response body."). Validate(). - AssertEqual("status_code", 200, "check status code"), + AssertEqual("status_code", 200, "check status code"). + AssertEqual("body.data", "This is expected to be sent back as part of response body.", "check data"), }, } diff --git a/runner.go b/runner.go index d0bb60f8..7cee988c 100644 --- a/runner.go +++ b/runner.go @@ -72,10 +72,18 @@ func (r *Runner) runStep(step IStep, config *TConfig) error { func (r *Runner) runStepRequest(step *TStep) error { // prepare request args var v []interface{} - v = append(v, req.Header(step.Request.Headers)) - v = append(v, req.Param(step.Request.Params)) - v = append(v, step.Request.Data) - v = append(v, req.BodyJSON(step.Request.JSON)) + if len(step.Request.Headers) > 0 { + v = append(v, req.Header(step.Request.Headers)) + } + if len(step.Request.Params) > 0 { + v = append(v, req.Param(step.Request.Params)) + } + if step.Request.Data != nil { + v = append(v, step.Request.Data) + } + if step.Request.JSON != nil { + v = append(v, req.BodyJSON(step.Request.JSON)) + } for cookieName, cookieValue := range step.Request.Cookies { v = append(v, &http.Cookie{ From c7e2f3b8087a30ff8783997cb3a37f0f3b2bb422 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 28 Sep 2021 15:48:13 +0800 Subject: [PATCH 026/479] feat: support set debug for runner --- runner.go | 9 ++++++++- step_test.go | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/runner.go b/runner.go index 7cee988c..d8779c23 100644 --- a/runner.go +++ b/runner.go @@ -17,12 +17,14 @@ func Test(t *testing.T, testcases ...*TestCase) error { func NewRunner() *Runner { return &Runner{ t: &testing.T{}, + debug: false, // default to turn off debug Client: req.New(), } } type Runner struct { t *testing.T + debug bool Client *req.Req } @@ -31,6 +33,11 @@ func (r *Runner) WithTestingT(t *testing.T) *Runner { return r } +func (r *Runner) SetDebug(debug bool) *Runner { + r.debug = debug + return r +} + func (r *Runner) Run(testcases ...*TestCase) error { for _, testcase := range testcases { if err := r.runCase(testcase); err != nil { @@ -93,7 +100,7 @@ func (r *Runner) runStepRequest(step *TStep) error { } // do request action - req.Debug = true + req.Debug = r.debug resp, err := r.Client.Do(string(step.Request.Method), step.Request.URL, v...) if err != nil { return err diff --git a/step_test.go b/step_test.go index 08f0d922..09d42d31 100644 --- a/step_test.go +++ b/step_test.go @@ -77,10 +77,10 @@ func TestRunRequestRun(t *testing.T) { config := &TConfig{ BaseURL: "https://postman-echo.com", } - if err := defaultRunner.WithTestingT(t).runStep(stepGET, config); err != nil { + if err := defaultRunner.WithTestingT(t).SetDebug(true).runStep(stepGET, config); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := defaultRunner.WithTestingT(t).runStep(stepPOSTData, config); err != nil { + if err := defaultRunner.WithTestingT(t).SetDebug(true).runStep(stepPOSTData, config); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } From 33112642f0554fd0a0f9d0112efc89f859b22ed3 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 28 Sep 2021 15:48:53 +0800 Subject: [PATCH 027/479] feat: support set debug for boomer --- boomer.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/boomer.go b/boomer.go index 9fc7e5ab..dce01639 100644 --- a/boomer.go +++ b/boomer.go @@ -13,23 +13,32 @@ func Run(testcases ...*TestCase) { } func NewBoomer() *Boomer { - return &Boomer{} + return &Boomer{ + debug: false, + } } type Boomer struct { + debug bool +} + +func (b *Boomer) SetDebug(debug bool) *Boomer { + b.debug = debug + return b } func (b *Boomer) Run(testcases ...*TestCase) { var taskSlice []*boomer.Task for _, testcase := range testcases { - task := convertBoomerTask(testcase) + task := b.convertBoomerTask(testcase) taskSlice = append(taskSlice, task) } boomer.Run(taskSlice...) } -func convertBoomerTask(testcase *TestCase) *boomer.Task { +func (b *Boomer) convertBoomerTask(testcase *TestCase) *boomer.Task { runner := NewRunner() + runner = runner.SetDebug(b.debug) return &boomer.Task{ Name: testcase.Config.Name, Weight: testcase.Config.Weight, From 698733c52214c27f2c48a0ab2ffdba118a829666 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 28 Sep 2021 16:08:54 +0800 Subject: [PATCH 028/479] change: remove defaultRunner --- boomer.go | 3 +-- runner.go | 4 +--- step_test.go | 5 +++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/boomer.go b/boomer.go index dce01639..5053110b 100644 --- a/boomer.go +++ b/boomer.go @@ -37,8 +37,7 @@ func (b *Boomer) Run(testcases ...*TestCase) { } func (b *Boomer) convertBoomerTask(testcase *TestCase) *boomer.Task { - runner := NewRunner() - runner = runner.SetDebug(b.debug) + runner := NewRunner().SetDebug(b.debug) return &boomer.Task{ Name: testcase.Config.Name, Weight: testcase.Config.Weight, diff --git a/runner.go b/runner.go index d8779c23..9ac2ff56 100644 --- a/runner.go +++ b/runner.go @@ -8,10 +8,8 @@ import ( "github.com/imroc/req" ) -var defaultRunner = NewRunner() - func Test(t *testing.T, testcases ...*TestCase) error { - return defaultRunner.WithTestingT(t).Run(testcases...) + return NewRunner().WithTestingT(t).SetDebug(true).Run(testcases...) } func NewRunner() *Runner { diff --git a/step_test.go b/step_test.go index 09d42d31..f6d00e90 100644 --- a/step_test.go +++ b/step_test.go @@ -77,10 +77,11 @@ func TestRunRequestRun(t *testing.T) { config := &TConfig{ BaseURL: "https://postman-echo.com", } - if err := defaultRunner.WithTestingT(t).SetDebug(true).runStep(stepGET, config); err != nil { + runner := NewRunner().SetDebug(true).WithTestingT(t) + if err := runner.runStep(stepGET, config); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := defaultRunner.WithTestingT(t).SetDebug(true).runStep(stepPOSTData, config); err != nil { + if err := runner.runStep(stepPOSTData, config); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } From b6ba6214bce746024e5f04b66609a27e48f2a8fa Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 28 Sep 2021 16:10:33 +0800 Subject: [PATCH 029/479] change: remove defaultBoomer --- boomer.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/boomer.go b/boomer.go index 5053110b..73c2a2d1 100644 --- a/boomer.go +++ b/boomer.go @@ -6,10 +6,8 @@ import ( "github.com/myzhan/boomer" ) -var defaultBoomer = NewBoomer() - func Run(testcases ...*TestCase) { - defaultBoomer.Run(testcases...) + NewBoomer().Run(testcases...) } func NewBoomer() *Boomer { From 93d5a18992fd637e8b8693e0df349712f22d9d52 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 28 Sep 2021 22:50:24 +0800 Subject: [PATCH 030/479] feat: parse variable --- parser.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ parser_test.go | 29 ++++++++++++++++++++++++++ response.go | 4 ++-- runner.go | 2 +- 4 files changed, 87 insertions(+), 3 deletions(-) diff --git a/parser.go b/parser.go index 48ec28d7..4540f777 100644 --- a/parser.go +++ b/parser.go @@ -1,8 +1,11 @@ package httpboomer import ( + "fmt" "log" "net/url" + "regexp" + "strings" ) func parseStep(step IStep, config *TConfig) *TStep { @@ -27,3 +30,55 @@ func buildURL(baseURL, stepURL string) string { // base url missed return uStep.String() } + +func parseData(raw interface{}, variablesMapping map[string]interface{}) interface{} { + switch v := raw.(type) { + case string: + v = strings.TrimSpace(v) + return parseString(v, variablesMapping) + default: + return raw + } +} + +var ( + regexCompileVariable = regexp.MustCompile(`\$\{(\w+)\}|\$(\w+)`) // parse ${var} or $var +) + +// parseString parse string with variables +func parseString(raw string, variablesMapping map[string]interface{}) interface{} { + matchStartPosition := strings.Index(raw, "$") + if matchStartPosition == -1 { // no $ found + return raw + } + parsedString := raw[0:matchStartPosition] + + for matchStartPosition < len(raw) { + // search variable like ${var} or $var + varMatched := regexCompileVariable.FindStringSubmatch(raw[matchStartPosition:]) + if len(varMatched) == 3 { + var varName string + if varMatched[1] != "" { + varName = varMatched[1] // match ${var} + } else { + varName = varMatched[2] // match $var + } + varValue := variablesMapping[varName] + + if fmt.Sprintf("${%s}", varName) == raw || fmt.Sprintf("$%s", varName) == raw { + // raw string is a variable, $var or ${var}, return its value directly + return varValue + } + + parsedString += fmt.Sprintf("%v", varValue) + matchStartPosition += len(varMatched[0]) + log.Printf("[parseString] parsedString: %v, matchStartPosition: %v", parsedString, matchStartPosition) + continue + } + + // append remained string + parsedString += raw[matchStartPosition:] + } + + return parsedString +} diff --git a/parser_test.go b/parser_test.go index 5e1b67ac..c6ff2576 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2,6 +2,8 @@ package httpboomer import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestBuildURL(t *testing.T) { @@ -27,3 +29,30 @@ func TestBuildURL(t *testing.T) { t.Fatalf("buildURL error, %s != 'https://httpbin.org/get'", url) } } + +func TestParseDataStringWithVariables(t *testing.T) { + variablesMapping := map[string]interface{}{ + "var_1": "abc", + "var_2": "def", + "var_3": 123, + "var_4": map[string]interface{}{"a": 1}, + "var_5": true, + "var_6": nil, + } + + if !assert.Equal(t, "abc", parseData("$var_1", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "abc", parseData("${var_1}", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "var_1", parseData("var_1", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "abc#XYZ", parseData("$var_1#XYZ", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "abc#XYZ", parseData("${var_1}#XYZ", variablesMapping)) { + t.Fail() + } +} diff --git a/response.go b/response.go index 5d4cba91..25a70704 100644 --- a/response.go +++ b/response.go @@ -80,7 +80,7 @@ type ResponseObject struct { validationResults map[string]interface{} } -func (v *ResponseObject) Validate(validators []TValidator) error { +func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) error { for _, validator := range validators { // parse check value checkItem := validator.Check @@ -89,7 +89,7 @@ func (v *ResponseObject) Validate(validators []TValidator) error { assertMethod := validator.Assert assertFunc := assertFunctionsMap[assertMethod] // parse expected value - expectValue := validator.Expect + expectValue := parseData(validator.Expect, variablesMapping) // do assertion result := assertFunc(v.t, expectValue, checkValue) log.Printf("assert %s %s %v => %v", checkItem, assertMethod, expectValue, result) diff --git a/runner.go b/runner.go index 9ac2ff56..978a9e2a 100644 --- a/runner.go +++ b/runner.go @@ -107,7 +107,7 @@ func (r *Runner) runStepRequest(step *TStep) error { // validate response respObj := NewResponseObject(r.t, resp) - err = respObj.Validate(step.Validators) + err = respObj.Validate(step.Validators, step.Variables) if err != nil { return err } From 193fcd4d090bbe1872267fdaa77af6d23d173d0f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 09:40:17 +0800 Subject: [PATCH 031/479] fix: parse variables --- parser.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/parser.go b/parser.go index 4540f777..c64d2c8a 100644 --- a/parser.go +++ b/parser.go @@ -76,8 +76,21 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ continue } + currentPosition := matchStartPosition + var remainedString string + // find next $ location + nextStartPosition := strings.Index(raw[currentPosition+1:], "$") + if nextStartPosition == -1 { // no $ found + remainedString = raw[currentPosition:] + // break loop + matchStartPosition = len(raw) + } else { // found next $ + matchStartPosition = nextStartPosition + remainedString = raw[currentPosition:nextStartPosition] + } + // append remained string - parsedString += raw[matchStartPosition:] + parsedString += remainedString } return parsedString From 5f1c79fa4ae09e1b32f4cf062bb863683a5b40be Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 11:07:40 +0800 Subject: [PATCH 032/479] fix: parse variables --- parser.go | 40 ++++++++++++++++++---------------------- parser_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/parser.go b/parser.go index c64d2c8a..73491576 100644 --- a/parser.go +++ b/parser.go @@ -47,13 +47,24 @@ var ( // parseString parse string with variables func parseString(raw string, variablesMapping map[string]interface{}) interface{} { - matchStartPosition := strings.Index(raw, "$") - if matchStartPosition == -1 { // no $ found - return raw - } - parsedString := raw[0:matchStartPosition] + matchStartPosition := 0 + parsedString := "" + remainedString := raw for matchStartPosition < len(raw) { + // locate $ char position + startPosition := strings.Index(remainedString, "$") + if startPosition == -1 { // no $ found + // append remained string + parsedString += remainedString + break + } + + // found $, check if variable or function + matchStartPosition += startPosition + parsedString += remainedString[0:startPosition] + remainedString = remainedString[startPosition:] + // search variable like ${var} or $var varMatched := regexCompileVariable.FindStringSubmatch(raw[matchStartPosition:]) if len(varMatched) == 3 { @@ -70,27 +81,12 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ return varValue } - parsedString += fmt.Sprintf("%v", varValue) matchStartPosition += len(varMatched[0]) + parsedString += fmt.Sprintf("%v", varValue) + remainedString = raw[matchStartPosition:] log.Printf("[parseString] parsedString: %v, matchStartPosition: %v", parsedString, matchStartPosition) continue } - - currentPosition := matchStartPosition - var remainedString string - // find next $ location - nextStartPosition := strings.Index(raw[currentPosition+1:], "$") - if nextStartPosition == -1 { // no $ found - remainedString = raw[currentPosition:] - // break loop - matchStartPosition = len(raw) - } else { // found next $ - matchStartPosition = nextStartPosition - remainedString = raw[currentPosition:nextStartPosition] - } - - // append remained string - parsedString += remainedString } return parsedString diff --git a/parser_test.go b/parser_test.go index c6ff2576..bf239b3b 100644 --- a/parser_test.go +++ b/parser_test.go @@ -55,4 +55,31 @@ func TestParseDataStringWithVariables(t *testing.T) { if !assert.Equal(t, "abc#XYZ", parseData("${var_1}#XYZ", variablesMapping)) { t.Fail() } + if !assert.Equal(t, "/abc/def/var3", parseData("/$var_1/$var_2/var3", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, 123, parseData("$var_3", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, map[string]interface{}{"a": 1}, parseData("$var_4", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, true, parseData("$var_5", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, nil, parseData("$var_6", variablesMapping)) { + t.Fail() + } + // TODO: fix compatibility with python version, "abc{'a': 1}" + if !assert.Equal(t, "abcmap[a:1]", parseData("abc$var_4", variablesMapping)) { + t.Fail() + } + // TODO: fix compatibility with python version, "abcTrue" + if !assert.Equal(t, "abctrue", parseData("abc$var_5", variablesMapping)) { + t.Fail() + } + // TODO: fix compatibility with python version, raise exception + if !assert.Equal(t, "/api/", parseData("/api/$SECRET_KEY", variablesMapping)) { + t.Fail() + } } From 8431d2d96ff0adcfaa3fc2d219d5ca0bbc5c254c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 12:00:11 +0800 Subject: [PATCH 033/479] fix: variable name should start with a letter or underscore --- parser.go | 6 ++- parser_test.go | 99 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 90 insertions(+), 15 deletions(-) diff --git a/parser.go b/parser.go index 73491576..d8180596 100644 --- a/parser.go +++ b/parser.go @@ -42,7 +42,8 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa } var ( - regexCompileVariable = regexp.MustCompile(`\$\{(\w+)\}|\$(\w+)`) // parse ${var} or $var + regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore + regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var ) // parseString parse string with variables @@ -87,6 +88,9 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ log.Printf("[parseString] parsedString: %v, matchStartPosition: %v", parsedString, matchStartPosition) continue } + + parsedString += remainedString + break } return parsedString diff --git a/parser_test.go b/parser_test.go index bf239b3b..1fe47010 100644 --- a/parser_test.go +++ b/parser_test.go @@ -38,47 +38,118 @@ func TestParseDataStringWithVariables(t *testing.T) { "var_4": map[string]interface{}{"a": 1}, "var_5": true, "var_6": nil, + "v": 4.5, // variable name with one character + "_v": 6.9, // variable name starts with underscore } + // no variable + if !assert.Equal(t, "var_1", parseData("var_1", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "$", parseData("$", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "var_1$", parseData("var_1$", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "var_1$123", parseData("var_1$123", variablesMapping)) { // variable should starts with a letter + t.Fail() + } + + // single variable if !assert.Equal(t, "abc", parseData("$var_1", variablesMapping)) { t.Fail() } if !assert.Equal(t, "abc", parseData("${var_1}", variablesMapping)) { t.Fail() } - if !assert.Equal(t, "var_1", parseData("var_1", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "abc#XYZ", parseData("$var_1#XYZ", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "abc#XYZ", parseData("${var_1}#XYZ", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "/abc/def/var3", parseData("/$var_1/$var_2/var3", variablesMapping)) { - t.Fail() - } if !assert.Equal(t, 123, parseData("$var_3", variablesMapping)) { t.Fail() } if !assert.Equal(t, map[string]interface{}{"a": 1}, parseData("$var_4", variablesMapping)) { t.Fail() } + if !assert.Equal(t, map[string]interface{}{"a": 1}, parseData("${var_4}", variablesMapping)) { + t.Fail() + } if !assert.Equal(t, true, parseData("$var_5", variablesMapping)) { t.Fail() } if !assert.Equal(t, nil, parseData("$var_6", variablesMapping)) { t.Fail() } + if !assert.Equal(t, 4.5, parseData("$v", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "var_16.9", parseData("var_1$_v", variablesMapping)) { + t.Fail() + } + + // single variable with prefix or suffix + if !assert.Equal(t, "abc#XYZ", parseData("$var_1#XYZ", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "abc#XYZ", parseData("${var_1}#XYZ", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc", parseData("ABC$var_1", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc", parseData("ABC${var_1}", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc/", parseData("ABC$var_1/", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABC4.5/", parseData("ABC${v}/", variablesMapping)) { + t.Fail() + } + + // multiple variables + if !assert.Equal(t, "/abc/def/var3", parseData("/$var_1/$var_2/var3", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "/abc/def/var3", parseData("/${var_1}/$var_2/var3", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc123", parseData("ABC$var_1$var_3", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc123", parseData("ABC$var_1${var_3}", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc/123", parseData("ABC$var_1/$var_3", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc/123", parseData("ABC${var_1}/${var_3}", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc/123abc/456", parseData("ABC$var_1/123$var_1/456", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc/123abc/456", parseData("ABC$var_1/123${var_1}/456", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc/def/abc", parseData("ABC$var_1/$var_2/$var_1", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "ABCabc/def/abc", parseData("ABC$var_1/$var_2/${var_1}", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "func1(abc, 123)", parseData("func1($var_1, $var_3)", variablesMapping)) { + t.Fail() + } + if !assert.Equal(t, "func1(abc, 123)", parseData("func1($var_1, ${var_3})", variablesMapping)) { + t.Fail() + } + // TODO: fix compatibility with python version, "abc{'a': 1}" if !assert.Equal(t, "abcmap[a:1]", parseData("abc$var_4", variablesMapping)) { t.Fail() } - // TODO: fix compatibility with python version, "abcTrue" if !assert.Equal(t, "abctrue", parseData("abc$var_5", variablesMapping)) { t.Fail() } - // TODO: fix compatibility with python version, raise exception if !assert.Equal(t, "/api/", parseData("/api/$SECRET_KEY", variablesMapping)) { t.Fail() } From 559f579fb66b3f572a2479c50d10aa76b3e3e4e6 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 12:03:12 +0800 Subject: [PATCH 034/479] change: make regexVariable as const --- parser.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/parser.go b/parser.go index d8180596..f7b75efc 100644 --- a/parser.go +++ b/parser.go @@ -41,8 +41,11 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa } } +const ( + regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore +) + var ( - regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var ) From 985059fa799c2d0b66abf6e73b9c595b26482fa1 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 12:49:54 +0800 Subject: [PATCH 035/479] change: use data driven test --- parser.go | 2 +- parser_test.go | 183 ++++++++++++++++++++----------------------------- 2 files changed, 76 insertions(+), 109 deletions(-) diff --git a/parser.go b/parser.go index f7b75efc..e3800de7 100644 --- a/parser.go +++ b/parser.go @@ -70,7 +70,7 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ remainedString = remainedString[startPosition:] // search variable like ${var} or $var - varMatched := regexCompileVariable.FindStringSubmatch(raw[matchStartPosition:]) + varMatched := regexCompileVariable.FindStringSubmatch(remainedString) if len(varMatched) == 3 { var varName string if varMatched[1] != "" { diff --git a/parser_test.go b/parser_test.go index 1fe47010..1de295aa 100644 --- a/parser_test.go +++ b/parser_test.go @@ -42,115 +42,82 @@ func TestParseDataStringWithVariables(t *testing.T) { "_v": 6.9, // variable name starts with underscore } - // no variable - if !assert.Equal(t, "var_1", parseData("var_1", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "$", parseData("$", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "var_1$", parseData("var_1$", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "var_1$123", parseData("var_1$123", variablesMapping)) { // variable should starts with a letter - t.Fail() + testData := []struct { + expr string + expect interface{} + }{ + // no variable + {"var_1", "var_1"}, + {"$", "$"}, + {"var_1$", "var_1$"}, + {"var_1$123", "var_1$123"}, // variable should starts with a letter + // single variable + {"$var_1", "abc"}, + {"${var_1}", "abc"}, + {"$var_3", 123}, + {"$var_4", map[string]interface{}{"a": 1}}, + {"${var_4}", map[string]interface{}{"a": 1}}, + {"$var_5", true}, + {"$var_6", nil}, + {"$v", 4.5}, + {"var_1$_v", "var_16.9"}, + // single variable with prefix or suffix + {"$var_1#XYZ", "abc#XYZ"}, + {"${var_1}#XYZ", "abc#XYZ"}, + {"ABC$var_1", "ABCabc"}, + {"ABC${var_1}", "ABCabc"}, + {"ABC$var_1/", "ABCabc/"}, + {"ABC${v}/", "ABC4.5/"}, + // multiple variables + {"/$var_1/$var_2/var3", "/abc/def/var3"}, + {"/${var_1}/$var_2/var3", "/abc/def/var3"}, + {"ABC$var_1$var_3", "ABCabc123"}, + {"ABC$var_1${var_3}", "ABCabc123"}, + {"ABC$var_1/$var_3", "ABCabc/123"}, + {"ABC${var_1}/${var_3}", "ABCabc/123"}, + {"ABC$var_1/123$var_1/456", "ABCabc/123abc/456"}, + {"ABC$var_1/123${var_1}/456", "ABCabc/123abc/456"}, + {"ABC$var_1/$var_2/$var_1", "ABCabc/def/abc"}, + {"ABC$var_1/$var_2/${var_1}", "ABCabc/def/abc"}, + {"func1($var_1, $var_3)", "func1(abc, 123)"}, + {"func1($var_1, ${var_3})", "func1(abc, 123)"}, + // TODO: fix compatibility with python version + {"abc$var_4", "abcmap[a:1]"}, // "abc{'a': 1}" + {"abc$var_5", "abctrue"}, // "abcTrue" + {"/api/$SECRET_KEY", "/api/"}, // raise error } - // single variable - if !assert.Equal(t, "abc", parseData("$var_1", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "abc", parseData("${var_1}", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, 123, parseData("$var_3", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, map[string]interface{}{"a": 1}, parseData("$var_4", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, map[string]interface{}{"a": 1}, parseData("${var_4}", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, true, parseData("$var_5", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, nil, parseData("$var_6", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, 4.5, parseData("$v", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "var_16.9", parseData("var_1$_v", variablesMapping)) { - t.Fail() - } - - // single variable with prefix or suffix - if !assert.Equal(t, "abc#XYZ", parseData("$var_1#XYZ", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "abc#XYZ", parseData("${var_1}#XYZ", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc", parseData("ABC$var_1", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc", parseData("ABC${var_1}", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc/", parseData("ABC$var_1/", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABC4.5/", parseData("ABC${v}/", variablesMapping)) { - t.Fail() - } - - // multiple variables - if !assert.Equal(t, "/abc/def/var3", parseData("/$var_1/$var_2/var3", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "/abc/def/var3", parseData("/${var_1}/$var_2/var3", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc123", parseData("ABC$var_1$var_3", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc123", parseData("ABC$var_1${var_3}", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc/123", parseData("ABC$var_1/$var_3", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc/123", parseData("ABC${var_1}/${var_3}", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc/123abc/456", parseData("ABC$var_1/123$var_1/456", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc/123abc/456", parseData("ABC$var_1/123${var_1}/456", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc/def/abc", parseData("ABC$var_1/$var_2/$var_1", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "ABCabc/def/abc", parseData("ABC$var_1/$var_2/${var_1}", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "func1(abc, 123)", parseData("func1($var_1, $var_3)", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "func1(abc, 123)", parseData("func1($var_1, ${var_3})", variablesMapping)) { - t.Fail() - } - - // TODO: fix compatibility with python version, "abc{'a': 1}" - if !assert.Equal(t, "abcmap[a:1]", parseData("abc$var_4", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "abctrue", parseData("abc$var_5", variablesMapping)) { - t.Fail() - } - if !assert.Equal(t, "/api/", parseData("/api/$SECRET_KEY", variablesMapping)) { - t.Fail() + for _, data := range testData { + if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + t.Fail() + } + } +} +func TestParseDataStringWithVariablesAbnormal(t *testing.T) { + variablesMapping := map[string]interface{}{ + "var_1": "abc", + "var_2": "def", + "var_3": 123, + "var_4": map[string]interface{}{"a": 1}, + "var_5": true, + "var_6": nil, + "v": 4.5, // variable name with one character + "_v": 6.9, // variable name starts with underscore + } + + testData := []struct { + expr string + expect interface{} + }{ + {"ABC$var_1{", "ABCabc{"}, + {"{ABC$var_1{}a}", "{ABCabc{}a}"}, + {"AB{C$var_1{}a}", "AB{Cabc{}a}"}, + {"ABC$var_1}", "ABCabc}"}, + } + + for _, data := range testData { + if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + t.Fail() + } } } From aa6a96538f7e02e0e0edbafd7b8b3d9fc144504e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 13:10:09 +0800 Subject: [PATCH 036/479] feat: use 28586 to escape $ notation --- parser.go | 8 ++++++++ parser_test.go | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/parser.go b/parser.go index e3800de7..01c9d04a 100644 --- a/parser.go +++ b/parser.go @@ -69,6 +69,14 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ parsedString += remainedString[0:startPosition] remainedString = remainedString[startPosition:] + // search $$, use $$ to escape $ notation + if strings.HasPrefix(remainedString, "$$") { // found $$ + matchStartPosition += 2 + parsedString += "$" + remainedString = remainedString[2:] + continue + } + // search variable like ${var} or $var varMatched := regexCompileVariable.FindStringSubmatch(remainedString) if len(varMatched) == 3 { diff --git a/parser_test.go b/parser_test.go index 1de295aa..bbf29c48 100644 --- a/parser_test.go +++ b/parser_test.go @@ -109,10 +109,18 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) { expr string expect interface{} }{ - {"ABC$var_1{", "ABCabc{"}, - {"{ABC$var_1{}a}", "{ABCabc{}a}"}, - {"AB{C$var_1{}a}", "AB{Cabc{}a}"}, - {"ABC$var_1}", "ABCabc}"}, + {"ABC$var_1{", "ABCabc{"}, // { + {"ABC$var_1}", "ABCabc}"}, // } + {"{ABC$var_1{}a}", "{ABCabc{}a}"}, // {xx} + {"AB{C$var_1{}a}", "AB{Cabc{}a}"}, // {xx{}x} + {"ABC$$var_1{", "ABC$var_1{"}, // $$ + {"ABC$$$var_1{", "ABC$abc{"}, // $$$ + {"ABC$$$$var_1{", "ABC$$var_1{"}, // $$$$ + {"ABC$var_1${", "ABCabc${"}, // ${ + {"ABC$var_1${a", "ABCabc${a"}, // ${ + {"ABC$var_1$}a", "ABCabc$}a"}, // $} + {"ABC$var_1}{a", "ABCabc}{a"}, // }{ + {"ABC$var_1{}a", "ABCabc{}a"}, // {} } for _, data := range testData { From 6e286b7bfb7dcd59262ca17d49c8df0017b0f616 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 13:14:35 +0800 Subject: [PATCH 037/479] change: update test --- parser_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parser_test.go b/parser_test.go index bbf29c48..5ecb4e24 100644 --- a/parser_test.go +++ b/parser_test.go @@ -48,9 +48,6 @@ func TestParseDataStringWithVariables(t *testing.T) { }{ // no variable {"var_1", "var_1"}, - {"$", "$"}, - {"var_1$", "var_1$"}, - {"var_1$123", "var_1$123"}, // variable should starts with a letter // single variable {"$var_1", "abc"}, {"${var_1}", "abc"}, @@ -109,6 +106,9 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) { expr string expect interface{} }{ + {"$", "$"}, + {"var_1$", "var_1$"}, + {"var_1$123", "var_1$123"}, // variable should starts with a letter {"ABC$var_1{", "ABCabc{"}, // { {"ABC$var_1}", "ABCabc}"}, // } {"{ABC$var_1{}a}", "{ABCabc{}a}"}, // {xx} From c2e76eaeca153425da2ac335a3bd10a6dfddd137 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 29 Sep 2021 13:45:49 +0800 Subject: [PATCH 038/479] feat: parse map data --- examples/postman_echo/variables_test.go | 35 +++++++++++++++++++++++++ parser.go | 13 ++++++--- parser_test.go | 24 +++++++++++++++++ runner.go | 9 ++++--- 4 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 examples/postman_echo/variables_test.go diff --git a/examples/postman_echo/variables_test.go b/examples/postman_echo/variables_test.go new file mode 100644 index 00000000..3fc6b3f7 --- /dev/null +++ b/examples/postman_echo/variables_test.go @@ -0,0 +1,35 @@ +package postman_echo + +import ( + "testing" + + "github.com/httprunner/httpboomer" +) + +func TestCaseVariables(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with variables", + BaseURL: "https://postman-echo.com", + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{"var1": "bar1", "expectedStatusCode": 200}). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + Validate(). + AssertEqual("status_code", "$expectedStatusCode", "check status code"). + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). + AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). + AssertEqual("body.args.foo1", "$var1", "check args foo1"). + AssertEqual("body.args.foo2", "bar2", "check args foo2"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/parser.go b/parser.go index 01c9d04a..d32026b4 100644 --- a/parser.go +++ b/parser.go @@ -32,11 +32,18 @@ func buildURL(baseURL, stepURL string) string { } func parseData(raw interface{}, variablesMapping map[string]interface{}) interface{} { - switch v := raw.(type) { + switch value := raw.(type) { case string: - v = strings.TrimSpace(v) - return parseString(v, variablesMapping) + value = strings.TrimSpace(value) + return parseString(value, variablesMapping) + case map[string]interface{}: + parsedMap := make(map[string]interface{}) + for k, v := range value { + parsedMap[k] = parseData(v, variablesMapping) + } + return parsedMap default: + // other types, e.g. nil, int, float, bool return raw } } diff --git a/parser_test.go b/parser_test.go index 5ecb4e24..c3a3423e 100644 --- a/parser_test.go +++ b/parser_test.go @@ -129,3 +129,27 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) { } } } + +func TestParseDataMapWithVariables(t *testing.T) { + variablesMapping := map[string]interface{}{ + "var1": "foo1", + "val1": 200, + "var2": 123, + } + + testData := []struct { + expr map[string]interface{} + expect interface{} + }{ + {map[string]interface{}{"key": "$var1"}, map[string]interface{}{"key": "foo1"}}, + {map[string]interface{}{"foo1": "$val1", "foo2": "bar2"}, map[string]interface{}{"foo1": 200, "foo2": "bar2"}}, + // {map[string]interface{}{"$var1": "$val1"}, map[string]interface{}{"foo1": 200}}, + // {map[string]interface{}{"$var2": "$val1"}, map[string]interface{}{123: 200}}, + } + + for _, data := range testData { + if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + t.Fail() + } + } +} diff --git a/runner.go b/runner.go index 978a9e2a..a2747e90 100644 --- a/runner.go +++ b/runner.go @@ -78,13 +78,16 @@ func (r *Runner) runStepRequest(step *TStep) error { // prepare request args var v []interface{} if len(step.Request.Headers) > 0 { - v = append(v, req.Header(step.Request.Headers)) + headers := parseData(step.Request.Headers, step.Variables) + v = append(v, req.Header(headers.(map[string]string))) } if len(step.Request.Params) > 0 { - v = append(v, req.Param(step.Request.Params)) + params := parseData(step.Request.Params, step.Variables) + v = append(v, req.Param(params.(map[string]interface{}))) } if step.Request.Data != nil { - v = append(v, step.Request.Data) + data := parseData(step.Request.Data, step.Variables) + v = append(v, data) } if step.Request.JSON != nil { v = append(v, req.BodyJSON(step.Request.JSON)) From b7487020c015627a7e94ab88edf3564f322521a2 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 10:34:40 +0800 Subject: [PATCH 039/479] feat: parseHeaders --- examples/postman_echo/variables_test.go | 13 ++++++++---- parser.go | 27 ++++++++++++++++++++----- runner.go | 4 ++-- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/examples/postman_echo/variables_test.go b/examples/postman_echo/variables_test.go index 3fc6b3f7..e184034b 100644 --- a/examples/postman_echo/variables_test.go +++ b/examples/postman_echo/variables_test.go @@ -15,16 +15,21 @@ func TestCaseVariables(t *testing.T) { }, TestSteps: []httpboomer.IStep{ httpboomer.Step("get with params"). - WithVariables(map[string]interface{}{"var1": "bar1", "expectedStatusCode": 200}). + WithVariables(map[string]interface{}{ + "var1": "bar1", + "agent": "HttpBoomer", + "expectedStatusCode": 200, + }). GET("/get"). WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). - WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + WithHeaders(map[string]string{"User-Agent": "$agent"}). Validate(). AssertEqual("status_code", "$expectedStatusCode", "check status code"). AssertEqual("headers.Connection", "keep-alive", "check header Connection"). AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). - AssertEqual("body.args.foo1", "$var1", "check args foo1"). - AssertEqual("body.args.foo2", "bar2", "check args foo2"), + AssertEqual("body.args.foo1", "bar1", "check args foo1"). + AssertEqual("body.args.foo2", "bar2", "check args foo2"). + AssertEqual("body.headers.\"user-agent\"", "HttpBoomer", "check header user agent"), }, } diff --git a/parser.go b/parser.go index d32026b4..d19b9e08 100644 --- a/parser.go +++ b/parser.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "net/url" + "reflect" "regexp" "strings" ) @@ -31,15 +32,31 @@ func buildURL(baseURL, stepURL string) string { return uStep.String() } +func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) map[string]string { + parsedHeaders := make(map[string]string) + for k, v := range rawHeaders { + parsedValue := parseString(v, variablesMapping) + if value, ok := parsedValue.(string); ok { + parsedHeaders[k] = value + } else { + parsedHeaders[k] = fmt.Sprintf("%v", parsedValue) + } + } + return parsedHeaders +} + func parseData(raw interface{}, variablesMapping map[string]interface{}) interface{} { - switch value := raw.(type) { - case string: + rawValue := reflect.ValueOf(raw) + switch rawValue.Kind() { + case reflect.String: + value := rawValue.String() value = strings.TrimSpace(value) return parseString(value, variablesMapping) - case map[string]interface{}: + case reflect.Map: // convert any map to map[string]interface{} parsedMap := make(map[string]interface{}) - for k, v := range value { - parsedMap[k] = parseData(v, variablesMapping) + for _, k := range rawValue.MapKeys() { + v := rawValue.MapIndex(k) + parsedMap[k.String()] = parseData(v.Interface(), variablesMapping) } return parsedMap default: diff --git a/runner.go b/runner.go index a2747e90..971ef76b 100644 --- a/runner.go +++ b/runner.go @@ -78,8 +78,8 @@ func (r *Runner) runStepRequest(step *TStep) error { // prepare request args var v []interface{} if len(step.Request.Headers) > 0 { - headers := parseData(step.Request.Headers, step.Variables) - v = append(v, req.Header(headers.(map[string]string))) + headers := parseHeaders(step.Request.Headers, step.Variables) + v = append(v, req.Header(headers)) } if len(step.Request.Params) > 0 { params := parseData(step.Request.Params, step.Variables) From bf2440f6e54358cf66949bd96b3bb4651a3bf51d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 10:48:27 +0800 Subject: [PATCH 040/479] fix: parse map key --- parser.go | 13 ++++++++++++- parser_test.go | 8 +++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/parser.go b/parser.go index d19b9e08..7eb381e2 100644 --- a/parser.go +++ b/parser.go @@ -39,6 +39,8 @@ func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]inte if value, ok := parsedValue.(string); ok { parsedHeaders[k] = value } else { + // parsed value is not string, e.g. int, float, etc. + // convert to string parsedHeaders[k] = fmt.Sprintf("%v", parsedValue) } } @@ -55,8 +57,17 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa case reflect.Map: // convert any map to map[string]interface{} parsedMap := make(map[string]interface{}) for _, k := range rawValue.MapKeys() { + parsedKey := parseString(k.String(), variablesMapping) v := rawValue.MapIndex(k) - parsedMap[k.String()] = parseData(v.Interface(), variablesMapping) + parsedValue := parseData(v.Interface(), variablesMapping) + + if key, ok := parsedKey.(string); ok { + parsedMap[key] = parsedValue + } else { + // parsed key is not string, e.g. int, float, etc. + // convert to string + parsedMap[fmt.Sprintf("%v", parsedKey)] = parsedValue + } } return parsedMap default: diff --git a/parser_test.go b/parser_test.go index c3a3423e..b6e1110c 100644 --- a/parser_test.go +++ b/parser_test.go @@ -134,7 +134,7 @@ func TestParseDataMapWithVariables(t *testing.T) { variablesMapping := map[string]interface{}{ "var1": "foo1", "val1": 200, - "var2": 123, + "var2": 123, // key is int } testData := []struct { @@ -143,8 +143,10 @@ func TestParseDataMapWithVariables(t *testing.T) { }{ {map[string]interface{}{"key": "$var1"}, map[string]interface{}{"key": "foo1"}}, {map[string]interface{}{"foo1": "$val1", "foo2": "bar2"}, map[string]interface{}{"foo1": 200, "foo2": "bar2"}}, - // {map[string]interface{}{"$var1": "$val1"}, map[string]interface{}{"foo1": 200}}, - // {map[string]interface{}{"$var2": "$val1"}, map[string]interface{}{123: 200}}, + // parse map key + {map[string]interface{}{"$var1": "$val1"}, map[string]interface{}{"foo1": 200}}, + // map key is int + {map[string]interface{}{"$var2": "$val1"}, map[string]interface{}{"123": 200}}, } for _, data := range testData { From e0b884a1fd2a5b7c56db8884547a1da6900da645 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 11:03:31 +0800 Subject: [PATCH 041/479] fix: parse headers --- parser.go | 10 ++++------ parser_test.go | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/parser.go b/parser.go index 7eb381e2..49698a61 100644 --- a/parser.go +++ b/parser.go @@ -34,14 +34,12 @@ func buildURL(baseURL, stepURL string) string { func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) map[string]string { parsedHeaders := make(map[string]string) - for k, v := range rawHeaders { - parsedValue := parseString(v, variablesMapping) - if value, ok := parsedValue.(string); ok { + headers := parseData(rawHeaders, variablesMapping).(map[string]interface{}) + for k, v := range headers { + if value, ok := v.(string); ok { parsedHeaders[k] = value } else { - // parsed value is not string, e.g. int, float, etc. - // convert to string - parsedHeaders[k] = fmt.Sprintf("%v", parsedValue) + parsedHeaders[k] = fmt.Sprintf("%v", v) } } return parsedHeaders diff --git a/parser_test.go b/parser_test.go index b6e1110c..7f6618b0 100644 --- a/parser_test.go +++ b/parser_test.go @@ -143,9 +143,9 @@ func TestParseDataMapWithVariables(t *testing.T) { }{ {map[string]interface{}{"key": "$var1"}, map[string]interface{}{"key": "foo1"}}, {map[string]interface{}{"foo1": "$val1", "foo2": "bar2"}, map[string]interface{}{"foo1": 200, "foo2": "bar2"}}, - // parse map key + // parse map key, key is string {map[string]interface{}{"$var1": "$val1"}, map[string]interface{}{"foo1": 200}}, - // map key is int + // parse map key, key is int {map[string]interface{}{"$var2": "$val1"}, map[string]interface{}{"123": 200}}, } @@ -155,3 +155,32 @@ func TestParseDataMapWithVariables(t *testing.T) { } } } + +func TestParseHeaders(t *testing.T) { + variablesMapping := map[string]interface{}{ + "var1": "foo1", + "val1": 200, + "var2": 123, // key is int + "val2": nil, // value is nil + } + + testData := []struct { + rawHeaders map[string]string + expectHeaders map[string]string + }{ + {map[string]string{"key": "$var1"}, map[string]string{"key": "foo1"}}, + {map[string]string{"foo1": "$val1", "foo2": "bar2"}, map[string]string{"foo1": "200", "foo2": "bar2"}}, + // parse map key, key is string + {map[string]string{"$var1": "$val1"}, map[string]string{"foo1": "200"}}, + // parse map key, key is int + {map[string]string{"$var2": "$val1"}, map[string]string{"123": "200"}}, + // parse map key & value, key is int, value is nil + {map[string]string{"$var2": "$val2"}, map[string]string{"123": ""}}, + } + + for _, data := range testData { + if !assert.Equal(t, data.expectHeaders, parseHeaders(data.rawHeaders, variablesMapping)) { + t.Fail() + } + } +} From d92b466897928456842cad97dc196c2b892ff086 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 11:33:55 +0800 Subject: [PATCH 042/479] feat: override config variables --- examples/postman_echo/variables_test.go | 73 ++++++++++++++++++++++++- parser.go | 25 +++++++++ parser_test.go | 19 +++++++ runner.go | 7 +++ 4 files changed, 123 insertions(+), 1 deletion(-) diff --git a/examples/postman_echo/variables_test.go b/examples/postman_echo/variables_test.go index e184034b..0f94cd03 100644 --- a/examples/postman_echo/variables_test.go +++ b/examples/postman_echo/variables_test.go @@ -6,7 +6,40 @@ import ( "github.com/httprunner/httpboomer" ) -func TestCaseVariables(t *testing.T) { +func TestCaseConfigVariables(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with variables", + BaseURL: "https://postman-echo.com", + Variables: map[string]interface{}{ + "var1": "bar1", + "agent": "HttpBoomer", + "expectedStatusCode": 200, + }, + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "$agent"}). + Validate(). + AssertEqual("status_code", "$expectedStatusCode", "check status code"). + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). + AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). + AssertEqual("body.args.foo1", "bar1", "check args foo1"). + AssertEqual("body.args.foo2", "bar2", "check args foo2"). + AssertEqual("body.headers.\"user-agent\"", "HttpBoomer", "check header user agent"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} + +func TestCaseStepVariables(t *testing.T) { testcase := &httpboomer.TestCase{ Config: httpboomer.TConfig{ Name: "run request with variables", @@ -38,3 +71,41 @@ func TestCaseVariables(t *testing.T) { t.Fatalf("run testcase error: %v", err) } } + +func TestCaseOverrideConfigVariables(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with variables", + BaseURL: "https://postman-echo.com", + Variables: map[string]interface{}{ + "var1": "bar0", + "agent": "HttpBoomer", + "expectedStatusCode": 200, + }, + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ + "var1": "bar1", // override config variable + "agent": "$agent", // reference config variable + // expectedStatusCode, inherit config variable + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "$agent"}). + Validate(). + AssertEqual("status_code", "$expectedStatusCode", "check status code"). + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). + AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). + AssertEqual("body.args.foo1", "bar1", "check args foo1"). + AssertEqual("body.args.foo2", "bar2", "check args foo2"). + AssertEqual("body.headers.\"user-agent\"", "HttpBoomer", "check header user agent"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/parser.go b/parser.go index 49698a61..3dfb39ba 100644 --- a/parser.go +++ b/parser.go @@ -139,3 +139,28 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ return parsedString } + +// merge two variables mapping, the first variables have higher priority +func mergeVariables(variables, overriddenVariables map[string]interface{}) map[string]interface{} { + if overriddenVariables == nil { + return variables + } + if variables == nil { + return overriddenVariables + } + + mergedVariables := make(map[string]interface{}) + for k, v := range overriddenVariables { + mergedVariables[k] = v + } + for k, v := range variables { + if fmt.Sprintf("${%s}", k) == v || fmt.Sprintf("$%s", k) == v { + // e.g. {"base_url": "$base_url"} + // or {"base_url": "${base_url}"} + continue + } + + mergedVariables[k] = v + } + return mergedVariables +} diff --git a/parser_test.go b/parser_test.go index 7f6618b0..f32ad9f1 100644 --- a/parser_test.go +++ b/parser_test.go @@ -184,3 +184,22 @@ func TestParseHeaders(t *testing.T) { } } } + +func TestMergeVariables(t *testing.T) { + stepVariables := map[string]interface{}{ + "base_url": "$base_url", + "foo1": "bar1", + } + configVariables := map[string]interface{}{ + "base_url": "https://httpbin.org", + "foo1": "bar111", + } + mergedVariables := mergeVariables(stepVariables, configVariables) + expectVariables := map[string]interface{}{ + "base_url": "https://httpbin.org", + "foo1": "bar1", + } + if !assert.Equal(t, expectVariables, mergedVariables) { + t.Fail() + } +} diff --git a/runner.go b/runner.go index 971ef76b..b64230f6 100644 --- a/runner.go +++ b/runner.go @@ -48,9 +48,16 @@ func (r *Runner) Run(testcases ...*TestCase) error { func (r *Runner) runCase(testcase *TestCase) error { config := &testcase.Config log.Printf("Start to run testcase: %v", config.Name) + for _, step := range testcase.TestSteps { + // override variables + // step variables > extracted variables from previous steps + // step variables > testcase config variables + step.ToStruct().Variables = mergeVariables(step.ToStruct().Variables, config.Variables) + r.runStep(step, config) } + return nil } From 2a793cfc99ea3a246b4a73cd162fe67e0771e07c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 13:19:54 +0800 Subject: [PATCH 043/479] feat: extract variables --- boomer.go | 2 +- boomer_test.go | 26 +++++++++++----------- models.go | 6 ++++++ response.go | 14 ++++++++++++ runner.go | 58 ++++++++++++++++++++++++++++++++++++++------------ step_test.go | 4 ++-- 6 files changed, 80 insertions(+), 30 deletions(-) diff --git a/boomer.go b/boomer.go index 73c2a2d1..c9863441 100644 --- a/boomer.go +++ b/boomer.go @@ -43,7 +43,7 @@ func (b *Boomer) convertBoomerTask(testcase *TestCase) *boomer.Task { config := &testcase.Config for _, step := range testcase.TestSteps { start := time.Now() - err := runner.runStep(step, config) + _, err := runner.runStep(step, config) elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) if err == nil { diff --git a/boomer_test.go b/boomer_test.go index 20aa39f4..2c6645c2 100644 --- a/boomer_test.go +++ b/boomer_test.go @@ -5,18 +5,18 @@ import ( ) func TestHttpBoomer(t *testing.T) { - testcase1 := &TestCase{ - Config: TConfig{ - Name: "TestCase1", - Weight: 2, - }, - } - testcase2 := &TestCase{ - Config: TConfig{ - Name: "TestCase2", - Weight: 3, - }, - } + // testcase1 := &TestCase{ + // Config: TConfig{ + // Name: "TestCase1", + // Weight: 2, + // }, + // } + // testcase2 := &TestCase{ + // Config: TConfig{ + // Name: "TestCase2", + // Weight: 3, + // }, + // } - Run(testcase1, testcase2) + // Run(testcase1, testcase2) } diff --git a/models.go b/models.go index 3c48d2e3..9cc46949 100644 --- a/models.go +++ b/models.go @@ -67,3 +67,9 @@ type TestCase struct { } type TestCaseSummary struct{} + +type StepData struct { + Name string // step name + Success bool // step execution result + ExportVars map[string]interface{} // extract variables +} diff --git a/response.go b/response.go index 25a70704..fd36ded2 100644 --- a/response.go +++ b/response.go @@ -80,6 +80,20 @@ type ResponseObject struct { validationResults map[string]interface{} } +func (v *ResponseObject) Extract(extractors map[string]string) map[string]interface{} { + if extractors == nil { + return nil + } + + extractMapping := make(map[string]interface{}) + for key, value := range extractors { + extractMapping[key] = v.searchJmespath(value) + } + + log.Printf("[Extract] extractMapping: %v", extractMapping) + return extractMapping +} + func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) error { for _, validator := range validators { // parse check value diff --git a/runner.go b/runner.go index b64230f6..4677e0cd 100644 --- a/runner.go +++ b/runner.go @@ -49,39 +49,56 @@ func (r *Runner) runCase(testcase *TestCase) error { config := &testcase.Config log.Printf("Start to run testcase: %v", config.Name) + extractedVariables := make(map[string]interface{}) + for _, step := range testcase.TestSteps { // override variables // step variables > extracted variables from previous steps + step.ToStruct().Variables = mergeVariables(step.ToStruct().Variables, extractedVariables) // step variables > testcase config variables step.ToStruct().Variables = mergeVariables(step.ToStruct().Variables, config.Variables) - r.runStep(step, config) + stepData, err := r.runStep(step, config) + if err != nil { + return err + } + // update extracted variables + for k, v := range stepData.ExportVars { + extractedVariables[k] = v + } } return nil } -func (r *Runner) runStep(step IStep, config *TConfig) error { +func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err error) { log.Printf("run step begin: %v >>>>>>", step.Name()) if tc, ok := step.(*testcaseWithOptionalArgs); ok { // run referenced testcase log.Printf("run referenced testcase: %v", tc.step.Name) // TODO: override testcase config - if err := r.runStepTestCase(tc.step); err != nil { - return err + stepData, err = r.runStepTestCase(tc.step) + if err != nil { + return } } else { // run request tStep := parseStep(step, config) - if err := r.runStepRequest(tStep); err != nil { - return err + stepData, err = r.runStepRequest(tStep) + if err != nil { + return } } log.Printf("run step end: %v <<<<<<\n", step.Name()) - return nil + return } -func (r *Runner) runStepRequest(step *TStep) error { +func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { + stepData = &StepData{ + Name: step.Name, + Success: false, + } + // prepare request args var v []interface{} if len(step.Request.Headers) > 0 { @@ -111,23 +128,36 @@ func (r *Runner) runStepRequest(step *TStep) error { req.Debug = r.debug resp, err := r.Client.Do(string(step.Request.Method), step.Request.URL, v...) if err != nil { - return err + return } defer resp.Response().Body.Close() - // validate response + // new response object respObj := NewResponseObject(r.t, resp) + + // extract variables from response + extractors := step.Extract + extractMapping := respObj.Extract(extractors) + stepData.ExportVars = extractMapping + + // validate response err = respObj.Validate(step.Validators, step.Variables) if err != nil { - return err + return } - return nil + stepData.Success = true + return } -func (r *Runner) runStepTestCase(step *TStep) error { +func (r *Runner) runStepTestCase(step *TStep) (stepData *StepData, err error) { + stepData = &StepData{ + Name: step.Name, + Success: false, + } testcase := step.TestCase - return r.runCase(testcase) + err = r.runCase(testcase) + return } func (r *Runner) GetSummary() *TestCaseSummary { diff --git a/step_test.go b/step_test.go index f6d00e90..530a8691 100644 --- a/step_test.go +++ b/step_test.go @@ -78,10 +78,10 @@ func TestRunRequestRun(t *testing.T) { BaseURL: "https://postman-echo.com", } runner := NewRunner().SetDebug(true).WithTestingT(t) - if err := runner.runStep(stepGET, config); err != nil { + if _, err := runner.runStep(stepGET, config); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := runner.runStep(stepPOSTData, config); err != nil { + if _, err := runner.runStep(stepPOSTData, config); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } } From a3543215dc86a599ad74985a0992bb5e9f586f3e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 13:23:59 +0800 Subject: [PATCH 044/479] change: move test file --- examples/{postman_echo => }/variables_test.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{postman_echo => }/variables_test.go (100%) diff --git a/examples/postman_echo/variables_test.go b/examples/variables_test.go similarity index 100% rename from examples/postman_echo/variables_test.go rename to examples/variables_test.go From 9304a4e839da58541b8ff051fdf9cbfde78a8e90 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 15:00:51 +0800 Subject: [PATCH 045/479] fix: extract variables --- response.go | 5 +++-- runner.go | 5 ++++- step.go | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/response.go b/response.go index fd36ded2..bcd18d10 100644 --- a/response.go +++ b/response.go @@ -87,10 +87,11 @@ func (v *ResponseObject) Extract(extractors map[string]string) map[string]interf extractMapping := make(map[string]interface{}) for key, value := range extractors { - extractMapping[key] = v.searchJmespath(value) + extractedValue := v.searchJmespath(value) + log.Printf("extract %s => %v", value, extractedValue) + extractMapping[key] = extractedValue } - log.Printf("[Extract] extractMapping: %v", extractMapping) return extractMapping } diff --git a/runner.go b/runner.go index 4677e0cd..809fb0d1 100644 --- a/runner.go +++ b/runner.go @@ -140,8 +140,11 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { extractMapping := respObj.Extract(extractors) stepData.ExportVars = extractMapping + // override step variables with extracted variables + stepVariables := mergeVariables(step.Variables, extractMapping) + // validate response - err = respObj.Validate(step.Validators, step.Variables) + err = respObj.Validate(step.Validators, stepVariables) if err != nil { return } diff --git a/step.go b/step.go index e5920b50..b4bbd021 100644 --- a/step.go +++ b/step.go @@ -170,6 +170,7 @@ func (s *requestWithOptionalArgs) Validate() *stepRequestValidation { } func (s *requestWithOptionalArgs) Extract() *stepRequestExtraction { + s.step.Extract = make(map[string]string) return &stepRequestExtraction{ step: s.step, } From 5029d1e85e52e151a63867ec13e0abe119620a9e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 15:21:46 +0800 Subject: [PATCH 046/479] fix: extract variables for reference --- examples/extract_test.go | 44 ++++++++++++++++++++++++++++++++++++++++ response.go | 14 +++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 examples/extract_test.go diff --git a/examples/extract_test.go b/examples/extract_test.go new file mode 100644 index 00000000..1fb1df8c --- /dev/null +++ b/examples/extract_test.go @@ -0,0 +1,44 @@ +package postman_echo + +import ( + "testing" + + "github.com/httprunner/httpboomer" +) + +func TestCaseExtractStep(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with variables", + BaseURL: "https://postman-echo.com", + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ + "var1": "bar1", + "agent": "HttpBoomer", + "expectedStatusCode": 200, + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "$agent"}). + Extract(). + WithJmesPath("status_code", "statusCode"). + WithJmesPath("headers.\"Content-Type\"", "contentType"). + WithJmesPath("body.args.foo1", "varFoo1"). + Validate(). + AssertEqual("$statusCode", "$expectedStatusCode", "check status code"). // assert with extracted variable + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). + AssertEqual("$contentType", "application/json; charset=utf-8", "check header Content-Type"). // assert with extracted variable + AssertEqual("$varFoo1", "bar1", "check args foo1"). // assert with extracted variable + AssertEqual("body.args.foo2", "bar2", "check args foo2"). + AssertEqual("body.headers.\"user-agent\"", "HttpBoomer", "check header user agent"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/response.go b/response.go index bcd18d10..a61abc92 100644 --- a/response.go +++ b/response.go @@ -3,6 +3,7 @@ package httpboomer import ( "encoding/json" "log" + "strings" "testing" "github.com/imroc/req" @@ -99,12 +100,21 @@ func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[ for _, validator := range validators { // parse check value checkItem := validator.Check - checkValue := v.searchJmespath(checkItem) + var checkValue interface{} + if strings.Contains(checkItem, "$") { + // reference variable + checkValue = parseData(checkItem, variablesMapping) + } else { + checkValue = v.searchJmespath(checkItem) + } + // get assert method assertMethod := validator.Assert assertFunc := assertFunctionsMap[assertMethod] + // parse expected value expectValue := parseData(validator.Expect, variablesMapping) + // do assertion result := assertFunc(v.t, expectValue, checkValue) log.Printf("assert %s %s %v => %v", checkItem, assertMethod, expectValue, result) @@ -119,7 +129,7 @@ func (v *ResponseObject) searchJmespath(expr string) interface{} { checkValue, err := jmespath.Search(expr, v.respObjMeta) if err != nil { log.Printf("[searchJmespath] jmespath.Search error: %v", err) - return nil + return expr // jmespath not found, return the expression } return checkValue } From 4910580c380cd46080313e7e76de3fcf2ef1cca6 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 15:37:06 +0800 Subject: [PATCH 047/479] change: add examples --- examples/extract_test.go | 55 ++++++++++++++++++++++++++++++++++++---- response.go | 5 ++-- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/examples/extract_test.go b/examples/extract_test.go index 1fb1df8c..6f5d5dc2 100644 --- a/examples/extract_test.go +++ b/examples/extract_test.go @@ -6,7 +6,8 @@ import ( "github.com/httprunner/httpboomer" ) -func TestCaseExtractStep(t *testing.T) { +// reference extracted variables for validation in the same step +func TestCaseExtractStepSingle(t *testing.T) { testcase := &httpboomer.TestCase{ Config: httpboomer.TConfig{ Name: "run request with variables", @@ -28,10 +29,9 @@ func TestCaseExtractStep(t *testing.T) { WithJmesPath("headers.\"Content-Type\"", "contentType"). WithJmesPath("body.args.foo1", "varFoo1"). Validate(). - AssertEqual("$statusCode", "$expectedStatusCode", "check status code"). // assert with extracted variable - AssertEqual("headers.Connection", "keep-alive", "check header Connection"). - AssertEqual("$contentType", "application/json; charset=utf-8", "check header Content-Type"). // assert with extracted variable - AssertEqual("$varFoo1", "bar1", "check args foo1"). // assert with extracted variable + AssertEqual("$statusCode", "$expectedStatusCode", "check status code"). // assert with extracted variable from current step + AssertEqual("$contentType", "application/json; charset=utf-8", "check header Content-Type"). // assert with extracted variable from current step + AssertEqual("$varFoo1", "bar1", "check args foo1"). // assert with extracted variable from current step AssertEqual("body.args.foo2", "bar2", "check args foo2"). AssertEqual("body.headers.\"user-agent\"", "HttpBoomer", "check header user agent"), }, @@ -42,3 +42,48 @@ func TestCaseExtractStep(t *testing.T) { t.Fatalf("run testcase error: %v", err) } } + +// reference extracted variables from previous step +func TestCaseExtractStepAssociation(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with variables", + BaseURL: "https://postman-echo.com", + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ + "var1": "bar1", + "agent": "HttpBoomer", + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "$agent"}). + Extract(). + WithJmesPath("status_code", "statusCode"). + WithJmesPath("headers.\"Content-Type\"", "contentType"). + WithJmesPath("body.args.foo1", "varFoo1"). + Validate(). + AssertEqual("$statusCode", 200, "check status code"). + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). + AssertEqual("$contentType", "application/json; charset=utf-8", "check header Content-Type"). + AssertEqual("$varFoo1", "bar1", "check args foo1"). + AssertEqual("body.args.foo2", "bar2", "check args foo2"). + AssertEqual("body.headers.\"user-agent\"", "HttpBoomer", "check header user agent"), + httpboomer.Step("post json data"). + POST("/post"). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + WithJSON(map[string]interface{}{"foo1": "bar1", "foo2": "bar2"}). + Validate(). + AssertEqual("status_code", "$statusCode", "check status code"). // assert with extracted variable from previous step + AssertEqual("$varFoo1", "bar1", "check json foo1"). // assert with extracted variable from previous step + AssertEqual("body.json.foo2", "bar2", "check json foo2"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/response.go b/response.go index a61abc92..816246ad 100644 --- a/response.go +++ b/response.go @@ -89,7 +89,8 @@ func (v *ResponseObject) Extract(extractors map[string]string) map[string]interf extractMapping := make(map[string]interface{}) for key, value := range extractors { extractedValue := v.searchJmespath(value) - log.Printf("extract %s => %v", value, extractedValue) + log.Printf("[extract] %s => %v", value, extractedValue) + log.Printf("[setVariable] %s = %v", key, extractedValue) extractMapping[key] = extractedValue } @@ -117,7 +118,7 @@ func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[ // do assertion result := assertFunc(v.t, expectValue, checkValue) - log.Printf("assert %s %s %v => %v", checkItem, assertMethod, expectValue, result) + log.Printf("[assert] %s <%s> %v => %v", checkItem, assertMethod, expectValue, result) if !result { v.t.Fail() } From d76b2f3e0fd13a7f69483fb9aad163eace27d10a Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 15:41:30 +0800 Subject: [PATCH 048/479] change: remove .vscode --- .gitignore | 3 ++- .vscode/settings.json | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index d6993ff2..53afb774 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ -__debug_bin \ No newline at end of file +__debug_bin +.vscode/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 77c6ed97..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "cSpell.words": [ - "debugtalk" - ] -} \ No newline at end of file From 1a1124b6e479d94f066c532f305bff6567298f2e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 16:05:38 +0800 Subject: [PATCH 049/479] change: add example for validation --- examples/extract_test.go | 2 +- examples/validate_test.go | 58 ++++++++++++++++++++++++++++++++++++++ examples/variables_test.go | 2 +- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 examples/validate_test.go diff --git a/examples/extract_test.go b/examples/extract_test.go index 6f5d5dc2..5a7c05e0 100644 --- a/examples/extract_test.go +++ b/examples/extract_test.go @@ -1,4 +1,4 @@ -package postman_echo +package examples import ( "testing" diff --git a/examples/validate_test.go b/examples/validate_test.go new file mode 100644 index 00000000..4f25c128 --- /dev/null +++ b/examples/validate_test.go @@ -0,0 +1,58 @@ +package examples + +import ( + "testing" + + "github.com/httprunner/httpboomer" +) + +func TestCaseValidateStep(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with variables", + BaseURL: "https://postman-echo.com", + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ + "var1": "bar1", + "agent": "HttpBoomer", + "expectedStatusCode": 200, + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "$agent"}). + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). + Validate(). + AssertEqual("status_code", "$expectedStatusCode", "check status code"). // assert status code + AssertEqual("headers.Connection", "keep-alive", "check header Connection"). // assert response header + AssertEqual("headers.\"Content-Type\"", "application/json; charset=utf-8", "check header Content-Type"). // assert response header, with double quotes + AssertEqual("body.args.foo1", "bar1", "check args foo1"). // assert response json body with jmespath + AssertEqual("body.args.foo2", "bar2", "check args foo2"). + AssertEqual("body.headers.\"user-agent\"", "HttpBoomer", "check header user agent"), + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ + "var1": "bar1", + "agent": "HttpBoomer", + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$var1", "foo2": "bar2"}). + WithHeaders(map[string]string{"User-Agent": "$agent"}). + Extract(). + WithJmesPath("status_code", "statusCode"). + WithJmesPath("headers.\"Content-Type\"", "contentType"). + Validate(). + AssertEqual("$statusCode", 200, "check status code"). // assert with extracted variable from current step + AssertEqual("$contentType", "application/json; charset=utf-8", "check header Content-Type"). // assert with extracted variable from current step + AssertEqual("$varFoo1", "bar1", "check args foo1"). // assert with extracted variable from previous step + AssertEqual("body.args.foo2", "bar2", "check args foo2"), // assert response json body with jmespath + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/examples/variables_test.go b/examples/variables_test.go index 0f94cd03..f3df328a 100644 --- a/examples/variables_test.go +++ b/examples/variables_test.go @@ -1,4 +1,4 @@ -package postman_echo +package examples import ( "testing" From ba7503a7c04905ed2f6e07d2b61483a94b2f3968 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 16:21:52 +0800 Subject: [PATCH 050/479] docs: add go reference badge --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f569bd85..8d910f21 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ +[![Go Reference](https://pkg.go.dev/badge/github.com/httprunner/httpboomer.svg)](https://pkg.go.dev/github.com/httprunner/httpboomer) + # HttpBoomer > HttpBoomer = [HttpRunner] + [Boomer] HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will be fully compatible with HttpRunner, including testcase format and usage. What's more, HttpBoomer will integrate Boomer natively to be a better load generator for [locust]. +## Key Features + +## Quick Start [HttpRunner]: https://github.com/httprunner/httprunner [Boomer]: https://github.com/myzhan/boomer From 2ed06ce29074a043b9acfafc156e859d66b8b243 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 16:30:18 +0800 Subject: [PATCH 051/479] Create main.yml feat: add github action --- .github/workflows/main.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..ca3c297b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,33 @@ +name: HttpBoomer CI + +on: + push: + pull_request: + types: [synchronize] + schedule: + - cron: "0 0 1,11,21 * *" + +jobs: + test: + strategy: + fail-fast: false + matrix: + go-version: + - 1.13.x + - 1.14.x + - 1.15.x + - 1.16.x + - 1.17.x + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v2 + - name: Test + run: go test ./... + - name: Test coverage + run: go test -coverprofile="cover.out" ./... # quotes needed for powershell From 6c9a7cc1f5890b5be36d9567c0f8ff99d93e18b5 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 16:35:31 +0800 Subject: [PATCH 052/479] docs: add github action badge --- .github/workflows/main.yml | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ca3c297b..9f92fdc0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: HttpBoomer CI +name: Build and Test on: push: diff --git a/README.md b/README.md index 8d910f21..c85fcf0e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/httprunner/httpboomer.svg)](https://pkg.go.dev/github.com/httprunner/httpboomer) +[![Github Actions](https://github.com/httprunner/HttpBoomer/actions/workflows/main.yml/badge.svg)](https://github.com/httprunner/HttpBoomer/actions) # HttpBoomer From a241df6ff3fd810b82bd72498fa577c36bc148f4 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 16:39:36 +0800 Subject: [PATCH 053/479] docs: add coveralls badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c85fcf0e..2f044440 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/httprunner/httpboomer.svg)](https://pkg.go.dev/github.com/httprunner/httpboomer) [![Github Actions](https://github.com/httprunner/HttpBoomer/actions/workflows/main.yml/badge.svg)](https://github.com/httprunner/HttpBoomer/actions) +[![Coverage Status](https://coveralls.io/repos/github/httprunner/HttpBoomer/badge.svg?branch=main)](https://coveralls.io/github/httprunner/HttpBoomer?branch=main) # HttpBoomer From 094a724a77c8a8489a5716e8e5f533aa583fe2b6 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 16:58:04 +0800 Subject: [PATCH 054/479] feat: add codecov --- .github/workflows/main.yml | 8 ++++---- README.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9f92fdc0..7ece2dd5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,7 +27,7 @@ jobs: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 - - name: Test - run: go test ./... - - name: Test coverage - run: go test -coverprofile="cover.out" ./... # quotes needed for powershell + - name: Run coverage + run: go test -race -coverprofile=coverage.txt -covermode=atomic + - name: Upload coverage to Codecov + run: bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index 2f044440..34fd7832 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/httprunner/httpboomer.svg)](https://pkg.go.dev/github.com/httprunner/httpboomer) [![Github Actions](https://github.com/httprunner/HttpBoomer/actions/workflows/main.yml/badge.svg)](https://github.com/httprunner/HttpBoomer/actions) -[![Coverage Status](https://coveralls.io/repos/github/httprunner/HttpBoomer/badge.svg?branch=main)](https://coveralls.io/github/httprunner/HttpBoomer?branch=main) +[![codecov](https://codecov.io/gh/httprunner/HttpBoomer/branch/main/graph/badge.svg?token=HPCQWCD7KO)](https://codecov.io/gh/httprunner/HttpBoomer) # HttpBoomer From 7b9573875504ba2bc966ba41bfc9c7f90c96a8a3 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 17:13:10 +0800 Subject: [PATCH 055/479] fix: coverage script --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7ece2dd5..09c1a1d5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,6 +28,6 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - name: Run coverage - run: go test -race -coverprofile=coverage.txt -covermode=atomic + run: go test -race -coverprofile="cover.out" -covermode=atomic ./... - name: Upload coverage to Codecov run: bash <(curl -s https://codecov.io/bash) From c4e59b5fe189f99f03601fbb0e2ea2264fd91996 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 17:21:58 +0800 Subject: [PATCH 056/479] fix: upload coverage for windows --- .github/workflows/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 09c1a1d5..9aaabbb1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,4 +30,7 @@ jobs: - name: Run coverage run: go test -race -coverprofile="cover.out" -covermode=atomic ./... - name: Upload coverage to Codecov - run: bash <(curl -s https://codecov.io/bash) + if: success() + run: | + curl -s https://codecov.io/bash | bash + shell: bash From 406ea4640cd384f74df3bdfc728a8a95d6d2d6f8 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 17:22:39 +0800 Subject: [PATCH 057/479] fix: upload coverage for windows --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9aaabbb1..502bf0d5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,6 +31,5 @@ jobs: run: go test -race -coverprofile="cover.out" -covermode=atomic ./... - name: Upload coverage to Codecov if: success() - run: | - curl -s https://codecov.io/bash | bash + run: curl -s https://codecov.io/bash | bash shell: bash From 009815c4e5029d52167d6299754d4edfaabb4028 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 17:39:49 +0800 Subject: [PATCH 058/479] change: replace with codecov-action --- .github/workflows/main.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 502bf0d5..a0c2c2d8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,6 +30,11 @@ jobs: - name: Run coverage run: go test -race -coverprofile="cover.out" -covermode=atomic ./... - name: Upload coverage to Codecov - if: success() - run: curl -s https://codecov.io/bash | bash - shell: bash + uses: codecov/codecov-action@v2 + with: + name: httpboomer # User defined upload name. Visible in Codecov UI + token: ${{ secrets.CODECOV_TOKEN }} # Repository upload token + file: ./cover.out # Path to coverage file to upload + flags: unittests # Flag upload to group coverage metrics + fail_ci_if_error: true # Specify whether or not CI build should fail if Codecov runs into an error during upload + verbose: true From 958e7adc7d4b3fedbee05f56fca3008b3bdc22ff Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 18:36:58 +0800 Subject: [PATCH 059/479] feat: add FOSSA Status badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 34fd7832..49b0e437 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/httprunner/httpboomer.svg)](https://pkg.go.dev/github.com/httprunner/httpboomer) [![Github Actions](https://github.com/httprunner/HttpBoomer/actions/workflows/main.yml/badge.svg)](https://github.com/httprunner/HttpBoomer/actions) [![codecov](https://codecov.io/gh/httprunner/HttpBoomer/branch/main/graph/badge.svg?token=HPCQWCD7KO)](https://codecov.io/gh/httprunner/HttpBoomer) +[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B27856%2Fgithub.com%2Fhttprunner%2FHttpBoomer.svg?type=shield)](https://app.fossa.com/reports/fb0e64a7-7dcf-48bb-8de9-8f0e016b903b) # HttpBoomer From 917a4b4f0617e19700db1323f991e5ec00df2204 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 1 Oct 2021 20:55:40 +0800 Subject: [PATCH 060/479] feat: add go report card --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 49b0e437..15bac0ca 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/httprunner/httpboomer.svg)](https://pkg.go.dev/github.com/httprunner/httpboomer) [![Github Actions](https://github.com/httprunner/HttpBoomer/actions/workflows/main.yml/badge.svg)](https://github.com/httprunner/HttpBoomer/actions) [![codecov](https://codecov.io/gh/httprunner/HttpBoomer/branch/main/graph/badge.svg?token=HPCQWCD7KO)](https://codecov.io/gh/httprunner/HttpBoomer) +[![Go Report Card](https://goreportcard.com/badge/github.com/httprunner/HttpBoomer)](https://goreportcard.com/report/github.com/httprunner/HttpBoomer) [![FOSSA Status](https://app.fossa.com/api/projects/custom%2B27856%2Fgithub.com%2Fhttprunner%2FHttpBoomer.svg?type=shield)](https://app.fossa.com/reports/fb0e64a7-7dcf-48bb-8de9-8f0e016b903b) # HttpBoomer From 661ac529b125ab001d89a3f59322a43205a5f7eb Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 1 Oct 2021 20:59:45 +0800 Subject: [PATCH 061/479] change: relocate unittest --- examples/{postman_echo/hardcode_test.go => request_test.go} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename examples/{postman_echo/hardcode_test.go => request_test.go} (97%) diff --git a/examples/postman_echo/hardcode_test.go b/examples/request_test.go similarity index 97% rename from examples/postman_echo/hardcode_test.go rename to examples/request_test.go index f061d146..3180244b 100644 --- a/examples/postman_echo/hardcode_test.go +++ b/examples/request_test.go @@ -1,4 +1,4 @@ -package postman_echo +package examples import ( "testing" @@ -6,7 +6,7 @@ import ( "github.com/httprunner/httpboomer" ) -func TestCaseHardcode(t *testing.T) { +func TestCaseBasicRequest(t *testing.T) { testcase := &httpboomer.TestCase{ Config: httpboomer.TConfig{ Name: "request methods testcase in hardcode", From ecbfad6900d7f3faf2ebfeaf4366f9e95bafe6e9 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 1 Oct 2021 21:02:42 +0800 Subject: [PATCH 062/479] change: doc style --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 15bac0ca..a5792cc9 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ +# HttpBoomer + [![Go Reference](https://pkg.go.dev/badge/github.com/httprunner/httpboomer.svg)](https://pkg.go.dev/github.com/httprunner/httpboomer) [![Github Actions](https://github.com/httprunner/HttpBoomer/actions/workflows/main.yml/badge.svg)](https://github.com/httprunner/HttpBoomer/actions) [![codecov](https://codecov.io/gh/httprunner/HttpBoomer/branch/main/graph/badge.svg?token=HPCQWCD7KO)](https://codecov.io/gh/httprunner/HttpBoomer) [![Go Report Card](https://goreportcard.com/badge/github.com/httprunner/HttpBoomer)](https://goreportcard.com/report/github.com/httprunner/HttpBoomer) [![FOSSA Status](https://app.fossa.com/api/projects/custom%2B27856%2Fgithub.com%2Fhttprunner%2FHttpBoomer.svg?type=shield)](https://app.fossa.com/reports/fb0e64a7-7dcf-48bb-8de9-8f0e016b903b) -# HttpBoomer - > HttpBoomer = [HttpRunner] + [Boomer] HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will be fully compatible with HttpRunner, including testcase format and usage. What's more, HttpBoomer will integrate Boomer natively to be a better load generator for [locust]. From 8bd7be85474361d0e8e47d402f2c0b8dc8a74395 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 2 Oct 2021 23:22:29 +0800 Subject: [PATCH 063/479] change: add unittest for regexCompileVariable --- parser_test.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/parser_test.go b/parser_test.go index f32ad9f1..47c84591 100644 --- a/parser_test.go +++ b/parser_test.go @@ -30,6 +30,40 @@ func TestBuildURL(t *testing.T) { } } +func TestRegexCompileVariable(t *testing.T) { + testData := []string{ + "$var1", + "${var1}", + "$v", + "var_1$_v", + "${var_1}#XYZ", + "func1($var_1, $var_3)", + } + + for _, expr := range testData { + varMatched := regexCompileVariable.FindStringSubmatch(expr) + if !assert.Len(t, varMatched, 3) { + t.Fail() + } + } +} + +func TestRegexCompileAbnormalVariable(t *testing.T) { + testData := []string{ + "var1", + "${var1", + "$123", + "var_1$", + "func1($123, var_3)", + } + + for _, expr := range testData { + varMatched := regexCompileVariable.FindStringSubmatch(expr) + if !assert.Len(t, varMatched, 0) { + t.Fail() + } + } +} func TestParseDataStringWithVariables(t *testing.T) { variablesMapping := map[string]interface{}{ "var_1": "abc", From fd4837c85264c4874b7285867e67048a17afe04c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 2 Oct 2021 23:58:12 +0800 Subject: [PATCH 064/479] feat: add regexCompileFunction --- parser.go | 6 ++++-- parser_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index 3dfb39ba..b4ea30d9 100644 --- a/parser.go +++ b/parser.go @@ -75,11 +75,13 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa } const ( - regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore + regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore + regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore ) var ( - regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var + regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var + regexCompileFunction = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\(([\$\w\.\-/\s=,]*)\)\}`, regexFunctionName)) // parse ${func1($a, $b)} ) // parseString parse string with variables diff --git a/parser_test.go b/parser_test.go index 47c84591..c1db12b3 100644 --- a/parser_test.go +++ b/parser_test.go @@ -64,6 +64,47 @@ func TestRegexCompileAbnormalVariable(t *testing.T) { } } } + +func TestRegexCompileFunction(t *testing.T) { + testData := []string{ + "${func1()}", + "${func1($a)}", + "${func1($a, $b)}", + "${func1($a, 123)}", + "${func1(123, $b)}", + "abc${func1(123, $b)}123", + } + + for _, expr := range testData { + varMatched := regexCompileFunction.FindStringSubmatch(expr) + if !assert.Len(t, varMatched, 3) { + t.Fail() + } + } +} + +func TestRegexCompileAbnormalFunction(t *testing.T) { + testData := []string{ + "${func1()", + "${func1(}", + "${func1)}", + "$func1()}", + "${1func1()}", // function name can not start with number + "${func1($a}", + "abc$func1(123, $b)}123", + // "${func1($a $b)}", + // "${func1($a, $123)}", + // "${func1(123 $b)}", + } + + for _, expr := range testData { + varMatched := regexCompileFunction.FindStringSubmatch(expr) + if !assert.Len(t, varMatched, 0) { + t.Fail() + } + } +} + func TestParseDataStringWithVariables(t *testing.T) { variablesMapping := map[string]interface{}{ "var_1": "abc", From bb9456cc7fed2d5aa94a713b1a3d01dea5bda207 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 10:04:29 +0800 Subject: [PATCH 065/479] feat: call function --- builtin/function.go | 11 +++++++++++ parser.go | 26 ++++++++++++++++++++++++++ parser_test.go | 14 ++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 builtin/function.go diff --git a/builtin/function.go b/builtin/function.go new file mode 100644 index 00000000..8ea6792d --- /dev/null +++ b/builtin/function.go @@ -0,0 +1,11 @@ +package builtin + +import "time" + +var FunctionsMap = map[string]interface{}{ + "sleep": Sleep, +} + +func Sleep(nSecs int) { + time.Sleep(time.Duration(nSecs) * time.Second) +} diff --git a/parser.go b/parser.go index b4ea30d9..7b8ee46d 100644 --- a/parser.go +++ b/parser.go @@ -7,6 +7,8 @@ import ( "reflect" "regexp" "strings" + + "github.com/httprunner/httpboomer/builtin" ) func parseStep(step IStep, config *TConfig) *TStep { @@ -166,3 +168,27 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s } return mergedVariables } + +func callFunction(funcName string, params []interface{}) (interface{}, error) { + function, ok := builtin.FunctionsMap[funcName] + if !ok { + // function not found + return nil, fmt.Errorf("function %s is not found", funcName) + } + + funcValue := reflect.ValueOf(function) + if funcValue.Kind() != reflect.Func { + // function not valid + return nil, fmt.Errorf("function %s is invalid", funcName) + } + + paramsValue := make([]reflect.Value, len(params)) + for index, param := range params { + paramsValue[index] = reflect.ValueOf(param) + } + + log.Printf("[callFunction] function: %v, params: %v", funcName, params) + result := funcValue.Call(paramsValue) + log.Printf("[callFunction] result: %v", result) + return result, nil +} diff --git a/parser_test.go b/parser_test.go index c1db12b3..9cd7a026 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2,6 +2,7 @@ package httpboomer import ( "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -278,3 +279,16 @@ func TestMergeVariables(t *testing.T) { t.Fail() } } + +func TestCallFunction(t *testing.T) { + funcName := "sleep" + params := []interface{}{1} + timeStart := time.Now() + _, err := callFunction(funcName, params) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second) { + t.Fail() + } +} From 249cd5cb348ff2018186a6ef5f85b7eff849b10d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 10:08:24 +0800 Subject: [PATCH 066/479] change: move assertions to builtin --- builtin/assertion.go | 15 +++++++++++++++ builtin/function.go | 2 +- parser.go | 2 +- response.go | 17 +++-------------- 4 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 builtin/assertion.go diff --git a/builtin/assertion.go b/builtin/assertion.go new file mode 100644 index 00000000..6634207f --- /dev/null +++ b/builtin/assertion.go @@ -0,0 +1,15 @@ +package builtin + +import "github.com/stretchr/testify/assert" + +var Assertions = map[string]func(t assert.TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool{ + "equals": assert.EqualValues, + "equal": assert.EqualValues, // alias for equals + "greater_than": assert.Greater, + "less_than": assert.Less, + "greater_or_equals": assert.GreaterOrEqual, + "less_or_equals": assert.LessOrEqual, + "not_equal": assert.NotEqual, + "contains": assert.Contains, + "regex_match": assert.Regexp, +} diff --git a/builtin/function.go b/builtin/function.go index 8ea6792d..e240fa16 100644 --- a/builtin/function.go +++ b/builtin/function.go @@ -2,7 +2,7 @@ package builtin import "time" -var FunctionsMap = map[string]interface{}{ +var Functions = map[string]interface{}{ "sleep": Sleep, } diff --git a/parser.go b/parser.go index 7b8ee46d..b9023a9c 100644 --- a/parser.go +++ b/parser.go @@ -170,7 +170,7 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s } func callFunction(funcName string, params []interface{}) (interface{}, error) { - function, ok := builtin.FunctionsMap[funcName] + function, ok := builtin.Functions[funcName] if !ok { // function not found return nil, fmt.Errorf("function %s is not found", funcName) diff --git a/response.go b/response.go index 816246ad..60c01b6b 100644 --- a/response.go +++ b/response.go @@ -8,20 +8,9 @@ import ( "github.com/imroc/req" "github.com/jmespath/go-jmespath" - "github.com/stretchr/testify/assert" -) -var assertFunctionsMap = map[string]func(t assert.TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool{ - "equals": assert.EqualValues, - "equal": assert.EqualValues, // alias for equals - "greater_than": assert.Greater, - "less_than": assert.Less, - "greater_or_equals": assert.GreaterOrEqual, - "less_or_equals": assert.LessOrEqual, - "not_equal": assert.NotEqual, - "contains": assert.Contains, - "regex_match": assert.Regexp, -} + "github.com/httprunner/httpboomer/builtin" +) func NewResponseObject(t *testing.T, resp *req.Resp) *ResponseObject { // prepare response headers @@ -111,7 +100,7 @@ func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[ // get assert method assertMethod := validator.Assert - assertFunc := assertFunctionsMap[assertMethod] + assertFunc := builtin.Assertions[assertMethod] // parse expected value expectValue := parseData(validator.Expect, variablesMapping) From b32a7a32b655c2c647221be54fc3f238e4e9eb67 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 10:41:49 +0800 Subject: [PATCH 067/479] feat: call function with one argument --- builtin/function.go | 21 ++++++++++++++++++--- parser.go | 28 +++++++++++++++++++++------- parser_test.go | 12 ++++++++++++ 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/builtin/function.go b/builtin/function.go index e240fa16..897c5078 100644 --- a/builtin/function.go +++ b/builtin/function.go @@ -1,11 +1,26 @@ package builtin -import "time" +import ( + "math/rand" + "time" +) var Functions = map[string]interface{}{ - "sleep": Sleep, + "sleep": sleep, + "gen_random_string": genRandomString, } -func Sleep(nSecs int) { +func sleep(nSecs int) { time.Sleep(time.Duration(nSecs) * time.Second) } + +func genRandomString(n int) string { + const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" + const lettersLen = len(letters) + + b := make([]byte, n) + for i := range b { + b[i] = letters[rand.Intn(lettersLen)] + } + return string(b) +} diff --git a/parser.go b/parser.go index b9023a9c..c27a8e30 100644 --- a/parser.go +++ b/parser.go @@ -169,7 +169,9 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s return mergedVariables } -func callFunction(funcName string, params []interface{}) (interface{}, error) { +// callFunction call function with arguments +// only support return at most one result value +func callFunction(funcName string, arguments []interface{}) (interface{}, error) { function, ok := builtin.Functions[funcName] if !ok { // function not found @@ -182,13 +184,25 @@ func callFunction(funcName string, params []interface{}) (interface{}, error) { return nil, fmt.Errorf("function %s is invalid", funcName) } - paramsValue := make([]reflect.Value, len(params)) - for index, param := range params { - paramsValue[index] = reflect.ValueOf(param) + argumentsValue := make([]reflect.Value, len(arguments)) + for index, argument := range arguments { + argumentsValue[index] = reflect.ValueOf(argument) } - log.Printf("[callFunction] function: %v, params: %v", funcName, params) - result := funcValue.Call(paramsValue) - log.Printf("[callFunction] result: %v", result) + log.Printf("[callFunction] func: %v, input arguments: %v", funcName, arguments) + resultValues := funcValue.Call(argumentsValue) + log.Printf("[callFunction] output results: %v", resultValues) + + if len(resultValues) == 0 { + return nil, nil + } else if len(resultValues) > 1 { + // function should return at most one value + err := fmt.Errorf("function %s should return at most one value", funcName) + log.Printf("[callFunction] error: %s", err.Error()) + return nil, err + } + + // convert reflect.Value to interface{} + result := resultValues[0].Interface() return result, nil } diff --git a/parser_test.go b/parser_test.go index 9cd7a026..674971ea 100644 --- a/parser_test.go +++ b/parser_test.go @@ -281,6 +281,7 @@ func TestMergeVariables(t *testing.T) { } func TestCallFunction(t *testing.T) { + // call function without arguments funcName := "sleep" params := []interface{}{1} timeStart := time.Now() @@ -291,4 +292,15 @@ func TestCallFunction(t *testing.T) { if !assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second) { t.Fail() } + + // call function with one argument + funcName = "gen_random_string" + params = []interface{}{10} + result, err := callFunction(funcName, params) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Equal(t, 10, len(result.(string))) { + t.Fail() + } } From eca8af71f1491a57ae5e6c27bdc37ab834826c51 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 11:38:04 +0800 Subject: [PATCH 068/479] feat: call function with two argument --- builtin/function.go | 6 ++++-- parser.go | 32 ++++++++++++++++++++++++++------ parser_test.go | 19 +++++++++++++++---- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/builtin/function.go b/builtin/function.go index 897c5078..8049a113 100644 --- a/builtin/function.go +++ b/builtin/function.go @@ -1,13 +1,15 @@ package builtin import ( + "math" "math/rand" "time" ) var Functions = map[string]interface{}{ - "sleep": sleep, - "gen_random_string": genRandomString, + "sleep": sleep, // call with one argument + "gen_random_string": genRandomString, // call with one argument + "max": math.Max, // call with two arguments } func sleep(nSecs int) { diff --git a/parser.go b/parser.go index c27a8e30..661d67c5 100644 --- a/parser.go +++ b/parser.go @@ -169,9 +169,9 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s return mergedVariables } -// callFunction call function with arguments +// callFunc call function with arguments // only support return at most one result value -func callFunction(funcName string, arguments []interface{}) (interface{}, error) { +func callFunc(funcName string, arguments []interface{}) (interface{}, error) { function, ok := builtin.Functions[funcName] if !ok { // function not found @@ -184,25 +184,45 @@ func callFunction(funcName string, arguments []interface{}) (interface{}, error) return nil, fmt.Errorf("function %s is invalid", funcName) } + if funcValue.Type().NumIn() != len(arguments) { + // function arguments not match + return nil, fmt.Errorf("function %s arguments number not match", funcName) + } + argumentsValue := make([]reflect.Value, len(arguments)) for index, argument := range arguments { + // ensure each argument type match + expectArgumentType := funcValue.Type().In(index) + actualArgumentType := reflect.TypeOf(argument) + if expectArgumentType != actualArgumentType { + // function argument type not match + err := fmt.Errorf("function %s argument %d type not match, expect %v, actual %v", + funcName, index, expectArgumentType, actualArgumentType) + log.Printf("[callFunction] error: %s", err.Error()) + return nil, err + } argumentsValue[index] = reflect.ValueOf(argument) } log.Printf("[callFunction] func: %v, input arguments: %v", funcName, arguments) resultValues := funcValue.Call(argumentsValue) - log.Printf("[callFunction] output results: %v", resultValues) + log.Printf("[callFunction] output values: %v", resultValues) - if len(resultValues) == 0 { - return nil, nil - } else if len(resultValues) > 1 { + if len(resultValues) > 1 { // function should return at most one value err := fmt.Errorf("function %s should return at most one value", funcName) log.Printf("[callFunction] error: %s", err.Error()) return nil, err } + // no return value + if len(resultValues) == 0 { + return nil, nil + } + + // return one value // convert reflect.Value to interface{} result := resultValues[0].Interface() + log.Printf("[callFunction] output result: %+v(%T)", result, result) return result, nil } diff --git a/parser_test.go b/parser_test.go index 674971ea..8e4e0540 100644 --- a/parser_test.go +++ b/parser_test.go @@ -283,9 +283,9 @@ func TestMergeVariables(t *testing.T) { func TestCallFunction(t *testing.T) { // call function without arguments funcName := "sleep" - params := []interface{}{1} + arguments := []interface{}{1} timeStart := time.Now() - _, err := callFunction(funcName, params) + _, err := callFunc(funcName, arguments) if !assert.Nil(t, err) { t.Fail() } @@ -295,12 +295,23 @@ func TestCallFunction(t *testing.T) { // call function with one argument funcName = "gen_random_string" - params = []interface{}{10} - result, err := callFunction(funcName, params) + arguments = []interface{}{10} + result, err := callFunc(funcName, arguments) if !assert.Nil(t, err) { t.Fail() } if !assert.Equal(t, 10, len(result.(string))) { t.Fail() } + + // call function with two argument + funcName = "max" + arguments = []interface{}{float64(10), 9.99} + result, err = callFunc(funcName, arguments) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Equal(t, float64(10), result.(float64)) { + t.Fail() + } } From 5ac86475917350ff5e7ec84c63ef0158dfa12719 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 12:30:53 +0800 Subject: [PATCH 069/479] feat: call function without argument --- builtin/function.go | 14 ++++++++++++++ parser.go | 2 +- parser_test.go | 18 +++++++++--------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/builtin/function.go b/builtin/function.go index 8049a113..12172327 100644 --- a/builtin/function.go +++ b/builtin/function.go @@ -1,15 +1,23 @@ package builtin import ( + "crypto/md5" + "encoding/hex" "math" "math/rand" "time" ) var Functions = map[string]interface{}{ + "get_timestamp": getTimestamp, // call without arguments "sleep": sleep, // call with one argument "gen_random_string": genRandomString, // call with one argument "max": math.Max, // call with two arguments + "md5": MD5, +} + +func getTimestamp() int64 { + return time.Now().UnixNano() / int64(time.Millisecond) } func sleep(nSecs int) { @@ -26,3 +34,9 @@ func genRandomString(n int) string { } return string(b) } + +func MD5(str string) string { + hasher := md5.New() + hasher.Write([]byte(str)) + return hex.EncodeToString(hasher.Sum(nil)) +} diff --git a/parser.go b/parser.go index 661d67c5..ca10c30b 100644 --- a/parser.go +++ b/parser.go @@ -171,7 +171,7 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s // callFunc call function with arguments // only support return at most one result value -func callFunc(funcName string, arguments []interface{}) (interface{}, error) { +func callFunc(funcName string, arguments ...interface{}) (interface{}, error) { function, ok := builtin.Functions[funcName] if !ok { // function not found diff --git a/parser_test.go b/parser_test.go index 8e4e0540..b1702625 100644 --- a/parser_test.go +++ b/parser_test.go @@ -282,10 +282,14 @@ func TestMergeVariables(t *testing.T) { func TestCallFunction(t *testing.T) { // call function without arguments - funcName := "sleep" - arguments := []interface{}{1} + _, err := callFunc("get_timestamp") + if !assert.Nil(t, err) { + t.Fail() + } + + // call function with one argument timeStart := time.Now() - _, err := callFunc(funcName, arguments) + _, err = callFunc("sleep", 1) if !assert.Nil(t, err) { t.Fail() } @@ -294,9 +298,7 @@ func TestCallFunction(t *testing.T) { } // call function with one argument - funcName = "gen_random_string" - arguments = []interface{}{10} - result, err := callFunc(funcName, arguments) + result, err := callFunc("gen_random_string", 10) if !assert.Nil(t, err) { t.Fail() } @@ -305,9 +307,7 @@ func TestCallFunction(t *testing.T) { } // call function with two argument - funcName = "max" - arguments = []interface{}{float64(10), 9.99} - result, err = callFunc(funcName, arguments) + result, err = callFunc("max", float64(10), 9.99) if !assert.Nil(t, err) { t.Fail() } From b7dbb6b5e76783662871b2a4ef164fb1d7671ba4 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 19:46:09 +0800 Subject: [PATCH 070/479] feat: eval literal --- go.mod | 1 + go.sum | 2 ++ parser.go | 28 ++++++++++++++++++++++++++-- parser_test.go | 25 +++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index df60a9f1..a0392e52 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/imroc/req v0.3.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/maja42/goval v1.2.1 // indirect github.com/myzhan/boomer v1.6.0 github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/shirou/gopsutil v3.21.8+incompatible // indirect diff --git a/go.sum b/go.sum index 8c81c378..5ebb7ba7 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Ca github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/maja42/goval v1.2.1 h1:fyEgzddqPgCZsKcFLk4C6SdCHyEaAHYvtZG4mGzQOHU= +github.com/maja42/goval v1.2.1/go.mod h1:42LU+BQXL/veE9jnTTUOSj38GRmOTSThYSXRVodI5J4= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0= diff --git a/parser.go b/parser.go index ca10c30b..c47be9f3 100644 --- a/parser.go +++ b/parser.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/httprunner/httpboomer/builtin" + "github.com/maja42/goval" ) func parseStep(step IStep, config *TConfig) *TStep { @@ -77,13 +78,15 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa } const ( - regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore - regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore + regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore + regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore + regexNumber = `-?\d+(\.\d+)?` // match number, e.g. 123, -123, 1.23, -1.23 ) var ( regexCompileVariable = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\}|\$(%s)`, regexVariable, regexVariable)) // parse ${var} or $var regexCompileFunction = regexp.MustCompile(fmt.Sprintf(`\$\{(%s)\(([\$\w\.\-/\s=,]*)\)\}`, regexFunctionName)) // parse ${func1($a, $b)} + regexCompileNumber = regexp.MustCompile(regexNumber) // parse number ) // parseString parse string with variables @@ -226,3 +229,24 @@ func callFunc(funcName string, arguments ...interface{}) (interface{}, error) { log.Printf("[callFunction] output result: %+v(%T)", result, result) return result, nil } + +var eval = goval.NewEvaluator() + +// literalEval parse string to number if possible +// e.g. "123" => 123 +// "1.23" => 1.23 +// "abc" => "abc" +// "$var" => "$var" +func literalEval(raw string) (interface{}, error) { + // check if raw is a number + if !regexCompileNumber.Match([]byte(raw)) { + return raw, nil + } + + // eval string to number + result, err := eval.Evaluate(raw, nil, nil) + if err != nil { + return raw, err + } + return result, nil +} diff --git a/parser_test.go b/parser_test.go index b1702625..92190bf9 100644 --- a/parser_test.go +++ b/parser_test.go @@ -315,3 +315,28 @@ func TestCallFunction(t *testing.T) { t.Fail() } } + +func TestLiteralEval(t *testing.T) { + testData := []struct { + expr string + expect interface{} + }{ + {"123", 123}, + {"1.23", 1.23}, + {"-123", -123}, + {"-1.23", -1.23}, + {"abc", "abc"}, + {"$var", "$var"}, + {"", ""}, + } + + for _, data := range testData { + value, err := literalEval(data.expr) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, value) { + t.Fail() + } + } +} From 58be5ff739ca6b3ab339dd7b5583ed0e77482cbb Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 20:15:55 +0800 Subject: [PATCH 071/479] feat: parseFunctionArguments --- parser.go | 41 +++++++++++++++++++++++++++++++++-------- parser_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/parser.go b/parser.go index c47be9f3..71617b54 100644 --- a/parser.go +++ b/parser.go @@ -78,9 +78,9 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa } const ( - regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore - regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore - regexNumber = `-?\d+(\.\d+)?` // match number, e.g. 123, -123, 1.23, -1.23 + regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore + regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore + regexNumber = `^-?\d+(\.\d+)?$` // match number, e.g. 123, -123, 1.23, -1.23 ) var ( @@ -233,12 +233,10 @@ func callFunc(funcName string, arguments ...interface{}) (interface{}, error) { var eval = goval.NewEvaluator() // literalEval parse string to number if possible -// e.g. "123" => 123 -// "1.23" => 1.23 -// "abc" => "abc" -// "$var" => "$var" func literalEval(raw string) (interface{}, error) { - // check if raw is a number + raw = strings.TrimSpace(raw) + + // return raw string if not number if !regexCompileNumber.Match([]byte(raw)) { return raw, nil } @@ -246,7 +244,34 @@ func literalEval(raw string) (interface{}, error) { // eval string to number result, err := eval.Evaluate(raw, nil, nil) if err != nil { + log.Printf("[literalEval] eval %s error: %s", raw, err.Error()) return raw, err } return result, nil } + +func parseFunctionArguments(argsStr string) ([]interface{}, error) { + argsStr = strings.TrimSpace(argsStr) + if argsStr == "" { + return []interface{}{}, nil + } + + // split arguments by comma + args := strings.Split(argsStr, ",") + arguments := make([]interface{}, len(args)) + for index, arg := range args { + arg = strings.TrimSpace(arg) + if arg == "" { + continue + } + + // parse argument to number if possible + arg, err := literalEval(arg) + if err != nil { + return nil, err + } + arguments[index] = arg + } + + return arguments, nil +} diff --git a/parser_test.go b/parser_test.go index 92190bf9..2d3e4063 100644 --- a/parser_test.go +++ b/parser_test.go @@ -326,7 +326,11 @@ func TestLiteralEval(t *testing.T) { {"-123", -123}, {"-1.23", -1.23}, {"abc", "abc"}, + {" a bc ", "a bc"}, + {" a $bc ", "a $bc"}, {"$var", "$var"}, + {" $var ", "$var"}, + {" $var1 ", "$var1"}, {"", ""}, } @@ -340,3 +344,33 @@ func TestLiteralEval(t *testing.T) { } } } + +func TestParseFunctionArguments(t *testing.T) { + testData := []struct { + expr string + expect interface{} + }{ + {"", []interface{}{}}, + {"123", []interface{}{123}}, + {"1.23", []interface{}{1.23}}, + {"-123", []interface{}{-123}}, + {"-1.23", []interface{}{-1.23}}, + {"abc", []interface{}{"abc"}}, + {"$var", []interface{}{"$var"}}, + {"1,2", []interface{}{1, 2}}, + {"1,2.3", []interface{}{1, 2.3}}, + {"1, -2.3", []interface{}{1, -2.3}}, + {"1,,2", []interface{}{1, nil, 2}}, + {" $var1 , 2 ", []interface{}{"$var1", 2}}, + } + + for _, data := range testData { + value, err := parseFunctionArguments(data.expr) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, value) { + t.Fail() + } + } +} From cb0e450f18ec92b9a80f350d15c7e40e3ee8f94c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 21:16:05 +0800 Subject: [PATCH 072/479] feat: parse functions --- parser.go | 38 ++++++++++++++++++++++++++++++++++++++ parser_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/parser.go b/parser.go index 71617b54..71bb243e 100644 --- a/parser.go +++ b/parser.go @@ -55,6 +55,12 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa value := rawValue.String() value = strings.TrimSpace(value) return parseString(value, variablesMapping) + case reflect.Slice: + parsedSlice := make([]interface{}, rawValue.Len()) + for i := 0; i < rawValue.Len(); i++ { + parsedSlice[i] = parseData(rawValue.Index(i).Interface(), variablesMapping) + } + return parsedSlice case reflect.Map: // convert any map to map[string]interface{} parsedMap := make(map[string]interface{}) for _, k := range rawValue.MapKeys() { @@ -109,6 +115,9 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ parsedString += remainedString[0:startPosition] remainedString = remainedString[startPosition:] + // Notice: notation priority + // $$ > ${func($a, $b)} > $var + // search $$, use $$ to escape $ notation if strings.HasPrefix(remainedString, "$$") { // found $$ matchStartPosition += 2 @@ -117,6 +126,35 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ continue } + // search function like ${func($a, $b)} + funcMatched := regexCompileFunction.FindStringSubmatch(remainedString) + if len(funcMatched) == 3 { + funcName := funcMatched[1] + argsStr := funcMatched[2] + arguments, err := parseFunctionArguments(argsStr) + if err != nil { + return raw + } + parsedArgs := parseData(arguments, variablesMapping).([]interface{}) + + result, err := callFunc(funcName, parsedArgs...) + if err != nil { + return raw + } + + if funcMatched[0] == raw { + // raw_string is a function, e.g. "${add_one(3)}", return its eval value directly + return result + } + + // raw_string contains one or many functions, e.g. "abc${add_one(3)}def" + matchStartPosition += len(funcMatched[0]) + parsedString += fmt.Sprintf("%v", result) + remainedString = raw[matchStartPosition:] + log.Printf("[parseString] parsedString: %v, matchStartPosition: %v", parsedString, matchStartPosition) + continue + } + // search variable like ${var} or $var varMatched := regexCompileVariable.FindStringSubmatch(remainedString) if len(varMatched) == 3 { diff --git a/parser_test.go b/parser_test.go index 2d3e4063..6793c9a0 100644 --- a/parser_test.go +++ b/parser_test.go @@ -166,6 +166,7 @@ func TestParseDataStringWithVariables(t *testing.T) { } } } + func TestParseDataStringWithVariablesAbnormal(t *testing.T) { variablesMapping := map[string]interface{}{ "var_1": "abc", @@ -374,3 +375,35 @@ func TestParseFunctionArguments(t *testing.T) { } } } + +func TestParseDataStringWithFunctions(t *testing.T) { + variablesMapping := map[string]interface{}{ + "n": 5, + "a": 12.3, + "b": 3.45, + } + + if !assert.Len(t, parseData("${gen_random_string(5)}", variablesMapping), 5) { + t.Fail() + } + if !assert.Len(t, parseData("${gen_random_string($n)}", variablesMapping), 5) { + t.Fail() + } + + if !assert.Len(t, parseData("123${gen_random_string(5)}abc", variablesMapping), 11) { + t.Fail() + } + if !assert.Len(t, parseData("123${gen_random_string($n)}abc", variablesMapping), 11) { + t.Fail() + } + + if !assert.Equal(t, parseData("${max($a, $b)}", variablesMapping), 12.3) { + t.Fail() + } + if !assert.Equal(t, parseData("abc${max($a, $b)}123", variablesMapping), "abc12.3123") { + t.Fail() + } + if !assert.Equal(t, parseData("abc${max($a, 3.45)}123", variablesMapping), "abc12.3123") { + t.Fail() + } +} From a221afb51d282f1e63ca495eaeacc578ea0b07bb Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 21:52:47 +0800 Subject: [PATCH 073/479] feat: add length equal --- builtin/assertion.go | 11 ++++++++++- examples/function_test.go | 38 ++++++++++++++++++++++++++++++++++++++ examples/validate_test.go | 2 +- validate.go | 33 ++++++++++++++++++++++----------- 4 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 examples/function_test.go diff --git a/builtin/assertion.go b/builtin/assertion.go index 6634207f..99782626 100644 --- a/builtin/assertion.go +++ b/builtin/assertion.go @@ -1,6 +1,8 @@ package builtin -import "github.com/stretchr/testify/assert" +import ( + "github.com/stretchr/testify/assert" +) var Assertions = map[string]func(t assert.TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool{ "equals": assert.EqualValues, @@ -12,4 +14,11 @@ var Assertions = map[string]func(t assert.TestingT, expected interface{}, actual "not_equal": assert.NotEqual, "contains": assert.Contains, "regex_match": assert.Regexp, + // custom assertions + "length_equals": EqualLength, + "length_equal": EqualLength, // alias for length_equals +} + +func EqualLength(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + return assert.Len(t, actual, expected.(int), msgAndArgs...) } diff --git a/examples/function_test.go b/examples/function_test.go new file mode 100644 index 00000000..81140e20 --- /dev/null +++ b/examples/function_test.go @@ -0,0 +1,38 @@ +package examples + +import ( + "testing" + + "github.com/httprunner/httpboomer" +) + +func TestCaseCallFunction(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with functions", + BaseURL: "https://postman-echo.com", + Verify: false, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ + "n": 5, + "a": 12.3, + "b": 3.45, + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). + Validate(). + AssertLengthEqual("body.args.foo1", 5, "check args foo1"). + AssertEqual("body.args.foo2", "12.3", "check args foo2"), // notice: request params value will be converted to string + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/examples/validate_test.go b/examples/validate_test.go index 4f25c128..ab6bbf96 100644 --- a/examples/validate_test.go +++ b/examples/validate_test.go @@ -9,7 +9,7 @@ import ( func TestCaseValidateStep(t *testing.T) { testcase := &httpboomer.TestCase{ Config: httpboomer.TConfig{ - Name: "run request with variables", + Name: "run request with validation", BaseURL: "https://postman-echo.com", Verify: false, }, diff --git a/validate.go b/validate.go index 8bc1f922..4244edb7 100644 --- a/validate.go +++ b/validate.go @@ -9,17 +9,6 @@ type stepRequestValidation struct { step *TStep } -func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { - validator := TValidator{ - Check: jmesPath, - Assert: "equals", - Expect: expected, - Message: msg, - } - s.step.Validators = append(s.step.Validators, validator) - return s -} - func (s *stepRequestValidation) Name() string { return s.step.Name } @@ -31,3 +20,25 @@ func (s *stepRequestValidation) Type() string { func (s *stepRequestValidation) ToStruct() *TStep { return s.step } + +func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { + validator := TValidator{ + Check: jmesPath, + Assert: "equals", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, validator) + return s +} + +func (s *stepRequestValidation) AssertLengthEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { + validator := TValidator{ + Check: jmesPath, + Assert: "length_equals", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, validator) + return s +} From 0a96da2f418fc19a71b2611443469a8bffb91bae Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 22:56:22 +0800 Subject: [PATCH 074/479] feat: post json with functions --- examples/function_test.go | 19 ++++++++++++++----- runner.go | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/function_test.go b/examples/function_test.go index 81140e20..e9398240 100644 --- a/examples/function_test.go +++ b/examples/function_test.go @@ -12,22 +12,31 @@ func TestCaseCallFunction(t *testing.T) { Name: "run request with functions", BaseURL: "https://postman-echo.com", Verify: false, + Variables: map[string]interface{}{ + "n": 5, + "a": 12.3, + "b": 3.45, + }, }, TestSteps: []httpboomer.IStep{ httpboomer.Step("get with params"). - WithVariables(map[string]interface{}{ - "n": 5, - "a": 12.3, - "b": 3.45, - }). GET("/get"). WithParams(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}). WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). Extract(). WithJmesPath("body.args.foo1", "varFoo1"). Validate(). + AssertEqual("status_code", 200, "check status code"). AssertLengthEqual("body.args.foo1", 5, "check args foo1"). AssertEqual("body.args.foo2", "12.3", "check args foo2"), // notice: request params value will be converted to string + httpboomer.Step("post json data with functions"). + POST("/post"). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + WithJSON(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.json.foo1", 5, "check args foo1"). + AssertEqual("body.json.foo2", 12.3, "check args foo2"), }, } diff --git a/runner.go b/runner.go index 809fb0d1..4a39e7ad 100644 --- a/runner.go +++ b/runner.go @@ -114,7 +114,8 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { v = append(v, data) } if step.Request.JSON != nil { - v = append(v, req.BodyJSON(step.Request.JSON)) + jsonData := parseData(step.Request.JSON, step.Variables) + v = append(v, req.BodyJSON(jsonData)) } for cookieName, cookieValue := range step.Request.Cookies { From 2515d31316fa0efdb40520f54e6c041a0e21dfbf Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 23:05:53 +0800 Subject: [PATCH 075/479] fix: genRandomString --- builtin/function.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/builtin/function.go b/builtin/function.go index 12172327..d1d3dd3b 100644 --- a/builtin/function.go +++ b/builtin/function.go @@ -16,6 +16,10 @@ var Functions = map[string]interface{}{ "md5": MD5, } +func init() { + rand.Seed(time.Now().UnixNano()) +} + func getTimestamp() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } @@ -24,10 +28,10 @@ func sleep(nSecs int) { time.Sleep(time.Duration(nSecs) * time.Second) } -func genRandomString(n int) string { - const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" - const lettersLen = len(letters) +const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" +func genRandomString(n int) string { + lettersLen := len(letters) b := make([]byte, n) for i := range b { b[i] = letters[rand.Intn(lettersLen)] From 3b99b57eea761e466ed8625a73d8d9b461b167e0 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 23:49:04 +0800 Subject: [PATCH 076/479] feat: convertString --- parser.go | 29 +++++++++++++++++------------ parser_test.go | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/parser.go b/parser.go index 71bb243e..6ebe6ed7 100644 --- a/parser.go +++ b/parser.go @@ -39,15 +39,21 @@ func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]inte parsedHeaders := make(map[string]string) headers := parseData(rawHeaders, variablesMapping).(map[string]interface{}) for k, v := range headers { - if value, ok := v.(string); ok { - parsedHeaders[k] = value - } else { - parsedHeaders[k] = fmt.Sprintf("%v", v) - } + parsedHeaders[k] = convertString(v) } return parsedHeaders } +func convertString(raw interface{}) string { + if value, ok := raw.(string); ok { + return value + } else { + // raw is not string, e.g. int, float, etc. + // convert to string + return fmt.Sprintf("%v", raw) + } +} + func parseData(raw interface{}, variablesMapping map[string]interface{}) interface{} { rawValue := reflect.ValueOf(raw) switch rawValue.Kind() { @@ -68,13 +74,8 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa v := rawValue.MapIndex(k) parsedValue := parseData(v.Interface(), variablesMapping) - if key, ok := parsedKey.(string); ok { - parsedMap[key] = parsedValue - } else { - // parsed key is not string, e.g. int, float, etc. - // convert to string - parsedMap[fmt.Sprintf("%v", parsedKey)] = parsedValue - } + key := convertString(parsedKey) + parsedMap[key] = parsedValue } return parsedMap default: @@ -313,3 +314,7 @@ func parseFunctionArguments(argsStr string) ([]interface{}, error) { return arguments, nil } + +func parseVariables(variables map[string]interface{}) (map[string]interface{}, error) { + return variables, nil +} diff --git a/parser_test.go b/parser_test.go index 6793c9a0..a0d3345c 100644 --- a/parser_test.go +++ b/parser_test.go @@ -407,3 +407,24 @@ func TestParseDataStringWithFunctions(t *testing.T) { t.Fail() } } + +func TestConvertString(t *testing.T) { + testData := []struct { + raw interface{} + expect interface{} + }{ + {"", ""}, + {"abc", "abc"}, + {"123", "123"}, + {123, "123"}, + {1.23, "1.23"}, + {nil, ""}, + } + + for _, data := range testData { + value := convertString(data.raw) + if !assert.Equal(t, data.expect, value) { + t.Fail() + } + } +} From ec295760dc61ccdc3451b2e7762e23f1e0511429 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 23:50:22 +0800 Subject: [PATCH 077/479] feat: parseConfig --- runner.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/runner.go b/runner.go index 4a39e7ad..303e9227 100644 --- a/runner.go +++ b/runner.go @@ -47,6 +47,10 @@ func (r *Runner) Run(testcases ...*TestCase) error { func (r *Runner) runCase(testcase *TestCase) error { config := &testcase.Config + if err := r.parseConfig(config); err != nil { + return err + } + log.Printf("Start to run testcase: %v", config.Name) extractedVariables := make(map[string]interface{}) @@ -164,6 +168,26 @@ func (r *Runner) runStepTestCase(step *TStep) (stepData *StepData, err error) { return } +func (r *Runner) parseConfig(config *TConfig) error { + // parse config variables + parsedVariables, err := parseVariables(config.Variables) + if err != nil { + log.Printf("[parseConfig] parse variables: %v, error: %v", config.Variables, err) + return err + } + config.Variables = parsedVariables + + // parse config name + parsedName := parseString(config.Name, config.Variables) + config.Name = convertString(parsedName) + + // parse config base url + parsedBaseURL := parseString(config.BaseURL, config.Variables) + config.BaseURL = convertString(parsedBaseURL) + + return nil +} + func (r *Runner) GetSummary() *TestCaseSummary { return &TestCaseSummary{} } From caf791a2826d67ab1e82344647794afd9e622546 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 3 Oct 2021 23:55:52 +0800 Subject: [PATCH 078/479] change: parse step variables --- runner.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/runner.go b/runner.go index 303e9227..0af2b28d 100644 --- a/runner.go +++ b/runner.go @@ -58,9 +58,17 @@ func (r *Runner) runCase(testcase *TestCase) error { for _, step := range testcase.TestSteps { // override variables // step variables > extracted variables from previous steps - step.ToStruct().Variables = mergeVariables(step.ToStruct().Variables, extractedVariables) + stepVariables := mergeVariables(step.ToStruct().Variables, extractedVariables) // step variables > testcase config variables - step.ToStruct().Variables = mergeVariables(step.ToStruct().Variables, config.Variables) + stepVariables = mergeVariables(stepVariables, config.Variables) + + // parse step variables + parsedVariables, err := parseVariables(stepVariables) + if err != nil { + log.Printf("[parseConfig] parse variables: %v, error: %v", config.Variables, err) + return err + } + step.ToStruct().Variables = parsedVariables stepData, err := r.runStep(step, config) if err != nil { From b54e39a5e7f7bb10219f7593a7da61c1a611beda Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 10:37:22 +0800 Subject: [PATCH 079/479] feat: parseVariables --- parser.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++- parser_test.go | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+), 1 deletion(-) diff --git a/parser.go b/parser.go index 6ebe6ed7..51f816f6 100644 --- a/parser.go +++ b/parser.go @@ -316,5 +316,134 @@ func parseFunctionArguments(argsStr string) ([]interface{}, error) { } func parseVariables(variables map[string]interface{}) (map[string]interface{}, error) { - return variables, nil + parsedVariables := make(map[string]interface{}) + var traverseRounds int + + for len(parsedVariables) != len(variables) { + for varName, varValue := range variables { + // skip parsed variables + if _, ok := parsedVariables[varName]; ok { + continue + } + + // extract variables from current value + extractVarsSet := extractVariables(varValue) + + // check if reference variable itself + // e.g. + // variables = {"token": "abc$token"} + // variables = {"key": ["$key", 2]} + if _, ok := extractVarsSet[varName]; ok { + log.Printf("[parseVariables] variable self reference error: %v", variables) + return variables, fmt.Errorf("variable self reference: %v", varName) + } + + // check if reference variable not in variables mapping + // e.g. + // {"varA": "123$varB", "varB": "456$varC"} => $varC not defined + // {"varC": "${sum_two($a, $b)}"} => $a, $b not defined + var undefinedVars []string + for extractVar := range extractVarsSet { + if _, ok := variables[extractVar]; !ok { // not in variables mapping + undefinedVars = append(undefinedVars, extractVar) + } + } + if len(undefinedVars) > 0 { + log.Printf("[parseVariables] variable not defined error: %v", undefinedVars) + return variables, fmt.Errorf("variable not defined: %v", undefinedVars) + } + + parsedValue := parseData(varValue, parsedVariables) + if parsedValue == nil { + continue + } + parsedVariables[varName] = parsedValue + } + traverseRounds += 1 + // check if circular reference exists + if traverseRounds > len(variables) { + log.Printf("[parseVariables] circular reference error, break infinite loop!") + return variables, fmt.Errorf("circular reference") + } + } + + return parsedVariables, nil +} + +type variableSet map[string]struct{} + +func extractVariables(raw interface{}) variableSet { + rawValue := reflect.ValueOf(raw) + switch rawValue.Kind() { + case reflect.String: + return findallVariables(rawValue.String()) + case reflect.Slice: + varSet := make(variableSet) + for i := 0; i < rawValue.Len(); i++ { + for extractVar := range extractVariables(rawValue.Index(i).Interface()) { + varSet[extractVar] = struct{}{} + } + } + return varSet + case reflect.Map: + varSet := make(variableSet) + for _, key := range rawValue.MapKeys() { + value := rawValue.MapIndex(key) + for extractVar := range extractVariables(value.Interface()) { + varSet[extractVar] = struct{}{} + } + } + return varSet + default: + // other types, e.g. nil, int, float, bool + return make(variableSet) + } +} + +func findallVariables(raw string) variableSet { + matchStartPosition := 0 + remainedString := raw + varSet := make(variableSet) + + for matchStartPosition < len(raw) { + // locate $ char position + startPosition := strings.Index(remainedString, "$") + if startPosition == -1 { // no $ found + return varSet + } + + // found $, check if variable or function + matchStartPosition += startPosition + remainedString = remainedString[startPosition:] + + // Notice: notation priority + // $$ > $var + + // search $$, use $$ to escape $ notation + if strings.HasPrefix(remainedString, "$$") { // found $$ + matchStartPosition += 2 + remainedString = remainedString[2:] + continue + } + + // search variable like ${var} or $var + varMatched := regexCompileVariable.FindStringSubmatch(remainedString) + if len(varMatched) == 3 { + var varName string + if varMatched[1] != "" { + varName = varMatched[1] // match ${var} + } else { + varName = varMatched[2] // match $var + } + varSet[varName] = struct{}{} + + matchStartPosition += len(varMatched[0]) + remainedString = raw[matchStartPosition:] + continue + } + + break + } + + return varSet } diff --git a/parser_test.go b/parser_test.go index a0d3345c..aacb5c64 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1,6 +1,7 @@ package httpboomer import ( + "sort" "testing" "time" @@ -428,3 +429,127 @@ func TestConvertString(t *testing.T) { } } } + +func TestParseVariables(t *testing.T) { + testData := []struct { + rawVars map[string]interface{} + expectVars map[string]interface{} + }{ + { + map[string]interface{}{"varA": "$varB", "varB": "$varC", "varC": "123", "a": 1, "b": 2}, + map[string]interface{}{"varA": "123", "varB": "123", "varC": "123", "a": 1, "b": 2}, + }, + } + + for _, data := range testData { + value, err := parseVariables(data.rawVars) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expectVars, value) { + t.Fail() + } + } +} + +func TestParseVariablesAbnormal(t *testing.T) { + testData := []struct { + rawVars map[string]interface{} + expectVars map[string]interface{} + }{ + { // self referenced variable $varA + map[string]interface{}{"varA": "$varA"}, + map[string]interface{}{"varA": "$varA"}, + }, + { // undefined variable $varC + map[string]interface{}{"varA": "$varB", "varB": "$varC", "a": 1, "b": 2}, + map[string]interface{}{"varA": "$varB", "varB": "$varC", "a": 1, "b": 2}, + }, + { // circular reference + map[string]interface{}{"varA": "$varB", "varB": "$varA"}, + map[string]interface{}{"varA": "$varB", "varB": "$varA"}, + }, + } + + for _, data := range testData { + value, err := parseVariables(data.rawVars) + if !assert.NotNil(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expectVars, value) { + t.Fail() + } + } +} + +func TestExtractVariables(t *testing.T) { + testData := []struct { + raw interface{} + expectVars []string + }{ + {nil, nil}, + {"/$var1/$var1", []string{"var1"}}, + { + map[string]interface{}{"varA": "$varB", "varB": "$varC", "varC": "123"}, + []string{"varB", "varC"}, + }, + { + []interface{}{"varA", "$varB", 123, "$varC", "123"}, + []string{"varB", "varC"}, + }, + { // nested map and slice + map[string]interface{}{"varA": "$varB", "varB": map[string]interface{}{"C": "$varC", "D": []string{"$varE"}}}, + []string{"varB", "varC", "varE"}, + }, + } + + for _, data := range testData { + var varList []string + for varName := range extractVariables(data.raw) { + varList = append(varList, varName) + } + sort.Strings(varList) + if !assert.Equal(t, data.expectVars, varList) { + t.Fail() + } + } +} + +func TestFindallVariables(t *testing.T) { + testData := []struct { + raw string + expectVars []string + }{ + {"", nil}, + {"$variable", []string{"variable"}}, + {"${variable}123", []string{"variable"}}, + {"/blog/$postid", []string{"postid"}}, + {"/$var1/$var2", []string{"var1", "var2"}}, + {"/$var1/$var1", []string{"var1"}}, + {"abc", nil}, + {"Z:2>1*0*1+1$a", []string{"a"}}, + {"Z:2>1*0*1+1$$a", nil}, + {"Z:2>1*0*1+1$$$a", []string{"a"}}, + {"Z:2>1*0*1+1$$$$a", nil}, + {"Z:2>1*0*1+1$$a$b", []string{"b"}}, + {"Z:2>1*0*1+1$$a$$b", nil}, + {"Z:2>1*0*1+1$a$b", []string{"a", "b"}}, + {"Z:2>1*0*1+1$$1", nil}, + {"a$var", []string{"var"}}, + {"a$v b", []string{"v"}}, + {"${func()}", nil}, + {"a${func(1,2)}b", nil}, + {"${gen_md5($TOKEN, $data, $random)}", []string{"TOKEN", "data", "random"}}, + } + + for _, data := range testData { + var varList []string + for varName := range findallVariables(data.raw) { + varList = append(varList, varName) + } + sort.Strings(varList) + if !assert.Equal(t, data.expectVars, varList) { + t.Fail() + } + } +} From f387799510a06bd5e0904ef9c13a2391e84c3d5d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 12:38:18 +0800 Subject: [PATCH 080/479] change: add unittest and examples for parseVariables --- examples/variables_test.go | 47 ++++++++++++++++++++++++++++++++++++++ parser_test.go | 39 +++++++++++++++++++------------ 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/examples/variables_test.go b/examples/variables_test.go index f3df328a..5ce4262c 100644 --- a/examples/variables_test.go +++ b/examples/variables_test.go @@ -109,3 +109,50 @@ func TestCaseOverrideConfigVariables(t *testing.T) { t.Fatalf("run testcase error: %v", err) } } + +func TestCaseParseVariables(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "run request with functions", + BaseURL: "https://postman-echo.com", + Verify: false, + Variables: map[string]interface{}{ + "n": 5, + "a": 12.3, + "b": 3.45, + "varFoo1": "${gen_random_string($n)}", + "varFoo2": "${max($a, $b)}", // 12.3 + }, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ + "n": 3, + "b": 34.5, + "varFoo2": "${max($a, $b)}", // 34.5 + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.args.foo1", 5, "check args foo1"). + AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string + httpboomer.Step("post json data with functions"). + POST("/post"). + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). + WithJSON(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.json.foo1", 5, "check args foo1"). + AssertEqual("body.json.foo2", 12.3, "check args foo2"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} diff --git a/parser_test.go b/parser_test.go index aacb5c64..a1f735e3 100644 --- a/parser_test.go +++ b/parser_test.go @@ -264,21 +264,28 @@ func TestParseHeaders(t *testing.T) { } func TestMergeVariables(t *testing.T) { - stepVariables := map[string]interface{}{ - "base_url": "$base_url", - "foo1": "bar1", + testData := []struct { + stepVariables map[string]interface{} + configVariables map[string]interface{} + expectVariables map[string]interface{} + }{ + { + map[string]interface{}{"base_url": "$base_url", "foo1": "bar1"}, + map[string]interface{}{"base_url": "https://httpbin.org", "foo1": "bar111"}, + map[string]interface{}{"base_url": "https://httpbin.org", "foo1": "bar1"}, + }, + { + map[string]interface{}{"n": 3, "b": 34.5, "varFoo2": "${max($a, $b)}"}, + map[string]interface{}{"n": 5, "a": 12.3, "b": 3.45, "varFoo1": "7a6K3", "varFoo2": 12.3}, + map[string]interface{}{"n": 3, "a": 12.3, "b": 34.5, "varFoo1": "7a6K3", "varFoo2": "${max($a, $b)}"}, + }, } - configVariables := map[string]interface{}{ - "base_url": "https://httpbin.org", - "foo1": "bar111", - } - mergedVariables := mergeVariables(stepVariables, configVariables) - expectVariables := map[string]interface{}{ - "base_url": "https://httpbin.org", - "foo1": "bar1", - } - if !assert.Equal(t, expectVariables, mergedVariables) { - t.Fail() + + for _, data := range testData { + mergedVariables := mergeVariables(data.stepVariables, data.configVariables) + if !assert.Equal(t, data.expectVariables, mergedVariables) { + t.Fail() + } } } @@ -439,6 +446,10 @@ func TestParseVariables(t *testing.T) { map[string]interface{}{"varA": "$varB", "varB": "$varC", "varC": "123", "a": 1, "b": 2}, map[string]interface{}{"varA": "123", "varB": "123", "varC": "123", "a": 1, "b": 2}, }, + { + map[string]interface{}{"n": 34.5, "a": 12.3, "b": "$n", "varFoo2": "${max($a, $b)}"}, + map[string]interface{}{"n": 34.5, "a": 12.3, "b": 34.5, "varFoo2": 34.5}, + }, } for _, data := range testData { From 693a8fc445ec45961cbd5c0df5291f720b93dfda Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 13:20:31 +0800 Subject: [PATCH 081/479] refactor: check errors --- parser.go | 65 +++++++++++++++++---------- parser_test.go | 116 ++++++++++++++++++++++++++++++++++++------------- response.go | 12 +++-- runner.go | 30 ++++++++++--- 4 files changed, 160 insertions(+), 63 deletions(-) diff --git a/parser.go b/parser.go index 51f816f6..f5179cb3 100644 --- a/parser.go +++ b/parser.go @@ -35,13 +35,16 @@ func buildURL(baseURL, stepURL string) string { return uStep.String() } -func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) map[string]string { +func parseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) (map[string]string, error) { parsedHeaders := make(map[string]string) - headers := parseData(rawHeaders, variablesMapping).(map[string]interface{}) - for k, v := range headers { + headers, err := parseData(rawHeaders, variablesMapping) + if err != nil { + return rawHeaders, err + } + for k, v := range headers.(map[string]interface{}) { parsedHeaders[k] = convertString(v) } - return parsedHeaders + return parsedHeaders, nil } func convertString(raw interface{}) string { @@ -54,7 +57,7 @@ func convertString(raw interface{}) string { } } -func parseData(raw interface{}, variablesMapping map[string]interface{}) interface{} { +func parseData(raw interface{}, variablesMapping map[string]interface{}) (interface{}, error) { rawValue := reflect.ValueOf(raw) switch rawValue.Kind() { case reflect.String: @@ -64,23 +67,33 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa case reflect.Slice: parsedSlice := make([]interface{}, rawValue.Len()) for i := 0; i < rawValue.Len(); i++ { - parsedSlice[i] = parseData(rawValue.Index(i).Interface(), variablesMapping) + parsedValue, err := parseData(rawValue.Index(i).Interface(), variablesMapping) + if err != nil { + return raw, err + } + parsedSlice[i] = parsedValue } - return parsedSlice + return parsedSlice, nil case reflect.Map: // convert any map to map[string]interface{} parsedMap := make(map[string]interface{}) for _, k := range rawValue.MapKeys() { - parsedKey := parseString(k.String(), variablesMapping) + parsedKey, err := parseString(k.String(), variablesMapping) + if err != nil { + return raw, err + } v := rawValue.MapIndex(k) - parsedValue := parseData(v.Interface(), variablesMapping) + parsedValue, err := parseData(v.Interface(), variablesMapping) + if err != nil { + return raw, err + } key := convertString(parsedKey) parsedMap[key] = parsedValue } - return parsedMap + return parsedMap, nil default: // other types, e.g. nil, int, float, bool - return raw + return raw, nil } } @@ -97,7 +110,7 @@ var ( ) // parseString parse string with variables -func parseString(raw string, variablesMapping map[string]interface{}) interface{} { +func parseString(raw string, variablesMapping map[string]interface{}) (interface{}, error) { matchStartPosition := 0 parsedString := "" remainedString := raw @@ -134,18 +147,21 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ argsStr := funcMatched[2] arguments, err := parseFunctionArguments(argsStr) if err != nil { - return raw + return raw, err } - parsedArgs := parseData(arguments, variablesMapping).([]interface{}) - - result, err := callFunc(funcName, parsedArgs...) + parsedArgs, err := parseData(arguments, variablesMapping) if err != nil { - return raw + return raw, err + } + + result, err := callFunc(funcName, parsedArgs.([]interface{})...) + if err != nil { + return raw, err } if funcMatched[0] == raw { // raw_string is a function, e.g. "${add_one(3)}", return its eval value directly - return result + return result, nil } // raw_string contains one or many functions, e.g. "abc${add_one(3)}def" @@ -165,11 +181,14 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ } else { varName = varMatched[2] // match $var } - varValue := variablesMapping[varName] + varValue, ok := variablesMapping[varName] + if !ok { + return raw, fmt.Errorf("variable %s not found", varName) + } if fmt.Sprintf("${%s}", varName) == raw || fmt.Sprintf("$%s", varName) == raw { // raw string is a variable, $var or ${var}, return its value directly - return varValue + return varValue, nil } matchStartPosition += len(varMatched[0]) @@ -183,7 +202,7 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ break } - return parsedString + return parsedString, nil } // merge two variables mapping, the first variables have higher priority @@ -353,8 +372,8 @@ func parseVariables(variables map[string]interface{}) (map[string]interface{}, e return variables, fmt.Errorf("variable not defined: %v", undefinedVars) } - parsedValue := parseData(varValue, parsedVariables) - if parsedValue == nil { + parsedValue, err := parseData(varValue, parsedVariables) + if err != nil { continue } parsedVariables[varName] = parsedValue diff --git a/parser_test.go b/parser_test.go index a1f735e3..2943cce4 100644 --- a/parser_test.go +++ b/parser_test.go @@ -156,13 +156,40 @@ func TestParseDataStringWithVariables(t *testing.T) { {"func1($var_1, $var_3)", "func1(abc, 123)"}, {"func1($var_1, ${var_3})", "func1(abc, 123)"}, // TODO: fix compatibility with python version - {"abc$var_4", "abcmap[a:1]"}, // "abc{'a': 1}" - {"abc$var_5", "abctrue"}, // "abcTrue" - {"/api/$SECRET_KEY", "/api/"}, // raise error + {"abc$var_4", "abcmap[a:1]"}, // "abc{'a': 1}" + {"abc$var_5", "abctrue"}, // "abcTrue" } for _, data := range testData { - if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { + t.Fail() + } + } +} + +func TestParseDataStringWithUndefinedVariables(t *testing.T) { + variablesMapping := map[string]interface{}{ + "var_1": "abc", + "var_2": "def", + } + + testData := []struct { + expr string + expect interface{} + }{ + {"/api/$SECRET_KEY", "/api/$SECRET_KEY"}, // raise error + } + + for _, data := range testData { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.Error(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { t.Fail() } } @@ -202,7 +229,11 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) { } for _, data := range testData { - if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { t.Fail() } } @@ -228,7 +259,11 @@ func TestParseDataMapWithVariables(t *testing.T) { } for _, data := range testData { - if !assert.Equal(t, data.expect, parseData(data.expr, variablesMapping)) { + parsedData, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, parsedData) { t.Fail() } } @@ -257,7 +292,11 @@ func TestParseHeaders(t *testing.T) { } for _, data := range testData { - if !assert.Equal(t, data.expectHeaders, parseHeaders(data.rawHeaders, variablesMapping)) { + parsedHeaders, err := parseHeaders(data.rawHeaders, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expectHeaders, parsedHeaders) { t.Fail() } } @@ -292,14 +331,14 @@ func TestMergeVariables(t *testing.T) { func TestCallFunction(t *testing.T) { // call function without arguments _, err := callFunc("get_timestamp") - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } // call function with one argument timeStart := time.Now() _, err = callFunc("sleep", 1) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second) { @@ -308,7 +347,7 @@ func TestCallFunction(t *testing.T) { // call function with one argument result, err := callFunc("gen_random_string", 10) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, 10, len(result.(string))) { @@ -317,7 +356,7 @@ func TestCallFunction(t *testing.T) { // call function with two argument result, err = callFunc("max", float64(10), 9.99) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, float64(10), result.(float64)) { @@ -345,7 +384,7 @@ func TestLiteralEval(t *testing.T) { for _, data := range testData { value, err := literalEval(data.expr) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, data.expect, value) { @@ -375,7 +414,7 @@ func TestParseFunctionArguments(t *testing.T) { for _, data := range testData { value, err := parseFunctionArguments(data.expr) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, data.expect, value) { @@ -391,28 +430,43 @@ func TestParseDataStringWithFunctions(t *testing.T) { "b": 3.45, } - if !assert.Len(t, parseData("${gen_random_string(5)}", variablesMapping), 5) { - t.Fail() - } - if !assert.Len(t, parseData("${gen_random_string($n)}", variablesMapping), 5) { - t.Fail() + testData1 := []struct { + expr string + expect interface{} + }{ + {"${gen_random_string(5)}", 5}, + {"${gen_random_string($n)}", 5}, + {"123${gen_random_string(5)}abc", 11}, + {"123${gen_random_string($n)}abc", 11}, } - if !assert.Len(t, parseData("123${gen_random_string(5)}abc", variablesMapping), 11) { - t.Fail() - } - if !assert.Len(t, parseData("123${gen_random_string($n)}abc", variablesMapping), 11) { - t.Fail() + for _, data := range testData1 { + value, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, len(value.(string))) { + t.Fail() + } } - if !assert.Equal(t, parseData("${max($a, $b)}", variablesMapping), 12.3) { - t.Fail() + testData2 := []struct { + expr string + expect interface{} + }{ + {"${max($a, $b)}", 12.3}, + {"abc${max($a, $b)}123", "abc12.3123"}, + {"abc${max($a, 3.45)}123", "abc12.3123"}, } - if !assert.Equal(t, parseData("abc${max($a, $b)}123", variablesMapping), "abc12.3123") { - t.Fail() - } - if !assert.Equal(t, parseData("abc${max($a, 3.45)}123", variablesMapping), "abc12.3123") { - t.Fail() + + for _, data := range testData2 { + value, err := parseData(data.expr, variablesMapping) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, data.expect, value) { + t.Fail() + } } } @@ -454,7 +508,7 @@ func TestParseVariables(t *testing.T) { for _, data := range testData { value, err := parseVariables(data.rawVars) - if !assert.Nil(t, err) { + if !assert.NoError(t, err) { t.Fail() } if !assert.Equal(t, data.expectVars, value) { diff --git a/response.go b/response.go index 60c01b6b..beaca56c 100644 --- a/response.go +++ b/response.go @@ -86,14 +86,17 @@ func (v *ResponseObject) Extract(extractors map[string]string) map[string]interf return extractMapping } -func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) error { +func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) (err error) { for _, validator := range validators { // parse check value checkItem := validator.Check var checkValue interface{} if strings.Contains(checkItem, "$") { // reference variable - checkValue = parseData(checkItem, variablesMapping) + checkValue, err = parseData(checkItem, variablesMapping) + if err != nil { + return err + } } else { checkValue = v.searchJmespath(checkItem) } @@ -103,7 +106,10 @@ func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[ assertFunc := builtin.Assertions[assertMethod] // parse expected value - expectValue := parseData(validator.Expect, variablesMapping) + expectValue, err := parseData(validator.Expect, variablesMapping) + if err != nil { + return err + } // do assertion result := assertFunc(v.t, expectValue, checkValue) diff --git a/runner.go b/runner.go index 0af2b28d..6565f120 100644 --- a/runner.go +++ b/runner.go @@ -114,19 +114,31 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { // prepare request args var v []interface{} if len(step.Request.Headers) > 0 { - headers := parseHeaders(step.Request.Headers, step.Variables) + headers, err := parseHeaders(step.Request.Headers, step.Variables) + if err != nil { + return nil, err + } v = append(v, req.Header(headers)) } if len(step.Request.Params) > 0 { - params := parseData(step.Request.Params, step.Variables) + params, err := parseData(step.Request.Params, step.Variables) + if err != nil { + return nil, err + } v = append(v, req.Param(params.(map[string]interface{}))) } if step.Request.Data != nil { - data := parseData(step.Request.Data, step.Variables) + data, err := parseData(step.Request.Data, step.Variables) + if err != nil { + return nil, err + } v = append(v, data) } if step.Request.JSON != nil { - jsonData := parseData(step.Request.JSON, step.Variables) + jsonData, err := parseData(step.Request.JSON, step.Variables) + if err != nil { + return nil, err + } v = append(v, req.BodyJSON(jsonData)) } @@ -186,11 +198,17 @@ func (r *Runner) parseConfig(config *TConfig) error { config.Variables = parsedVariables // parse config name - parsedName := parseString(config.Name, config.Variables) + parsedName, err := parseString(config.Name, config.Variables) + if err != nil { + return err + } config.Name = convertString(parsedName) // parse config base url - parsedBaseURL := parseString(config.BaseURL, config.Variables) + parsedBaseURL, err := parseString(config.BaseURL, config.Variables) + if err != nil { + return err + } config.BaseURL = convertString(parsedBaseURL) return nil From f49a560c025710dc1be13745ec791273a9e3794f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 13:21:52 +0800 Subject: [PATCH 082/479] change: assert.Error --- parser_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser_test.go b/parser_test.go index 2943cce4..459591d8 100644 --- a/parser_test.go +++ b/parser_test.go @@ -538,7 +538,7 @@ func TestParseVariablesAbnormal(t *testing.T) { for _, data := range testData { value, err := parseVariables(data.rawVars) - if !assert.NotNil(t, err) { + if !assert.Error(t, err) { t.Fail() } if !assert.Equal(t, data.expectVars, value) { From 957b899658dba3a7a948f5dd8f48bcbff21671a5 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 17:05:22 +0800 Subject: [PATCH 083/479] docs: add key features --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a5792cc9..4cf7a807 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,20 @@ HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will ## Key Features +- [x] Full support for HTTP(S) requests, more protocols are also in the plan. +- [ ] Testcases can be described in multiple formats, `YAML`/`JSON`/`Golang`, and they are interchangeable. +- [ ] With [`HAR`][HAR] support, you can use Charles/Fiddler/Chrome/etc as a script recording generator. +- [x] Supports `variables`/`extract`/`validate`/`hooks` mechanisms to create extremely complex test scenarios. +- [ ] Built-in integration of rich functions, and you can also use [`go plugin`][plugin] to create and call custom functions. +- [x] Inherit all powerful features of [`Boomer`][Boomer] and [`locust`][locust], you can run `load test` without extra work. +- [ ] Use it as a `CLI tool` or as a `library` are both supported. + ## Quick Start [HttpRunner]: https://github.com/httprunner/httprunner [Boomer]: https://github.com/myzhan/boomer -[locust]: https://github.com/locustio/locust \ No newline at end of file +[locust]: https://github.com/locustio/locust +[jmespath]: https://jmespath.org/ +[allure]: https://docs.qameta.io/allure/ +[HAR]: http://httparchive.org/ +[plugin]: https://pkg.go.dev/plugin From 098c5b9c94b79494d9dfa868d940ab63f047d3cf Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 18:09:46 +0800 Subject: [PATCH 084/479] feat: assert with startswith and endswith for string --- builtin/assertion.go | 29 ++++++++++++++++++++++++++ builtin/assertion_test.go | 43 +++++++++++++++++++++++++++++++++++++++ validate.go | 22 ++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 builtin/assertion_test.go diff --git a/builtin/assertion.go b/builtin/assertion.go index 99782626..ca290aa8 100644 --- a/builtin/assertion.go +++ b/builtin/assertion.go @@ -1,6 +1,9 @@ package builtin import ( + "fmt" + "strings" + "github.com/stretchr/testify/assert" ) @@ -15,10 +18,36 @@ var Assertions = map[string]func(t assert.TestingT, expected interface{}, actual "contains": assert.Contains, "regex_match": assert.Regexp, // custom assertions + "startswith": StartsWith, // check if string starts with substring + "endswith": EndsWith, // check if string ends with substring "length_equals": EqualLength, "length_equal": EqualLength, // alias for length_equals } +func StartsWith(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if !assert.IsType(t, "string", actual, fmt.Sprintf("actual is %v", actual)) { + return false + } + if !assert.IsType(t, "string", expected, fmt.Sprintf("expected is %v", expected)) { + return false + } + actualString := actual.(string) + expectedString := expected.(string) + return assert.True(t, strings.HasPrefix(actualString, expectedString), msgAndArgs...) +} + +func EndsWith(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if !assert.IsType(t, "string", actual, fmt.Sprintf("actual is %v", actual)) { + return false + } + if !assert.IsType(t, "string", expected, fmt.Sprintf("expected is %v", expected)) { + return false + } + actualString := actual.(string) + expectedString := expected.(string) + return assert.True(t, strings.HasSuffix(actualString, expectedString), msgAndArgs...) +} + func EqualLength(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { return assert.Len(t, actual, expected.(int), msgAndArgs...) } diff --git a/builtin/assertion_test.go b/builtin/assertion_test.go new file mode 100644 index 00000000..328839be --- /dev/null +++ b/builtin/assertion_test.go @@ -0,0 +1,43 @@ +package builtin + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStartsWith(t *testing.T) { + testData := []struct { + raw string + expected string + }{ + {"", ""}, + {"a", "a"}, + {"abc", "a"}, + {"abc", "ab"}, + } + + for _, data := range testData { + if !assert.True(t, StartsWith(t, data.expected, data.raw)) { + t.Fail() + } + } +} + +func TestEndsWith(t *testing.T) { + testData := []struct { + raw string + expected string + }{ + {"", ""}, + {"a", "a"}, + {"abc", "c"}, + {"abc", "bc"}, + } + + for _, data := range testData { + if !assert.True(t, EndsWith(t, data.expected, data.raw)) { + t.Fail() + } + } +} diff --git a/validate.go b/validate.go index 4244edb7..d325fe22 100644 --- a/validate.go +++ b/validate.go @@ -32,6 +32,28 @@ func (s *stepRequestValidation) AssertEqual(jmesPath string, expected interface{ return s } +func (s *stepRequestValidation) AssertStartsWith(jmesPath string, expected interface{}, msg string) *stepRequestValidation { + validator := TValidator{ + Check: jmesPath, + Assert: "startswith", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, validator) + return s +} + +func (s *stepRequestValidation) AssertEndsWith(jmesPath string, expected interface{}, msg string) *stepRequestValidation { + validator := TValidator{ + Check: jmesPath, + Assert: "endswith", + Expect: expected, + Message: msg, + } + s.step.Validators = append(s.step.Validators, validator) + return s +} + func (s *stepRequestValidation) AssertLengthEqual(jmesPath string, expected interface{}, msg string) *stepRequestValidation { validator := TValidator{ Check: jmesPath, From 4c8bb400a16caf1d5e033dadf9885d5597e6555f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 18:35:25 +0800 Subject: [PATCH 085/479] change: add unittest for EqualLength --- builtin/assertion.go | 3 +++ builtin/assertion_test.go | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/builtin/assertion.go b/builtin/assertion.go index ca290aa8..30ecfdd5 100644 --- a/builtin/assertion.go +++ b/builtin/assertion.go @@ -49,5 +49,8 @@ func EndsWith(t assert.TestingT, expected, actual interface{}, msgAndArgs ...int } func EqualLength(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if !assert.IsType(t, 129, expected, fmt.Sprintf("expected type is not int, got %#v", expected)) { + return false + } return assert.Len(t, actual, expected.(int), msgAndArgs...) } diff --git a/builtin/assertion_test.go b/builtin/assertion_test.go index 328839be..632dad30 100644 --- a/builtin/assertion_test.go +++ b/builtin/assertion_test.go @@ -41,3 +41,23 @@ func TestEndsWith(t *testing.T) { } } } + +func TestEqualLength(t *testing.T) { + testData := []struct { + raw interface{} + expected int + }{ + {"", 0}, + {[]string{}, 0}, + {map[string]interface{}{}, 0}, + {"a", 1}, + {[]string{"a"}, 1}, + {map[string]interface{}{"a": 123}, 1}, + } + + for _, data := range testData { + if !assert.True(t, EqualLength(t, data.expected, data.raw)) { + t.Fail() + } + } +} From c40505955508d2ead552cdf8c864b50fbac44d15 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 18:39:34 +0800 Subject: [PATCH 086/479] docs: add example --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++ examples/demo_test.go | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 examples/demo_test.go diff --git a/README.md b/README.md index 4cf7a807..d0902090 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,75 @@ HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will ## Quick Start +### Install + +```bash +$ go get -u github.com/httprunner/httpboomer +``` + +### Examples + +This is an example of HttpBoomer testcase. You can find more in the [`examples`][examples] directory. + +```go + +import ( + "testing" + + "github.com/httprunner/httpboomer" +) + +func TestCaseDemo(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "demo with complex mechanisms", + BaseURL: "https://postman-echo.com", + Variables: map[string]interface{}{ // global level variables + "n": 5, + "a": 12.3, + "b": 3.45, + "varFoo1": "${gen_random_string($n)}", + "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function + }, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ // step level variables + "n": 3, // inherit config level variables if not set in step level, a/varFoo1 + "b": 34.5, // override config level variable if existed, n/b/varFoo2 + "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). // request with headers + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). // extract variable with jmespath + Validate(). + AssertEqual("status_code", 200, "check response status code"). // validate response status code + AssertStartsWith("headers.\"Content-Type\"", "application/json", ""). // validate response header + AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath + AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step + AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string + httpboomer.Step("post json data"). + POST("/post"). + WithJSON(map[string]interface{}{ + "foo1": "$varFoo1", // reference former extracted variable + "foo2": "${max($a, $b)}", // 12.3; step level variables are independent, variable b is 3.45 here + }). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.json.foo1", 5, "check args foo1"). + AssertEqual("body.json.foo2", 12.3, "check args foo2"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} +``` + [HttpRunner]: https://github.com/httprunner/httprunner [Boomer]: https://github.com/myzhan/boomer [locust]: https://github.com/locustio/locust @@ -29,3 +98,4 @@ HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will [allure]: https://docs.qameta.io/allure/ [HAR]: http://httparchive.org/ [plugin]: https://pkg.go.dev/plugin +[examples]: examples/ diff --git a/examples/demo_test.go b/examples/demo_test.go new file mode 100644 index 00000000..f421b3bd --- /dev/null +++ b/examples/demo_test.go @@ -0,0 +1,57 @@ +package examples + +import ( + "testing" + + "github.com/httprunner/httpboomer" +) + +func TestCaseDemo(t *testing.T) { + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "demo with complex mechanisms", + BaseURL: "https://postman-echo.com", + Variables: map[string]interface{}{ // global level variables + "n": 5, + "a": 12.3, + "b": 3.45, + "varFoo1": "${gen_random_string($n)}", + "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function + }, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ // step level variables + "n": 3, // inherit config level variables if not set in step level, a/varFoo1 + "b": 34.5, // override config level variable if existed, n/b/varFoo2 + "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). // request with headers + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). // extract variable with jmespath + Validate(). + AssertEqual("status_code", 200, "check response status code"). // validate response status code + AssertStartsWith("headers.\"Content-Type\"", "application/json", ""). // validate response header + AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath + AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step + AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string + httpboomer.Step("post json data"). + POST("/post"). + WithJSON(map[string]interface{}{ + "foo1": "$varFoo1", // reference former extracted variable + "foo2": "${max($a, $b)}", // 12.3; step level variables are independent, variable b is 3.45 here + }). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.json.foo1", 5, "check args foo1"). + AssertEqual("body.json.foo2", 12.3, "check args foo2"), + }, + } + + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} From a5830cba0be5786c7c227f72399a1cf9554beda5 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 4 Oct 2021 18:43:08 +0800 Subject: [PATCH 087/479] change: replace tab with space --- README.md | 96 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index d0902090..1556df8d 100644 --- a/README.md +++ b/README.md @@ -35,59 +35,59 @@ This is an example of HttpBoomer testcase. You can find more in the [`examples`] ```go import ( - "testing" + "testing" - "github.com/httprunner/httpboomer" + "github.com/httprunner/httpboomer" ) func TestCaseDemo(t *testing.T) { - testcase := &httpboomer.TestCase{ - Config: httpboomer.TConfig{ - Name: "demo with complex mechanisms", - BaseURL: "https://postman-echo.com", - Variables: map[string]interface{}{ // global level variables - "n": 5, - "a": 12.3, - "b": 3.45, - "varFoo1": "${gen_random_string($n)}", - "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function - }, - }, - TestSteps: []httpboomer.IStep{ - httpboomer.Step("get with params"). - WithVariables(map[string]interface{}{ // step level variables - "n": 3, // inherit config level variables if not set in step level, a/varFoo1 - "b": 34.5, // override config level variable if existed, n/b/varFoo2 - "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again - }). - GET("/get"). - WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params - WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). // request with headers - Extract(). - WithJmesPath("body.args.foo1", "varFoo1"). // extract variable with jmespath - Validate(). - AssertEqual("status_code", 200, "check response status code"). // validate response status code - AssertStartsWith("headers.\"Content-Type\"", "application/json", ""). // validate response header - AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath - AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step - AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string - httpboomer.Step("post json data"). - POST("/post"). - WithJSON(map[string]interface{}{ - "foo1": "$varFoo1", // reference former extracted variable - "foo2": "${max($a, $b)}", // 12.3; step level variables are independent, variable b is 3.45 here - }). - Validate(). - AssertEqual("status_code", 200, "check status code"). - AssertLengthEqual("body.json.foo1", 5, "check args foo1"). - AssertEqual("body.json.foo2", 12.3, "check args foo2"), - }, - } + testcase := &httpboomer.TestCase{ + Config: httpboomer.TConfig{ + Name: "demo with complex mechanisms", + BaseURL: "https://postman-echo.com", + Variables: map[string]interface{}{ // global level variables + "n": 5, + "a": 12.3, + "b": 3.45, + "varFoo1": "${gen_random_string($n)}", + "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function + }, + }, + TestSteps: []httpboomer.IStep{ + httpboomer.Step("get with params"). + WithVariables(map[string]interface{}{ // step level variables + "n": 3, // inherit config level variables if not set in step level, a/varFoo1 + "b": 34.5, // override config level variable if existed, n/b/varFoo2 + "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). // request with headers + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). // extract variable with jmespath + Validate(). + AssertEqual("status_code", 200, "check response status code"). // validate response status code + AssertStartsWith("headers.\"Content-Type\"", "application/json", ""). // validate response header + AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath + AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step + AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string + httpboomer.Step("post json data"). + POST("/post"). + WithJSON(map[string]interface{}{ + "foo1": "$varFoo1", // reference former extracted variable + "foo2": "${max($a, $b)}", // 12.3; step level variables are independent, variable b is 3.45 here + }). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.json.foo1", 5, "check args foo1"). + AssertEqual("body.json.foo2", 12.3, "check args foo2"), + }, + } - err := httpboomer.Test(t, testcase) - if err != nil { - t.Fatalf("run testcase error: %v", err) - } + err := httpboomer.Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } } ``` From 4ecdc59ca33962566709de2a3fc30c49324d9e44 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 5 Oct 2021 12:45:38 +0800 Subject: [PATCH 088/479] docs: add flow chart --- README.md | 2 ++ docs/flow.jpg | Bin 0 -> 148937 bytes 2 files changed, 2 insertions(+) create mode 100644 docs/flow.jpg diff --git a/README.md b/README.md index 1556df8d..851d0efa 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will ## Key Features +![flow chart](docs/flow.jpg) + - [x] Full support for HTTP(S) requests, more protocols are also in the plan. - [ ] Testcases can be described in multiple formats, `YAML`/`JSON`/`Golang`, and they are interchangeable. - [ ] With [`HAR`][HAR] support, you can use Charles/Fiddler/Chrome/etc as a script recording generator. diff --git a/docs/flow.jpg b/docs/flow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c8484916e389febb10cfaded1675112150cf39e2 GIT binary patch literal 148937 zcmeFZbySt%wk{4!8fgp!2|*AkB^M0>(k)$zAl(b;7U>p{4nd@q?hz5gD^P#N&Oi#O)`%x6Ax&M!noNtyuf9v&JR8iA~gq#7C;4ka2I zWE}?!e1|L2E)x8Q?xZID46US}Y6A_87EM-C{DrIGpIO`ww|>;N?AyJ0`BDsB*qFMj z;E|A`cDOsO1Nx6sed2cuu>`^L3gqY!%B@{!jOLO$?UH1%zE49~aL_}Zdx|MQIKo)p zW3)5%+>5~P61mwYXrzc3KIw1da@lAym__b72@!J5j%hTP+Wt(_Gt!D51%Gg`AvmQPij1MUN#|$NBd@IS#EGi{HV4^WXa5XlpSVF*I}x;(zsz zUn+(SZ&yfI9Lc|W67^xeCN~iOdJ|A9TV@a|6Iaqt4ga?uXP_L!{+CllO@NjO?3m-? zqRGEr8F-v_(pTi)I5hAGKW?;jG}#>{`hV;3Zmgq!V{5@9U9!F=0jV`miht{I8Qx5y zf5*st#grfnl*{cuy05hyUVQ6cU&Y6Z^_cXktYZx7-^wZ~M*JKjaGsTulglyfO&D(S z^599H6TPE zc84Qjd%F7EThun%Fw-bm{r5 zm|_Co94m9)8kaQnIN4!ynRCm#NiFni4vSoy3t{Bvi)l!Ga;Zg2lZZOrbew4G1zh^T zYj>Jl{$*$6eNM~KM{YZ{Ykh1ytuNbyNe+H+c&paA>>3tX+GmjaRa#B-91e(bl$#AG zF;L&UFYw|`-$y!|sfv*BJIH~4m5$u2$$aJFe`mQ_zU(@p*1w*V8?-&`q@s^wRE&N4 zy5rr~heIU?6&52V7pMDWW&_kiE87{aiy<=B#bQ_Z(#;W;SNK69m6B+FP++^ndFCUU z*W3x(c1WEyGMY|80l%bX5$~*($eZ@Q01U?=EZYG7Glj30t$OA`*ljjW0%Ndu3lNhH>z+-Q{=85jyCir?XXTDDa-P zsqXGe;!^x%Rci7xLK=(SE0 z%NB$p;v3-hI+yo&N;3uHtq)X(kZ>CaU;lv)Ekw+=%8`;C65@}1cIh`MI3*+bSMTan+or+R2k9Wip=M;fc5|#|sSfY|OqNTp z_NTD~U=vkQ`(K)Q1(%h)ynAjm97 zh`wUqA@T{VF<~4{+NcW$V8h}R(vK4;c7D8Vx;0*W(8E$Hb9IRxVwSb~BV;&NaAknsgD@E{3w0{B8?8 zyVa&Kjt9(8>^7S&gVS1nd}{!^C1<#O zAD>D*kD_LymyY}(P3^|e{H&%&*KgXkUhz$TD)UI5a-!;|=Q{tFAFhzhE&G^-Wo6D| z_ZBjlUKg^|8I^dxQ?JaZl==u!X@iX%s0wz~qnSR>s4mXs28P`t3Jm+AJoj3c?I(l@ zBXw_khr24OD6kNm;JdwF+2}pAN$|stV4tmt-vwZtIUc}@pEbjeufvK_hyvDo0Z#(W zx#QC*!)7c${wzcEMR{zLMK+Lt1EG|byVg4QRD8R|h@P^s_Mirf;!E5scuX>{5*XI= zz}NQ&*l}$ts)F~|*hjmDZ+DBi^gn*D`3d2bp0p>DZ

-mY4{ve38UVH{_b3@8rNx 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

#NNA^g#4o$I^QmbkE^3%urPQ+<`Lw@p81 zA=hAg>)QV)OEIu#?mL}681T+~X{9p!XxRF@Tuysl(0SF_OLM`C3qVlb?q%MU~D;S z1}IbVA4UXtIyTE@0a-BnUW9)WxR}_sV%owBhE?U#a_7f38zc=MlO0 z&fU8b*J_(I+k^JU7Be)S2Q9`pVan{I{}JG^9r4fI$gZ%j7KzA*n5)I1hrA6Y{1bMH zfi84?IR&JS#4P&4cYY*=zn(bv79l;N*}9{sHKBSUXfMF~7|b~{{SW_t*nxymAZ8FX zD9XD~xrh!ReY*SK%wB>C;fvza$8;-TLQ)W&q8d*7DpCX!ud>v1$gN!Vw~yVB$Onr7 zSvp+`YUI~HltGGOgJ)4{?T==hnLC&UA4c-hf^^oIgp?_)LZG=QYeA=dSLGJPIy@XGY4x-dUln$_nyHm;n zLYI5m_5o4(o(Ayc)zexLd1tcI2y#D)bHn!kxCd>X!=T|w&x$O8P7W;1H~QzE|E`rS zCR=joa^O^xxWe{%;pD8VVdCR9cmK~js>uz}la9N4l`_D5NgqC{zAEI=mBC6|Y zN3LEm6;4j0svNG!RHFyKBJ~*7)If6bsun%St(b$+%8+UTy)Oh;3l>@HO9~tg0t*`8 z9H6HlY?R1vqI?lbS?799B-`_U*X?aXXZc?IaM;cSdQu+0=02I2C<&kCJe)*^&&6X! z%>TVOPeJw16N!cKxLTolixbn~kLA@M?104+fFEUhhfzcK6m+}@ zGX(991zwVn<4Fw4SAfPoHT!BI^ z=awL{9*0UOASf{&K__U;t^{%qJqvyW(j5wbx(Y$h*5vT9!_<7D90zYBG7VWeMLT`R zpvelVQ^N&EAc`tD=dbk7O{YRVf<0fR!#tIx%&In4-+5?v9X@_0yv2C_ynGE8t@#wWb~ zInF?VST}kos5{s2)}ttND}q-z`5qjv|IwCP!h@!#(7yY+R1qe5Ul#ts0stSywZ>|c zpAVD$y?XGx+XjYDlj9_(7YyY*5JZ0RYnWSH7ezNYDU$PDNWwHJbz0n zgsk}njmAo{s-E~zmgew9XU8=@(j(Gc%752CYxkdu<$r(rbC?KdMCgwYql`kC(Qv!U zF*AKIW#tCV84&ap{m}#noviQdar+_;sy!9WqnCah`V>4kYnmWvd_W*wER_KAYErKU z0RsP*j8l~zXgFRvrvC<4Bl%Zz9nG>)fg2XL!xhTBNR^fJzgCXi1=o3v>EI6oHj-l1iT8uph<@0OwmskSPgm`^ zR%ux_8~#`sY4aH%U!lZO=aD)0UUy=zbi4f*Sb_C_bu@AQs4X4GU6VWQ-$*qz2+I!T zLld|aJZo|iF&lR99E95jI0YW=08G6$x`%@l5_{JB&PPS_{h8X~TCucNj1@`W>D|v$ z14Euk%HY4j0q^B09^#sqh((6Qb!MNr5-1T6o#5-7c&=)3@0g;3MbF)PSav(|kD~ga zL2%k}lDM`RT2McbZyrPp=U;Y>U_gFh`WS=$$}zISH`H$hPB!KtSiYV_pKQnH9;EC7 zb0@sieX21tzorI0P=K?eID8@XSGjqKJ8eD6rF%geHe;=V7{Nc@PNxZd@JO#N4qFLV zXt5(P3nHXC#1mZz8GFB9f_jYo%s?DeTDV=~X5YXu$*V_~+f}9mEaZ2N$4FhAN91Tn z#*_b41?n-G6@>=~I2X7jUlRmC&K-Gk0uv$)ENuE7AK}ZO&>W?7_B}5OGl%G5zpX#? zuQ(ln6JDl6#%#Nu*ms{Sjd zJ%{Vou2}))Ho4#{ng2g;EBIt1gs8`Te&FAU6!jQ@Uc7EO1^SNjPdz+)UNi`!bLbb> zSK?DI|Fa7Ta8m*pfu3(h{?RV)@DhlVY9O(IdtUO+>z}Qnh$A#l6biG12p;^gM}M|4 z!W%SnUmuAB=Sr|cb!0fVs{sQG*9kEWstIufe|9>i0d{ro0+q55=wL$<&X)MJ0YD^J6nQ}aWo=qYq zu?MsYg9f8k!#;ZP)K;A;f%l~>VWw9??EA%na{W;f4!(|LQAfxXC26*xC86vF4 zSzzh@Iy>KF;q#l%IY;X>BIo7e=1E}k_QU^-XsXWlY5*L~yS(!#`ggdx!k9%?W4Oyw`!JLs0)Vz61`t znI-bo{y8({gUA|wiI;^nJOFD5!zo^cqs$e%J*Zc$Y;4Bg5^JuiX< zG>MMmLvLv))vnB)1(RTCqWjNO?z-m4zCD87T#haQu@dey z<6aib92+U5T3*8MUQeEGBvCT__OXGwlL*cV9&t7o6F|dke#(#4VNd-C+To%1&EpqE zKT(uS8ITzHuVc^&^5QT59Gy#;z^wr`##4w)bv^7wo;DGT1f_(SdVxR!BKWn?y|Jeg z;7q4xH3MB^`hBU5v-*igmgq2_KHZuK%ZNMn{~TGWh^sW272f?ZO1P@SfU~%?&(|wOTGaTwVy< zoHH2bL2bz`{jc}YAqb%)WPe({`kn<`5i^&-uQdKCUeAI5P&b5zJNFH06ZwFz>B_!{ zNdNmi7sv>B1Bz370EVG5%m+>_8>U_GyxK%EIH9n${NF(k9U}M*W|a}7?*^JRw|+X- zoe=&LVD!)aw~_;jRyv^I*LHOZD1tjF z`jbB+mNFMC<$9^vCv2M&Scah=kBU~pEVc)F4QJevC@?0%6Jmx~24Yyb9NYB+HUXLRsB2&uuVQo{0&H)eSS}h4TbQG5Y;x}{LeBW z(B{tRgo?}m4s7{~aKam_UlmRKK`Pj_tfw^)ABl8_7YibNNsYCU$cfLpG#1HIEnkez zpMQXm&|^I?lgANV$`B|KSUBB&??HWZ_ty`0u5awmf8o`&v-4$v9)IjPzb^4ld4D-( zc<+dMM63Ey%sUrw)08!9jnnftTu1YXDs!UdFME()h1T32m@-r9zYX~TvFqJ4^<3x=Qu9+z z{;!l)ObXbzy$mYm26>%emJMhpFPz6f04V{T0<(}72*D>__`^MB?@m(+cwjTRcxoRr z)STjZJ^zhkgb|r{jP6oNod3k@6uhWmX77Cc7)#Jpe)Q|UEY0DT@bKTcJHLf~F2Dgb z2JP6{!IiAj_fwANh8{r%h#=U4kO`&SlonvINd)P#VPC#{IpaJg#s~Vf&db}u8o&CF zo%{JlwNHH&^yo4`0X1u37%Ak83GMXXxEIU%)(;>(ow+-k$8EIlNgDaWVq%VW-G&q= zVjUx~seS1<`mD{$_KytHPTPVA5q)JIzCj_y^n=2zXSo-|&!dy`{;N0Vzf5L_zWwxUHB>O^QkTP}dly4L4s7Jqe!SSU zK#90~e3^`*#%RAYrrgKW^PK2Qxic(A3Pp!B+lR+z&wqs>9clGC8`0(?5Q*<^`aI`) znf`sHt1HMwOs%*>-Tm(C198l(8dcso7-LYT{qw_WPIiwZCh2SrM3+#VWXj8nJ~_P$ z84Lpj|09*>=La32!Ol1GUMPH1xWRw&Nae9HG#@wAzcV0%D(xfKc9$i=M z100sdxj2>?SDFWu(k0OGD^mnX)FXISW&JpsdtbnYyZ|!9IG~!~jkj&ue6I~`T?>i| zS7s%k{IR6gXhU=lF0Y@!8{GKO6xofUo;-OmoDlkXV(jMHu~SasW^vx1)7LMRT8l{Y zu9fhQ{uGx9Y0ShJQ|@9o@KtkAym15|Gb6aM7a1>&=XR zsf)J)!Xn;SX#APj2+)|J8sy#pRH$UwGFNMNBrP+1txI*kZ5w{^~_Yn@@WZXG5_^Pjaq{!Sb6Qrt&vxm+3p ztza6LD2V=Ef0P4MFhs5O^2ZIh*_MLF4(%bpR`^C!ak+@wde4a<8f{=qBRXZFGu_vi zaRMkjPQo%r4rC#&!_chYSBMxS0zK^641`fk(kd~hZX65WE6UfFv)9R&3+0Wo@gBLq zH|eks{EEZjBcBhlBqBEbgAJuQ+RMo?7OH$lGQ7Gtdyb=@{Ip295$~?G!o8d8$e1vL zkh(|#Bj;Vy(B%{;>wCR_yMNRiVg;hsw%YoU)rHkBc}dM3i>K8>9Wm>_%I$0_zjq5C z;=fb*yJ?kp$gq~4jX3a#i$}4|Cc~@#IYow7Y3w(v`9|Z%Ai)C4voj}rSYMQa83P?@k{UKWP2}&oS6Wpe#@%?5^}Q?5rg+FhceFwO&3q@tS8L~K|0_VpT-SF5zWqsSovF8Rg9(eWhrck>I89v}kcpSh0 z$E!lgf6AnhUdOxpo|EeN#R4c-C$b5w4GS6nOm~_25WfA}=sxfatFEG%HdYseD7&UL z@mJWct`v_2a!0vMcIDa?L<3TRmN?9Ghu7wsOLK^W?;;)eEft&DhkBmueD-eaQ`G;R z(fS)ys&=dg79oj=-dvxpL~Nh#&_mKyc_P#{GuUE!PEqp6Eg)9dB)-*73;N!vP}FqT z-u!9F(J+EESz%ku-rX3p3lwl|gms}h&}tC4BN0f|-*^YR7g%+TAKI>vZ?)56b5>jFN;8K8pQ&b{tPA3K)7G^L;+O<@e^+Jx2#IYx?=p=+tEu_H z5U>YU^I|-~ z@!-t+(mMm~89GsoKOZr2?dc3_n_;(d%|X=DeemJcs38!xk0c0CTdR8y)R|lnat5(` z5JR?~C=}}{>ZiQVPc-pt3_o@`E$#6Qss+gm7L6z7Xt87ol#&Xyw91HBEgLHPBOF(q zq>JBKPYw;PuDnb$FpS3zJ?SknxZk8wt+`9GrnO7^8oTu9tbE;k&{ARZg+&tSRLpP~ zThH|1vzaQMu8b;P54Y!doq))1-#mO;vPJfvj4qbMb{h zkbp{I0k0trbd?*3=J06ywWjlKqT6r_e{IhPRHNEB9LubW2f5k#)I7yL?E;ta_fJeR zRkO!49Ov$#jyGw6Y^!IwV*07jRJ-j2mnJ;lWbLC&!-f4^uGfdbIOdbwBM~`qpbj1W zBN5YZZv1^$ZpczBQjniH4zB!wYJA<>jXtb+x-jY(;0bxg+N|M#%!`mZyhu{jtvz={ z%8$fWxGvE5BqqGIE+UrM;(G+{+fwjDTMI1)!N1sbxdY!YaIiW&e*e>{IV2}I{)Yat zhqu(uqW-C)F&YU;&pUmRlt1yQ1nPB)8eF)kD6&=`GL{fAmV1PkX4!A$REqg6;{ApW z8_Q2MI9#!I(%ai?zvmC6>}>6lR4@qJ>|q-fEEVDeQ@ z?xc!qu8g=^ry5s1t0DdP{z$~`e&PDH*>jT}7qKa6vIbT82?`q^FH}OMI-Kpu>8;RA z`rW0S^N^wzzFoJGR5KjDFunTgyI}Z2gVDEEI!%9KhSl5Rf&@MTi=2a@r6;Vq8lT#C zWeV3^dL+p~8EKvVxhjv+?!MUMcD8t`PX7q^N!Mi#y{X0V43SH}V&*(8dqITfvgS3O?9Ozrw6BE<($&j zgBONG^sh#BvDqQ_t-{k~Tnz0PKpmPpw`c!}p~i+yNWBgp)gw|9pG7_J8Fm3$U2@0I z4%Gy7f#cn@Bf4fAMTcWbvik0p6iTU74IkC3j@asm7rg#&G(axJfV6(z%#*ke*=l^M zcB4##l?}^nv~XOWd^1tE=zVc|0wVW~#e0 zepucMB9?0ZXU4OQ;R%P_c~}(;$*b4*TF4oo#YXlZo{g4`FJMlp;6VHgHHVN}%26i1 z_ACzBiBBiYU*WbxzZoAJA9%PD!#UHpdJjKJHFdT+W!|ULH7Q{{YhUOvy@X1p)|Xln zP!@v9XA-Y zyC;t4#vZ)I*=QKUQzfnKorzT`i1f?eXNEaLTibL=6OXx^5OJj0K`Eu@j%EQ}lWxh< z8`aWE!t3xNQ<2Qb@a^d!fuD>|6MT2GLMRm)5ngP0sl(@WW?j=gJ4=rxkECR@%EkM| zT_<@aYB@h`_KJ7)pN{txm{3sL_LWc4FXttw~P0u#Db^vSx8y&&CVMguq!m zGhto#ZFcE6I6qRwHP-tK9cJFRGp;yuv>ahh!M*>QbG05uckw9)-^gR$eIZV*x~GVY zKU9g&LBDp`4&N}E9~~b!?bHFAvvzNZL6Q~A`9){g@<1%h-GetpKuyGY{@5GZ(|ZQ8 zSX|y2-B}iT<=akM@8Wd*Y1jI`V&isN7+O%ZnYW9t(7#ukxeZpRD12ud-HL6a&HAcy z5bW=VtdI9}m+By{GC-z^0NBAU(+3(%8ul@#2dOg&k4b zNZfX#;J}T3D%WMf&}A!m3L=ny{qct;xY~xDEdYu}#;k}MI7pz;gZv3pyz;^kI56~n zr~bsv1jGf3;`u1o?;q7H?sNOW%Ex^h*rA12^O9`6$q#V@I}MZ8PMU!V3S!OpT(7QQ zg$muDugS+XZro2Pw|S)_MYh?IwyKLXRKZqubQj@;^~@q7tkpb|PH>*2=h-~{?8T*Q zO5bZ&Z(PMnU<~u?saG2dq8`DdU4&nqH%!FD%N|>z3XP}UONUfPgfq~*rY_9JdSN?a znzXmnR;Jk;PBAFu$a}pp`gqI-ebPX+d-XL-K_ml*Oy|YU{OhSpODRTzR=4II2R;O^ z%)CHH?a;e_jbp|gJuE~+f;`7sS-j#&4^Tatpc@D+8WdirzLTxddIn)z)m z*Ywa)kL}I()>jyUEc~BUp@JvktIruT-$F(;B!?|%Ml;+-g}*`~P~sFG?ATtCS2wTEwy>BT81Lh_FsQjz)UF9H}yF0b6gokH_tJs6wMNATR!o*&u@&F6Zllg zPQi!tXmC_k@I?P1kDqnUTftX50JD4vR^K=oB2qOVJv~y*GwsrmwQn;?406 zYOCaPMb=LzbHkXiDPpVNKTF!n6SEF(UEKiW@wnGFtsk{u6^!3~;taZCJqOjrp%YXo zam>w|OJ?flXKs+rFwnV?go-yKfZ@5utnF7tTgpU&zThKA)u*~Qk3P9jT0%2Cb#9b! z_;R|V8&o~4(cyVEy{0-bC^yu{nnSKoeI4&(-^W?NLpsV>8M#{|`qYYE1^E{tvD-SC z_vV2kd-SU#(u|^;Q~vetI{b*upzHL$jNvyX-@3!XZ+k)74eq%(Q)5{aBk-tDfBDe^DY0xe(dZ54&kt!>5>n3Px6vNB5Hq#QYLrS^ zWoqmDs6iL~&qMCiJ0&!&PrL5Cm2bwCx(GDXRDT`)%+Q8)dKd%Xr}g!goj8rHy2>f^ zf}T%y_MCKIO;Kmv$>#HJwL4e1LR>%B?^W2;RC$mj!1f|p_i`L_m2QH~0=940Gq3Pu zP5Cqty)I_H!B-g@4=W8~C~BuKGv32Qp^Rxz9a#xN@-RA~`8)wl)xpPU>P>~BQ5p4l zvU}-se@Q>3qM7u1WtF;q0qO?3olXh41hv~0MxMsq)KiLKX5d`@t~Al4b-2C3zxjND zGWTrFcN!i2EGFW+Za$)Zrqb8UouA^_rybHUuO&z{5mF)Dy>9D5gWX0;aaa#e{fecQzlZHeKqje9!mWZUd4HyfASQv~bkh$Fcdro@3saE|kd7>7F~$GQ4s$Xbi7oz*RUsuXv?qtt&tSUXdkE zHX_saiN;7STgRM9nqeqTlfqzNiq%6?urjB4sUB-%lmE#I>8D zf=>F*R%F;o5psjS0mak@#_-jh6|moh!Fs-2Yra2B_+@z@B4K80gIaaj>Y0rt4FeY; zuYT*-h=k!zI_gWX+ zy@ZcAuohjD;c8*-SCFP2q*y;CwtGI_8~459+e$F{D^aBrKn6Et4!=BJtJ>pEjirIT zm`u=!Ri;LY?vJkhsZe5XiHQ7N>7hMH6n^kpA8k^}Hg6Q$;t1k6q1TZXiS2%Efe!|A{edzgw-e4fcfO zTryoSw2*{nmG!;J*MB}{Iy&_5@|B$DrKi7RG#NS^y~!nS(qw2Z2w72Ys%|}q!L*9- zG4YB4_KTz`YZqL=J;h9?oT4?7pi10aJ*cd!UxoUi{X3EQL?K;$z>AC5h5AH#TW?6W zMsu7L*?(aCzCvHn~@;>Pp!Ce!Q{@Z{(}nx56ai+{LmA5US8h64E4EF zXa&uJa3Z@YGUf(p)}`BRS}vE>bPlvuKH!V(r;^qLwqYc>j;WfJk5KdUjI^7VUaUmj z_K6<+;sI+>op==9!$xD-x&j8aARxuxFlB}f*WMbg0kuM}t4ogmW!?~=8An2WMorV0 z(u@h*8Mrt%)vy9pm71Z<{!gND%ySyS^NRxcY4~QYQ8bo0exgAIHiqr@Eftz`9TVaz zdslw-xr#jQe7Tp4kMPF7IV7T4xK~-_{mRuhm%K;m*!T#V`>X}loT}cq5|uKQj0%JN zQ+BrdR^wWBI01UUmM!PN>Xv6StiOEuS+wS@&Gu^>E^G%0d1*SBtHCQFubw4M=I+{~ zg1s=)6c&mn#&yft2VbjeqZJClWsVAGANVnI*z#m}b!*e=`dtoevBgf!pL;~(Ph%yt z+0K!_PX3`c6Y1PwZN`n~gCc?P9mD1aqx*=BSWjcKp1TPPPjJ1q_jm=};+UU~wfE-J zv&-2ZVsU#Kt{X9!MExjO>iqH)zB8_8kR9ykrlzj8{ z`s_GVWri|dFfZqKgjaNQ%QK&6$)WiT-Nt;SR2!KvTtjiiX<*R>#%;#)H=8u2j} zN|8)jD36g*2n!*-r@g^2zH5jvT?XYIN#c90c`1%?MPK2Ts{A4Dh^;-@718~=^1mo6 z7g+gj$jcX)^$C2qEkta*@fT*^OE*m4z4i3e z{_yS1%eK@!iXMwDlkzI{;Fz4j(7Amm=SWP0C1k+LKHXQCpd*Ii4qW;E*-k>Io%1Q^ zsK_WCe`_nvrwr?ycW(Tp)=3Rmo71hQd$`+ad%DLS{I|$DXe<24gsJUX;9hz$sIv`6 zjUFFk5rxRrjr!$0q#Mw!SElxUIpXRlsk6eRwbUt!3@TYLvj9_(=P}(Iab2>%G~}MQ zLUn7spNM=jxBaeO+8Z+C8~d#-Th_}uORPj5zwa&UP81LULYRG5nyg5l^Vml4_W=>J z^q9>JAJPET?qI#D&m#6uaFIIftf#9ZID>_zg*GkQyRtY!rE2{I;*$wlpt)0k$hpRbitxq|!yu9h6^FWiAzp$BxBWFABf;pp8! zo6e<`rkyr6^NaWYq#MQ*4orQ+Xq=muq<|@TwNjrh{fj9UD&h34Zw*bz7*-%A(h+Qb za0wo~HDU4s1*cl=l8QnWoyddLd4cs8WMiHI=Xa)CKP~g9B$}S{wbM@HM>nSNi%F+Q zM31}=C&jCEt*uiB*rP+C7o&ye+T;PrZ|Ch>ss6=)z*7{=_p^S@;vNZ*i4tXe6|`($ zv1+_Ns2C%I^lzqPqiy4aQ*BJX39#bhm=B`gocgb0LB>>=P`TE)QCDTLA0L}A&5o#5 zXH`g{T5x4^tC8tr8`=7{F0Df)He!laL-MU;l1bA zot|6}VUxVu%v-@9z_~I@=d$t6vRSgpYS+v=J{CJc?fD>adk^Zy`SAUl&xH(9Q;FM~ z?;hQcq%&Y($EjbCTl5HoF4u6VB(%m#`?58pt(zWWEe2-K{BXzbZogRdX5`m*1Arl^ z&gfS8uJu#vVQB3u+G!oXT_vUPQn*7K$J5`toc$zXeK+2xWyOjwK8|_)7ssatr3ZpU zo?Io#??&XFPFk;Jnv-hD1^PZdEeQ#V!+p?sIyvxub>Ez>)=uN(1E}|HZ49KTpIdut zM(pQSS5!{=c+Y@9+f#8!I3+jS;+AXGMQIgmE>Gp6c%H#NtPyO@h`fr_;j$avt@;vg zANo&P{HYw4L(M+}=AW9S%Q@%;R|BUCvD!`Jz_=C${gNXspLM-`L8swG`tDnOJ1}Rf zBy5AvT1O}9$82Rw*^=>WGEml~k5pui)FS*(d6)#o^ztMOMeBu5{mKxip#f!AV&=GP zEVF6Gp55y=jvTy$o8(J+^Is@MW}`o${_*~3re1c!Em=R>w-d$`B1BZ?Q7_7zEun5X zTTF#YCd<+jT-$4gb6bb(yOudi{g zVEKJxfEX64DdXzmv2qH(`@Cww9ujqbi)LY@Av;YWiQFtU?ptB$=^FACuS9X*t+F<} zL!5uo^^OxmrDj#Bx{NAb7ABKjmAo*&Ou|!#OZuq_l5Xg?4NdfJ$egwHCKS}Q zsa~k)ypt41%4O+!TAeW=v~n~zSkzuKNmHV?p;q!G%>?u7T&Fi#B4!y^!^GB&)^!Fp zEVY?;@TS$eYp(+hWKhI?QxIXTy54tgc-nF2Ui(Qd33v1RKEut5V&h+wI_xAk8Cvlx25nVyaGA3DH4r!sTjt zY-X#-xutI=pJ3eY+;EF4bQUj0SVtM6#YovYP`E<`HPiL3FqC%72Q7Wu8@AUh^{>R3 zoCvkhY!+})DY%h|6JqZJndyPvx)G}s30-t^4AMA5c!n?ipznP)%{u?StMfKwVlMzz zzFS;{Tggz&b3vF?znp@JzhP$Po&3?6T;(>bl`>nodczd{dnkJ;seqjPt7OsLq932 zj=Sci8dYB+-!ryOu(dH}`Y6nuxiB~Nnd!bk8(lbK`USU-aw#_=pIr?#(1pge((%e!jn2B0&;j%nNrPQ8Bp!|)k<;B$BmH3t2;B#3U1 zLvu*O5~CIEsrMzqF@mW4w&u`{LMiqDX<9!W$Rx%Am2WHZ9AR(`wFyDt7h;A~nV}d9w3oiJ+-&n^?tBL5}ZLNRuQ?SX)NUizz|Etza zI)g@#6?&1t4l6OZ;B8G3D2GsF32L7*lTLLf@>LXH_cOIkhlK`^qXk(%RS75KSyb-w z6PKHRL&U8*nC!@TYxD6IeXc2-EYb1HvCQ+$pZ(o6ZV1=yZ2lJ6`E7YB77Sl@=WvVm z_aHKEtxJQ=p4O@nA4+JNeIToUX4$2APW`u*cV_i(Y(A<0Gu36?4*Au4R_UH+Y1jMj zQ(kP`2z{h1Q0e%AZF=P4XX|SY;x!RGp}GMK#88E&dZJYqD~(e`n)_3)8(9Am3mpO^ zL82hXi;S+-{eySv?Y7ReGo(Pl+Evrv5L3$ibRQHwzdn4iB$g+cSLD|1qR)FJaDG;2 zG1@%P9s5js6x`Qt&RjO7xe5^<`+W(kpi@^T~b4Ec#u)HTy+OcBSQXO*bv%KD}dI zWO});;6Z&1vqH=r!vyOxmN&_(W36;TwhXBNmk9nDO#CeDVu?O0?ECd8P0{{HcZOXu zQB^%myDn)QEI(X6!O1@-`KPrFLn|PeLJP`^qqx{cCQ7c+IBt$+4epS zPhvE?y}b#o@#{-EKHHaBiBc5$OHIOGXnLKd-e}iQ4@FW?bdGc^OkY+X4Y5x5wqIw_ z^GVX4H+huD^Q-Q1(Xycoy6|?lQ}9Xlk@EB<-6tS89$8$_J(P4;z621WL>|@0`nU)#z(k%~`k<46fw|*C}%KuvUH<6*^rv(t0*{{bWj#HjCzwlTF7u zeu~$m2XW8`Vs&gUt}mYySD*|(B?00#=w{%k(^KYsKz?>eVj3tm$^zz%$Uw=&d_xqw5gCB2ENs z2JWMuqF)Rf4Nm~r@XlMqH=W>fS-0hLs~@4@kAat7q{nAqQJt%up<*w0)@NIm< z7>ngg=GT^@cnUt0ihpl0Wfxw5c{Ix8I&t8k`FF1>teD;Xz_nQ#qxMs7$)uJfm~)y39bY)r&B8;<}^@3O*PWOOFd=^77- z&@&er=(u9v@A;=-5}}MG>GIy)60)=(bdHrg^Uwf2!3ssmJ(eFQ&O+WjsJZR6n&VDg z8(Y(Sf?m$C&X}ILsw8P=a^@%Rl+1GxO_s8g!)DoyJeleJ1jnJq9L$^{l@$^9Kws#} zX0#AdFht>jq-)r;iGMy%8w*a5D6{Rm^t1i!m}NL*L35S_33qC5ZxIX~llr&nAF%=S z0J@7^;lybt1S%0MqBLDj>*DtKRh1gGi1i(3)we8dB_flcqQg!5?Y8z(;2 z6iIT#cv)&m*97{_EI@)JCTV2R`l2hNvvkoEd0BL>36I?XEPF`kikBjvl zl)C($AuCN9^6=0AX|?Rl5Whubw?23eIa7))bb=)bsVF89>KNt|a0Fu|sj5*#ItAi> zlIfMUBzn!i)@~6(D~97}`u+?^m^KmxG#?4Vn|7V#p3^5h<6XvEVjLtfwH@qov95x5m=W0cij>o+(6R+`hn3;8Y6n8_8M*t_O?OGuE ztQ@w~m=J=#8_R5wqaj^nnwAQrJDC)eC~uTDcjIM@yNS*dRTC=Z!hBn$O!6*Dzyc1D}_4U8cd0V&SiOVfJ5`lVX!N zm<$AsS1{|#{UeL4#9s!B4h040y%x)Xu8bh=#}JQsv5LvmFs5Kl_3R@2{`SI`wF-6Vbx-urN{% zHHCoVyZw7u{e>~b5cAK;VHG!(8dp#F)BUD=fuPQJb**+)3@42`n`mg7*q5S1fSaGq z;l-+xW%F9PaDRK3GHVP?1W4)V(KF+8*`zNBD|cqA*Or>Yo7iSofaljSB6o3F(3RuV zLf7j8Wk z0Pk-!M@G^Bq3R~quKzAIJwmVIRX&jye6@rJCy=SxQozc0qWBIBh?4^T^SKdRm6_Ox zGa8{oRJb%{S(#)XXq)3fUXoiPFSOO7?S<;u``m&OoMNa~NC&S$IA32y0?MZyk;irW zq*ba$^3*zuKvUclC_%PK2>Ovn^{n&j;OB2BnfadWVk5zTV>$fQAH@&w$P}n1GJ>vZnJyag}Z@=ZB|0m}+e<>e~n$^Muo1E#8el z!8C$uLKa);km23^Djfre4Mt1}1{{&^sbq2}xSMI|_Y6fF!gl?T$>>%#h1 zv0-=ruKDPQfL#^GzyBmbpQ#U)Rh{2~ksfKCy`@${PJx8^$09t1x9Z{c0c21cU~}AA zqHjg)HF`~JOnCt2!S8#w)5EcYMWj#p)enwAB_IvKatc9P4~Z_7n=Gwjnb$n~mw%A{ zr#bi6!wYrE;Yt6u-q6S8CZtXYDoNvmP`bkOD2}-eGR4z*KGa16zGL#*f#+XdHhIa= z$R;I*hF&vbpy|%>&*|B}akg$0~LA6AUOvP2puMjhbM$Rex*N&h%3HOPn52Cr* zf&AmokLi$CZJ6?EAgYNd!6Nhl-hHRUa>KV9Uam0a>jI18PMOl+wFVyUJ$ut7QLGB4 zaNq9~+V9E1n5M~ygN^*D@~Oy-3t?t!93?rih>QsBzT@#C6>ViE7w;Zvf5`K;7>OF~ z{JN%<&|~l?c49+qnv)@CBv|aetMaVH$j|64tqDaLsqE>$h%?p!bQ3GQt$i#dMbzj< zHwmKG86tlat>?=du}`kSnjbi)VvN2o3~PS+>c9~JaMNXCl9HD~--6W{5!=6AoB~XL zi(IXHTF<_Qzd@j3pt#Od%=^q!T16o~j>d%I02?x1@x#OC&!yl+V`CYf0LEAO12JIt zj-WwQ0#^!I91B;ZO`)4U8u1Ou5gw|>6ci>5pp_^4tMccDnw+3;c`u6g3Im6!aaTQ0 zCT`CA2F6(z7RZjNcJ~2#w^k^WHa0&$7ulaqo~jc@Z$Isb&#?ao3y_wr?!S)R#dMg} zP#Ee$Z7xupKA9YZ`B>p_M}(JOJIuN0Ai0}Lnw`~ikm(?M&_gryTS-{8pU?a6 zYl3;siwnNXQ;OFVjt`k^S>B;>gQLSY%6m zA9!#b;)@sHp}0LQ1J!|=p+OI#agGSBLMUW0qVCLRl^pZh-K@U+s4#8+!Q>i!G137v zcxo%X$ZK5kFF_@^D2jGqHHvut>aB=ak**6Sl;6np-RA!MAiAHh2ddEw7l&c6k2DCV zAAO?N!bYU>$Nrpzp`9zizR*w3ygLR!R^7-qUM7Sd*vqsjJZS@YO_BgQmYomeGpx(( zD)#^t48dnR&@pI2=w_Tj0Xat*gh&q4qpX4BmVP#f69NjXIeEBC?Z9iXyxTEjCTdEh z`=8v018oegP+Q4+V(1C4yxK{DOGfBexaVdf)3r$C8Hihhx}~ix6ZS~*5#e+otjJB* zoMo+2K<)8z2=txeMQ&W%O&yVTT1m^C8&GpGMM4$ud5QjApZH32PS?W{8&0a!q6SRN8@!jR2 zWSuNbU5S{u>5XL2xK81OSGs{&+mbUSHx5Q4l~&H} z++44{qcF}U$Vq28Z~&fx6NN0R4J%hJjw)w^Nacf3%r;_I$(OLe{xm|)u)zL+WI$2% zX$l;-G@*3-e`6!>LAJ*h=#K=-!tsyXbj5+X)e$y^iVwGR*dwj(7?v91zKj4RH113! z{{7oG6l~rPRYN$Vzi<3?`)d+D(3PVy)sqefRf(PyYs$_1c@O0U6NDf`?bA@42uN=T7}3 z(RpsG`P_IM5|;yaRvt`BI>hDlTG37)fOPg(U=$94Lw`8aCu(@mERfPD`;X~b8WVsd zn8{%`=>4ZcV@wc0J8rN3Px|12)N^&wmsaP~pjFPOBKvphkVVr}XeLI|MsJ3Bwd}p~ zT#TfO21I!@D{ly>vCHV;qi8`Xr_VD#xW6CEu zh_645y&6idJ%lJ)kQ8+-_+t7f^epY&Yanz(#)u&IGHN97Pc()B zRw1gGh91~BT(!>o7w1%vn}`u6x6oG+i{;gT{qb@uY$sd z0VwbS%F_2$WFZJWCcN`y=XA@l08(iRkl!3)*4G1 zX&I$RwnVnuk$*uAvV_T z>zK!xJ>iQ(x~z`Z7~q=iC@Xw2C}#2q_Xpe;TlJ_P-gr z6-5~5_*GZjI@ zHUWK(8qd(X~OR+3MV(3GR)nX z`mcd%1Q=$b#>9dt?b!A{6ZaBd$2orr^4Eny{8t4f{ z)8qFrRRh<04bJox>QDugwlX*q-Du7G$X{J{%qyIl*dN!`?O|7U7VZui|yitJ~`>L+uqAW;lVRFM6*y|A#4ZEt)smk zhJt(}k}tirE3<_5X$JeqQQfcG3VicHiL*hc0Sm;0rgI+^%99E7AtLy??v@dF%CV37 zKjHkLJ=(ZUOC#t!u(&x>&6MXxyIK^LL1ZU(caW>iENj6nM9lQ>)e3mOGgU1SKn2_U#JvVIH#rCs9vARTu3epJR2DszJ-3l!37)ovnigksa2%1h+XQ;j<;c z6C#4nGD_43(?TYj1ipSa@zFlrgp#ds_?;FGKV`eSz@{lr1=7h&py{cdeQFGHX6U6w zvFTX>mGO?DuKlOdCn5$Xa~W^PEHNT4x&z;e;0Y-&_(Z!SgA9Lj-C)(@bz7Xh3v(1@ z$Ywd#est3Xzw0QFNN{3}ZMn6YHcHA;xlsRi`E!ZK&_PWb{d5}6WE9>!X@dmn{zRkX z784Ynp27DT!;bW7d@ld4(!^Z;)wk(gX85@Fuc4b4lYG&TW*GP4)b@%Nsj%nnZNI*U zJ>j~N={v7&mifF80^kfaoR#!ZSMt8M!>t`cNEALis%-GCOjcxH#|g}^KHVnj+Ya1v zi^>d-yehnO>IA2)AALe1i%FU;h~k~KC+@=5q9|}ZHph|L@A_wXY3lcn$1fmLeOZ*I ze$4N4iqGQVibongcye8jY&rQ?N57sks*7kNv99;31p=qkq~k;XsEJ zSd*~Wv}wKe_4fweL!v{jip-Spv7@*voF^t z9{dil{!-YN9{_RwRo%CmCY=*|V3^rKBJS{_w&>4q^IUVYo56OIEN)7|q6-hn;H8yR zor>QJ$dH(_NZ|2gNZj4Q8}o0{aS@Eg{8_H?a9)J^jk(tZ6a?9i&Kh`8`8+J|AZ=n~ zLIdXfAAHxC-MV0j;OD3r0_kxixU2r20-=*nw}NX7NE88D#U%if}$jaA6)PX zd>j+63^BUAsrrE{RYK6Y==-`{Q`$8Jyy)7S?)oT(L0?e>Lchh2eJ4K1%!}_o$tkMk zdQC;2JLpU@x&Y)IC=uB)S#cDC|ESE93udew$H#jMY{d1wtf~Xup3d5Sl-QUrwX3`j zhBV5(NLERPf#F&;KgLi%9E5+Jp{uCw^M)Qg`u3My?BdVv{MR=NjB^gb(X=Rg=|2!`?2ERDX!YQaqo)BKe zzDRV~+#?R~yn-@R+iIe4e~_}qGm=_!`qq1GFAnds40jmWimj`I6kO;NVa7>eEHi37 z4t*-2R{bs2_w$&M`}OdjJ%}&DY)J;^(bhlc~R!pfDe7WR1AHy6~HFiGk5Vd z{O0P*m~00}^;z~N=JB-4W9qB~fyxy)7{UuR$SDGqQ_M(?c1*=5jWty-tBnG_9>{~T z%9SM9RWAx){}&c2wK)poZ;P*=6IOq|Xzb_UQAS{K79egG`{VQWw*0lt2P&LehLqui zNw4^30H25W|K1~38MYVEH?8TVC5h!k_5!T^h)evQPj-NH5jf}XnmL)12R|_>1ukRK<9pmywNK(}PW}}C zKK|jsi{?ZrdlNJx17xu7p32)Xjx}JLgPaUT)A$~Jb^8LGKvw*vOP4$$0=>$iJC1)c zw$jpW<;Tz_O&k9}d5flJg{*1%0K`o5U_K(2_f1LsJO^Zs+)Nptil7goxeLrwUce}6`3 zT?SBw1kw9H(JD{c1V1hK#q+Dpd13WSNUjzc`RQ-3F4qK)I`{0e_!Vc$;(9OQ7icG( zD9w$yiF)eGWvr^atP8Af#;N&r(-YUaC#2qapN6hDl2(FWz=yDiSK%J}=eX8y8xmYD z;St$e8)gp|Rsp6RC4nTAK47i44!Qx?+kU(RzBey-Z^9k?26~nQ;DnNKYuE^OP4QJg zta|}t%yXd*Y;(t_Gxk1iJ>pIynV@JZ3N+aBPN@5!Y#IK6{DGfv!0nf-0BjSgi2&{X z%_qk9KFiR30BSA`t?E!gpNvayFq{F+PMa>L(|I>6Q})sloMf2}1S9(ZU;RgM#y5q% zQUpIRzVgO>01R&eg%)YVgFKKZ?ns?1X8*jCuu@(Ezh-X$hJY>OD(V5x;>hkRc{hz0j^ek|^3wBH&p- zo(a?7xsxBnSY@Ugf?$8a|6ng_n&lv>AXsiS0;mi zRb%M$Um9}|H9Uue$o_)HfR9BIK#>-t`V@`Bsc%a5?eT{!plt6i&`cP(uyx!@9CvbI zSWcg_2G~r3g&yBtUYi0XiXxSLLKo zGbZs+IUscpz1;)IedqcON z`_&ZibP7I}_ng1{D^()71puz^OX==RA4OpHqitU^M$R&g4k`Nrjf0`7Yv>t|CM$xS zA~#J$Dj#ejDi^KRlStTPZo;}wOf)Vcv3+^&Y5fqh^7Rd4$mE$q;PB*`n=GIsFLQ)4 zYanIHj%9Xy5HXMl;3}he!8zig#|p%^a$YP+)Y)rSmG(TZKh4fm6SHLF7q+F?V9Z#! zO`7-$^JkR9_u%|SuR}jeh$7oyINel}!2bO5hInX$erf^8N(A%ty^kktZ!DeyKFaI@ z%w>4^t966MMD2ZgSQdmq3P0K)CM~X~dna;0zoj%96%nB+`5E=S+Eq*4Y~vQ}VW{@0 zu^-*^LiEDz(|=dE*Ah8_9PtKY7QN9=rxrUN-`eC6#B{G^{ZEi~CZmg=#xC9Of^*yx z)7pP=`?A&jClYGp5$hEcZgrCzqcN79QXfK88x}Y?<62(=|8)yPIQ6kaAwJupyGK+- z!?E1`^g6Z+hz>O z(d&!?4*x z)XxSIEnlmYE4Kf&5o8>{G__+EuI%pcL(6d+SZr_Ym^40fFYEW+Bf}t3VqMSq$BTIg z^R|`VZjewPaNTmvkvEg>tBt%LBlBvOVY!~y&h=gY79vqw4;D{4pzq;Pn0tB0>^3N7 z0BvQn&zlWSH9-ZMZ1o;MXAOWR^KBC_Z!Gu}gf^R@oKqLuz6Su;H+aQ;HB~L62xW%n zd7)VPFng*J?4)^aXzS5iIWihrArn~-f4=iARHAq_gC#txHvtm+1+5#FnWO5U%IXQp z`V_QC7T!C;k)m%F^Kswm{>AOWskTK2IIvdQenDQ9EzI& zjuoEOS-^2O2Y2n0>p!z8y5BGW0-MXwp7I=$)Z9ifFi--Ny9@zMrIrt%c!sL*BbzHj z{6V+m{JKW5kEj0B3ZH>|!uAeU{8LfqxdbOv+Fb#_;-yd!sF2L7$$b2lM;nx zIa9l1;o*&VPEh=lYPLT2w`LA9SPys-oYx#qC;IAEO|Vjn`CpN`J7Gi;k&+Vl+&SfD6SEd~X}VwOz6W=qXGeNb=thDc zH32AfhhL5=HjHjxoQP>*uGK3v$d5bnDuzpMD00vH#U}?BPW}14|HtTXJO{~#>=Wbq z=mwA9$VCxN%foLP%JLJcxVuBR4iJ^>3hkZ^9##dMrX&f-rDb)T39{$wfyI>9$Ol1R zGMMB1g{*JJz*Urp7Sb4(ke-0%sg|`#j^6uX=FZVL>c7U*PXI)3zw-nHv4iY@mUDkN z{*bmB23){b5w^{X?2w8IB#>1x?6Q7MqsAI7?L|_eryN%xs4;U!^K^SjBnY9Qj5LtW z%5vTn#h*~%<>CEn-r@dB$6_qIJod5tjtldUe%^eBDf~)z<&382+d3V`{Mwhzdv=Un zxxg>dAM{7;MDGp4sAgq@QHha8Ye|D~2t*E@PjoH&pp|%c?dc={4lst@kvY5Vu|dwYfvl9#|vqBLeSucK`_P24$c2 z%0s(mGhF;yA#56k!nbLo*023#uKGWILxW{l8%ENa^xWW6{b}flQ=0mmprUXCyF*1o zD{Llz;6?JTTFy@_g3>ZFtS)CVX(3roVeT=eAtO!9sR4p}&auQwnR>gackoxLy3m;Q zSJ_K}D&RYMxBe_2*Vb%t-rVqPINFS^9$eK-wW^1$nwwGOZ8&9B_Kjx zqX%+WvpOYhc~#XRli56$Ue==Rydso(TNMrJN7LNE7fSa8>k`X0zjNmorB2|d-^okt z;bFeQ0qaMn?nD*sj;PA0_r2RH|JFt=i2c*;JPl+yQ_?qlpec)QQW;`%8%gH=S675R zf%I9MQ~C>Wi_P^8Njc0UGv|Uq^{1c=IKL4fVP;e8tgg;`uq8vOW!v-)N~HlTQ)4tV zTr9k~dDwDP^KrSvV}P9MKI__WdnL?QPe2HEDK8_;+(@gHoo^yTR+bT`)6VYYO{!uB zp^omf7D!{lM6Ndx4d>s3L&tOTxPsHOPUN2`YvnVC+EvnEM)+$*1u7XU$>a#}#luc- zk&%hR&k~yzvOIGV*J<^>4sU$1a&!S~_Un$~XbxLJ|RIjR)v&eISQRj1S=| z&;@NW!piIZwuf-Px)b7(BOsEsFE*e~bOdnEZ693stlB<)668l~nCosR2Ts4d!Suo( zmIzvJTP#*+$pdej(%gsk`0ix7?IcmN!4J&qNFZ@F|6hsOZUZ5aGelK=!5aH?lgAT+ z-u0~?;3$%Xu^WtC2~Aw<%%p*^Hgo;`LKxCpnO&pY{k!={#nG?kGNR<}bKsYqAjiqk zc;a@Wk(tN@S&R&0A2b;2MKZv@@e}p2>p&}1Tww~k)>|CG-M}NE3#zab_h8#w2E_m-qYHtxB*BR7aTd{b^{ zG#ttw_DO8#H3faS9$6IzQnn(#Pob<*xqi;JS@#$uaahIWpm}%``|$b;;@c6XW3Ak9 zg2oxms(ZUnZUGWdi1dg;aH%D~JSl3IW4x+dNqoUfQsGvovHZ;p+PY5Du0slCGpg)( z>bhn?1`7DvcE4O9rL6|OC?~_=VcihB)&4mqrS0JiYOpug#x&dt1EAJUfr+*bK|Q`* zB^-ixL50BYtklEDvKt8+eXwvh7H1(mfH$(aVX&sbmK8{=Ro*wBs{qzKdl=9=Y*#}m zM3zN*4aD!j%lE$wrwyNT8tC`*V6 zg%K)N-aM{oat;urSoR)+c;xGL5Xx9tD5k0dvwUp*{f|@b035MS$OA&rv%F8%XU9O6 zD7=0b(0CApR3?Yr={CxoTdZr+{K)0l^vg#cCM}SQezu^tyqKRl9_9-aq(iag#~v&UkC~~feeJ00r%17_c2=4cuq5cCKxwk@edckin{a$WLv5w z&Gn_XFrb|sQ|Em=%|`_k+1Qp!FFCU?*Qk!ge z`1}xVMXqk5feTGfE#g>){d;x2274NAE=d>HF3sQ+*V_pCbzDQQf%lRFkf96o4zlto zIon-fzfw=m%#c`F(sYtWD;e0EPH}66vaMzr@w@7c@=|{ym7&BsGrt0k znn-H2N{VFZ?PvuU2wY+6dTS2)Rm^daDh<2I*?;1@&P{K=!1H>u7)fHk@hkASh2kP( z;K0v~9D>5+6&fxsi7

$PGQ`Ih=BbNl|>NUB5)CYGD%MSh8Ki~3{y2xM}rdLSkQ zR$-dYAINhOlU>5hmLh-z>)n=pdf^gtM*>F{@f4)DwO=#=(F&+y@=KZnAnE;Mkef=J z2y*viTDs{D>>~3Zj7AyolN9P;TKuDJr~W}1U}<16g$A)GvIe|uFn1-pbdaOw7dq~a zb>pzuV^Uu8apvMb$Osh*S=2}?(Fo(`QGmnbI<5Qp<2I}XrDk|mEc_v@74x`QG+m+= zBq1Yr(>O3HwW{ldT`7`e1zg&47sygZooo=EC8=Yw*$@lBK4IrF#9>95lk5FU0HH9E z)~E(@gt;c>E63XF6L2)_HD`8hU&?Wbu|m4oAVjM-yA{u@^}V!~Wpa%M-I@i2o#-04 z$4G^QKD+O{{&5nluF(|@z}*LaCXEV;ewi**F2|U zZ9KBJ(|m5sD(aT&OK(e8)@Xea0lBJqP#I{f&+>fRNMSMk^fz!`iGxqU_5G5z@~sDc z{-N5F_*_%Qb36zDlAzbwPWkw7yOC0L$KRA5In={=`UOm8Oxa_>9U;_a42|gehJF{5 z$Q!uRxl6)E^Cw%wueHqL(5KGxW*ytwa~J3|-$rI~G{^C@OI;d^vngfTy3j9{Ng%xY zO$(!PobY>8>QpI99fwi65)frLGrnm`t_5OGbKW+MW`H3SFY5kGF)uFz0^IB}A~=|@ z+2OOpKSvj)|JjlE+~_kIVcHM~I=lRVJI`>6q2WAwZR}QrrOs^T`TcKTcGK5EkV#fp zr(j11MF>lN6_<^{BX3yq#S29IL3(g@0aVyKzg1dyw2RM-^>jR)1O7i-1o=UjZ+lKW znhkgMvWp*evn%z?Yamkmw;Cda%@?0-M8XDf^iWp&XKW0;;cD4 zT|sZOZ&o$p))Qf||FW08mKYd*{vGo>11flSXEG$%2v1ROGEr5q9Prmru7e`-7dL|f zgO$-Ik)OY+PMvdgP0D)F$jpeg9o71Dq2s&N8}ha2S6-3A*e~<#DxvceZo#}DW~#qF zVnYA^O3o84%)%?AhekFJ?@qB~r3jJJiFUmpDH{DM;*Z9xJ={6t!-7(^Ox;uyu(`Pa zNm>8qIlN|qnpj~>C%o9iziLQmTOs)Te4$sJBlTBkgL-SX zH5iuNJYbryG`zOlN+m-U=Lz%h3gt`--hr$O%govT64(QZ6L@k=X=sYVvWH2z{^GSt zBc3umwo;&yxWF9EwDZ+POVGC8XQYE@=rRjC9Ejw@5kQUoJo7R^0BSf{d7S zDulf8>#=ut;Xd(_7g`_(EHuqU#R5>IJH4N^bmiv$bqS`IF*d3U;yQ_9zO~(F41#b< zU$x7hT!ezmq{pkPrVJar`<;|K(2M~uo-H1?O_L}}x=mVLLq;kT^2@$eYtdl#PC!6h{)8$ zLK{yc-zGHrZt|V{+=Lh;PH-zn%t5I(X($Wy_sU0vM`cz5X~Jdd@z3YwKGx%(ejetb zbq$^m5_4M(v9%zoQPLx89J}Zq)4jGE;0|E@!gR{1Rzog`ZV+O3{fVREf6yG*x9(V2 zbq%2nHDpGQmd@0aTqs4aE-F`$~5Vnnh| za6;OD*)aeB=a*Cg%?f1N6^+jFZ7T9bg|XEni6M#SnGJ1_{E#Ptei2mEGn3=2%d@bk zOhx50PF%yzzu`t5D7hld{j0z*h&`g7@V5tkr9OeQ*{%Hg$0iRTx=9p8`Cz(Uq;swV z;_q}lhALl?DGMz5=lp+R0l}Nl`Y6M(}Er2%LoIKNm_%+?@>x|2`l3hB1toGz)~vQ_{p}>NnVP*?oMT%6xtT?_a%ofcnQ@6$ z;BHYnVi@)*kk+eqoQvJsQuLq=W&E5kPWM-a&RYPNJcvI5M!?!2Sx1- zZAccycA`>zG z{YJYY`nfWB_~(8)ff|ADIWsl=sENvO0ILxmpe*QlysaIt3;-Fbry3>jn?YvjIo^AJ zs*!h>8-^~rR zPx}V3uGjCrPaE(3RJIANK`SH&mpbZp;2;&}ds%>GVus$7)3RlUTGm5Hd%4G%Ua0w) zds#s`D7gxa{(F#Bkui?Ray$oF7o#M>335*RY}8GdFM%W#c*LrFI-h$RHZHH+k6xtq zsQISEx49%!bFZe=m?~VFdN@|sCL{urn7}NEyaurZFbb$p4AL0pUMpbYgOuF=aipnu zKG-!aZc|1se(cc-ka5Mu4S)6hcL8dZ=HM%G z0V_-F|MVZoK$aNzZjghQUIr8mxP{QSVqv)!)pLod-=G|?0+)678YF2C{f2k14LJ$? zk58;d(jz4zM~|oAx8c*OQ-Yy8|BAC42xa!UjrY|)I2%M)+^x*{?|=$&8mrdiv~e1D zg*JR#GQx3Xn8fn@ZTyI$Ghq0TMH+dvy!5*$nEStKk}Ibn2<#H=f;ezx-tz=b{nyDY zoO}ivhdiRF)_?bl(bH8no+;x3K5a3y;ldaFO!%AI07O}gUf|F@#*C${C23IMlA43b z{dVp`xd7Vtf4}IiVAZ8zn7}qx>9bT3{z{;L6j4|ZK9~X#?`<1JDAdcbZGFG$upuJj z4s00Y*v+wFD~PfE$IxQ0XkdqVT`$Y+`j(0rTy4`^byI-AqI-a3mw3u}v6@N;m0TV= z=I(Zcpg=eqNG=7Y=t$n_ptr3aJt3@8>=?|naevFKcw7#v34zfdp|T5}`5;QvSGpY| zSXUG{UtW{y+I#H$%k-_g0e&D;{_|cdFDQ%H43w1HXYpec#`y#%tvc%816la*flQCP z@Cdn8>hP+TI;w^a2Ui-L;0^pT$Z_JLRyd5Mfp$ne6)BmgqoyRSC^*r!0NP3;@D?g9 z$o%BMYcv95{NH7TZ(KLSHQd4ce8iNYjPHH;H}=&!_U)W-37k(Le?m-9{$iqS7dnfo zSKw^j`s{f?2j{>1unvT|;g@}aBePl5d7}l+n^!AhzKloMue(e?*EY5_3qY?%;DP`5 z2NIcJD;qHiMbg-<#K7O~Z{gb5`C_ z9DDCnUw`b1*}93cQ4ke1B^|~gsMH`vH~gn5w98&;%>uUF|K}tBSBuA?*wvr_H99Y4 zY&m2pqXneuGo5GXtDPU-aH>@~WE7{Q7%d`cJm~RV&w*KACFuN@g*~27>{9ED`2C0r z41|vtu+pI^45gTslm8tANuPsdV>N7vGb5D)d+qCsQ~!p#v&;rcl6MS_9zE*La<&+i zSN;*yRX!XI7Iw7>fZPplyjDa=#WaQr`eZ8-uN>gkZZDr%(YE;?3mlFX7)HR5uogT( zTb^)pdKgk}URnl=^F{FOQ^0a~2Dt?O z$3DpafDoR;U$gvcP#mJPvt4bd2?Gs;N1&GH^CUAEz6(Vt&qP$ChT^#_P=t1KYtUb# zW=gvsZKkBRh*s>c^NZj2*6ojm)J#n+USy9Mf^b&PA90uW!1p5Q{m7asuQr1^Byg}J zO{-0C!_}i|E95(1L~KABJRjeey3~#gEz4FM`_s~QjrzeZ@>In2JR5lqHF<-ZIr`r4 zCCO)CYMr=QBR^9@=770*qLonlj$=1YZZ~8y&#j|p>w7zR&Tr3LTAsV}akO_XBm9T< zT*k3+=ay+bmmj)^e%yH+{v&v8@~Sc(n1f|%Yy9n358YS#ycHXYPBU{a|G z6&IKWT#O_PBT3~DDlSa}19mhM$jR1c24d~#N>Q;^_m3X91H%uBpbWkHk6p2|{hzYL z?L%2HL7eTt z&a*QwWS>UAS`lZU(cG1he0)$BcQoazKOxWxJB;Dq)(-Xjbe9{DXO6Sc^KkJDU_lii zd5bk2iwP=^toBFFUXi^@Rt=P*NRdV`+o7B7&31|mRgf%=)06YTwz1RPL6I@#ZEp_g zIkt_H7Dk?7$|eyxusEhRZbUm-G9194obNqP-bPwt@t7`a;Ox+3_30`pMjaR5f##4L`aCFgfhLMY#4~9nx2%Lc)6t|AN9j4IhK)_ z8Gw|PB7`59`nRN5u5PfF++O$0pa}Yc3#GP~INvc>a;l(2LTFs>952YmoeyWysa0Cj zzIpxy$M~(c5#p!r8}0WIfNg+L8(0aEW>X@5iVv&(^61A&zZ9)%K@C60F&PbWQO`2B zb*#!bEdxXt4DVhPwRe8WC5nnP)!qV3C z@Vq0dokq;OKd8o}^A30YL^C(VjU>#3XO@dv41`d!@a6>_bFpJDH!YOqLcZ#B*zj)I zr7(CZQV1LmsgOuXVOCr;*`JsKz^-g&s@0E4=j!t0q$o0#pBsPHfF$0`tQ$6C1$khr zD#~_(zrV9*Hd-&qSWao?2T-`R_z>4Am>XX3LixcCF_@_yN;6eWU2HQEFUSK4dtJyi zH+|Me90Q1uXMdU7QJ(Bq4d1*U{%jK{Mh~)|yP4*v3f)BC{YIfc@c9I{KAA}1!ojt$ zMY`mV?zH@ne8%0cu`T49q25%R8xwed^u+_Al8OZKhJM)wDB8|&HR9x=tQf_o{QpcF zs$ovfhdZvR$8rnLOSm&_rv&u>u*6|M#qwIVZtqp2(nyj70xp``MW{ZK5xOrgNe=xd znaE9LymY?Y?5Gv}#f-(M$Kt1WgykE>x5o`aINiAtEPE)JLlr&A+b0`&;aE8I+;$mZ zBj93hKfQWMIf@;^>9tmLD@ycf1_VU*P~8ibHmQFP@{V5;O^ZoiR2;u-Ow_OzJN1dY zzlskGLxcfIs-h046WKZ^#&7Ec4P`yKD4w~i+HB97s8*>~nEt5_ra~RMJA$F?eddvF zj~@ea1odImDcIi0Q1-_)9BnT`uUwf4$gm4JbbMD-L==RnA66H zMpH(nSllq~pr+?JmFdcgA?A(vkNR_ z7b9E3juT4cKsw~FC};@Kok|l5+~N8d%8)& zN8|&bRfLBv8oVPNJ)=b2{90#p<6-gn5;-1vtSax=3_w)_EfRl2!V z+c?J5Z%c^5bO}=imwatm=J8mCw+DGTNn=vvnHKy?V*oD)3>hp6FfmjkMhD5#;^y|f z#=qA4?e~F-ydXT4!o%~=w!C@(-~lj0i}x%+Y3a=7`lxe{Cph12fIqWUQrY3--k{q- z<{XCS+>^)^2K-s;r{?eE2oyG~KbC%P8Q$uR_K;D#36;Q2(LoeHWxRuK4wLr0skrU$ zof=tCtg8qEErB3s%pRHz8h`2AE}}UFy>=rzb3+}UL%UdT5W&<`w7)4Yi}bw_O1yai zoY!K~x5dg=?9|8+rEQRmrY3_IV_2x=ZiKr@!{LqxF+I?wT#G*Ex_myMh@wt2_+cT^ zZbY>^Lgb*mISzz4-)!t}-o!=2eozeQNMU&gNJ|mw1A5hsee0&Yn?Jtn( zxMD8?T~6;7{(fA1|B*H$fdcSFj)^4((!KDCq~0X~&+n)Ghy#7xAJFp%5VVXmO>do{ zsGG*oBi0|l2O1lw&BI=G%IBqJL$@rq!k}l;Z&`uEP(wVuo@wt?|3k8L8X+))?9&d? zIz1b1zHYV^>M+F)p-2)auf9lRjI3Cdyemyee=?!P~nVLR|$YA#(&>C7H> zAO${pWUtaDc~mpn^bR>F9^$Og_bkS=OGqm%U1j=`h4QoXXq#ik=uM^O2fj)Yi4{?6 zxnNI;_~XVT>!28lqZz0$8~r+q$vH$@K?`g8quGNfG2oBBDT;=E^ncCPePX=gh4Kgz zOGCH2&x6MSgL?8&95f3LF>;LbnlD;;^Kx{b;rJ~3m-%B3&Xi04Z~;E*gxVm#9Mwyr zZ|ZRcI{8Sq$HmS9zUo;xy-mGU6rXkAHg^-s%m$BZ$@3u?3q0|c5#7)Y>Uh+{yK?|J ztCNZEO^KW!w2|(t&wVPjY-yM7Y!Wb0QWqdv@hFy2$7z0Z6S455^>g6rQ;sIY68LiV zs#XA(rx4{ViY3(sd3T7cToAnwcI`jC(^f(xw)iP!KP*NwGp>N5NzZ5Q8A=@LbR#Ia z$rc8Nr|)Sr1-4`-Fafi>tlP7bi2YM_Ahz-bLbOsCF`d}{U@CBeD5w`vT?3ldZ_MP< zbEHsNF1XZG5_e%9tOcYmFVYi?3f$RX7kF+>-R*+#%x z*4e8`rk1-!2a!Xx1^_1RELc}kGg3&aQk^{rMSLzFAb~EF(bTpXcUK?rJ$;>xMJ&i_ zWX%f^ZGCFstzJt1)x=0LIrS=Vc=7>nE{y!yBPUm5mOIW38yyaWWl6Om1eKCxH_3ELB&SuZv3K>H4*O&zn?_lGhpymL_PZhQh!J8Kx2aa zO=FHICpZN-8s^@2-_F8Zc*t9h8!k_~NN*(svZu|kLB{@OU45@?J63aU+GxHcWUTz@ z8{S>pxP7N2&WL6Q)PZ);*AZYIz7*IX`TjMkngbX%;n{mpEJV90!0Ja6rGe6s&wanijt zSagUuaT#}_w{T0rn)4+`-z3cdjyp7n{4U7H3`Jx@t^9UuMJVfG)qbh z6$X>dGs!yr4yWu6fIene$2^#@r>TVd{m&Ik?I_*Ecv&1}TPg_P$chUVfIoA;?C&pc zRQa`C0Y0@1ZSXmkl^e|?Mc-HxMNy}>T~@}q$%vF`uHWoQ^ZyC0N^gKokfw~Ea%m*y zJKrgEzUQCr43Veg*$h)PBI(`NO;1H{LbF3QKqw;J#?R*2{klAJ9O3nh&nG3Xz8()Q zBa!26>HgAVk1Ho2a0~D|M$bKNs*Y9{9|dX(1z`F2sPrdqbjnuPU9vIdD-rAOOV*i( z2J|X^aKBx{>ySIX>Hq*)+mG}kPlUW{f55zCIn*lR1M)y;t5^xvYf)enP`!oIXo?B@ zb`7P9>gACwMt8UYHnR|5NcV&(4?$DKX+a3G2aXI{bimV*D30y3BHxOgiVu z^pkl^oZG+b#8|iWf+Mk&5CPe$j(J{=73~{U^~PBI)xD6wG1P6I?C-B>v^^cIx4iA} zZ9dtRF!3TG#8`uD;=hTf2NM;)c9@J}lA{+f(oD_FghY-3jnMJmcn+<-(syv08t0G< zkcmz=cpzIFo(ilb3_=jdTl61>vkS1`&xjTttIzXYmnFUhYX zg-*2z2TT^TF3cxrEVS#uiJ`%kUgE&SS@pqqEpKjM?a5Y6T8oqW%0 zKc7{cc{K|S5h3>HlX$uDeUWqf#C}CH8A(xes=-#aUl`}+_R0~8CJ!LHG*DwFEDcM) zUaFt(136nbWNihXfFmA(w!u9Ik_&dbgc^M>f4V-^3*)qp0%sl-ifUk(n7VcP3l7OP z1L_Nwy^ccACOrZ$dB3pS7nQ($p9>81#ZP5uII!`etc+oT-zF!LRkTx!)Lw@IcCcR& zesO$C@VS$Oe#SU}Jr_Kwgf`N=2MntEl^TS(DWVPh++d$IlKj%Vh8q3a&CGPp3JFBq zl9Xv=;@U#~Qk`oMbZRHdOB>M8;PWvZ<38#n`VXXBZp453zoWqS|;DW~E|fp~r# z>W>GQ$!<(!Ue`yGv(ST5Oow>Jr~5D?*QU zgG$w-TekBM@~dx(PSQ3WI2ac4#IaQ^`0P1-JN_q2$`8hP)Pki7xLCFl&V>B$piuz@ zjqyIjPM534dkAEmI4|;r;{;|%A?Z~d1P&1AXx!q7CCJjcbBC3e0)^U`3$kH=-$xegVtXZy*ZZ@0OiVte{NfnETq8i-WcS?A zlt0V%>qsIPHj`rS_6$)e(iu(daZG^=+*pGcYP>(mn!}*uK(<#Zfo{W*T?e!JvTS9E zZB#xs$!M}9-cwiTdpY;aGDpqHw)JAm_beB>PMynTMxKNVA0ZBV&~7USnv zm5OW?iNjf-L-kzr=pf5jySYB;7(S42xTnI%WHaeNJpjABib)%o2lNDNc74-gxq(kB zrE@~mty#>OqtuS#X5CvZ5kvNRr1~|X9JM@>%7OFufhf9f0FukXXI#MSpDtvRHvv=m z5JWV>US8(GYeOy{Y}%Ba9Zm?kCP8$lz&5@Gxj55ekR#&F(g^5F6CzIC9V8G#&xo4_ zF4FMqQ6LB7>)wSN_65BKFNu8SqVR2<#$&m8feSW*zwblii9+`2BQBCW8v&OI>k!Xb z8|zi(-60D$%yy?5eVQClcK)dR=e6gK{!pmx8r+ePNiu%N{{Oc^6$TorR+gZ|H+b-g z^?|L%(@Z|zxukv-oT%w1`Y4n%e4qpK@fEXSClhLGVU7dI<}}ir@;VeQIo7qQ z4zv`~&&|llFx`6rIv!^GUIW3o)b&es0z_ zp4nnW|7c%sut7D58w5Fe9RgZ_}=#E)M(DBrKgTQC}v(H zlN3Ff@|u}*?D{n}wsHLI3lDG`AAA~e*&&REiA_g9p^17wf0*;!AGAX7p0m=1P-DglZ%B)<2Q$+c9^Qm>!jEdo^xCP3>Bq9#@D6Q?<@b zY_*=I;G^t_#6zwMUZ;Qbte7kI{TEp*Jcd8I&Ytm-f>-)v#L%oj{q>!lyZ0j`H4o$} zN;LTqkOl^Lt%`H@eG(CeM;N9$G+9J4MZ#+pf)O5p;%{3UCnfs~1Jcrbu`<}uBZ=pw zYNFl;vC-LHVq4)K6oB4Tp|GaEsP06L(G3cktnC2mJ`T_i)3I>bAxfKcNeGU;)VGf> z&8No?JM9WPlM5S`6HUD>XREI$*mAaZt?_mm|F1J-tM3P_nwcq^NY3jGf?{B_h29_L z_$n)R891P7DvYDFht8)~|mdAOo7lWtMqmaG2yG#=tSQ-B9`n(azEi z6n0mKaqE+41bW|>jPUWc!t~&`PG{}dO+=4Si1zE!+bOUu-Z;5ZR@{2<6J}g>Gvyn~ zll7t%jo5L@+=SHHwYZ)vXgK!)>n@C_jzy69R8wr%^Wxd0NUs@Q7is^OW6|zm=nXNr z0f3}0l5N0=;Q3JW+9dkKKnup0<=%J;H5&jp_vJ#{?Ux1AbgtOUYFXc14RVBUAeH26 zTN_dW%1R|KuClC&+DjtOzXKrM+C&>QE(n8g;JL9B_Q)K|AAm#WoNzQZi>3+y_S(+Q zwkmb<;$ydaufL%=t=e7odFj%`9fDq^)4LWA_ka9-wHfkZfRLELcUYMM`fsa-vC zCMqnuErvh#|2*%7qSQ|Y4BHCq4Ut#OxJy`1CIq22>*9JX2V0pW+M{ypnjn>XViuHUWM z8yF)iOzO=F16;>Go=2wQcD&u-I$TA@44WCahzWiyoUw#Ff!z4o8Jv;4A#q}T6sh~g z-b4-!zwWGfW>kWH2T-nhTYr+R<>Y!#`Awzh230-Sx^ljHtMxvak5JO=Xbiw)hM%?w!DFG3x7uUKR)4g`t~; zfQ@T+nknoOgIBqA;o3t9f+sJ0dyw~g_9#!dP}(EnPCQl)Kh@>}g-07+j_&=C8tnlh zvtfJMvzZAz_6Tzr&cl_Rb;ZOnj{BMNU0&ueGl@?{@m>gio0Lcu(2+o;$IL_{WH>h& z_c74gir8#|2X;#~{LjPh9drRI{zcIDdcUIvPh-y6)oe%eV@Oy*SA6ih*e$QM3=@rS zfL*@Euk?c*r)@;tdml6p-VPg?7O5WjOzDK8c-n~cmAp7~%^NH-`0+=h;M-$S3$KI! z-uP&;HDTa#?II}l%&fn>Z=H(EUL~MWh&t5$IMJrM3-sc57da>yaW-l8g&2aqk?O8Q!v4y5 zA2Nka&TMuIClwX6_VIlhG&wK4PLE9S(9a^^5pAj?iiEl|B+uP8Z6lP`X@q=bQ*_$( zjV-ibH_?;>ZADzN_^yQ7876yqTXd=-%9FdLY0P65ph<8$9_h5rt+$fj6o4*A)dRi% z>ogf?kW#U~B(+}-TJ1;4TXX`|iIRS5`IpY~DASV?P-Wunj24}PRQ;6-Cg)CQ3=EOd z%P)B_uJggg{}j(xvh*e#6zn?0{Xtphkm|Fr&4*eonxe_i;T~^3(7Y{DAB8mLZqd|I z0_B4XbYt1_a?laPl=yB31P{XKc%1htd(OUcJX~iF&Q~QabL`KIe}az3juj}d<7#g+ zG*Q~3CfnD)l~xfUYQ#4%n57Fq=?GSCf>J+zw^Q58EqHXXlpil1odqbesP^YO3y>Ky zN)I2upK(QH4J%Hc(hsi39`xp{w=d0H)&X=(9&HMnZ$920M)m{>! zN*^tltHX?}JU1=4fp;2!8uw13!8-Nt7=bx>-b*SVS zb4A8W=iL@Y(bPl8jBeRrt6(~Iz&!9-Lo}I0q-EENLck9;JNb#g12s^mBD*S?Q+Qzq zj4Su=EQ;=Xtly>b?q`XT{wwOU}`5iZUp)JvnCsnnrg8?<4N^ruL}h zU{j}P>Uk6i>aPF49@*G41_0tBR2j�vyoeBuTcI4-eVIuc@!W3a3#pXb;Rk$>K`~ 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
hiJDCC(b#dDJl5Z5M&7a_&4Rk*}Q^{s)E3=0Opv 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
)-5qU3qKp71 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 literal 0 HcmV?d00001 From 1127bac345c98a0a69dd91a5676270a5032393db Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 8 Oct 2021 10:28:38 +0800 Subject: [PATCH 089/479] change: update flow chart --- docs/flow.jpg | Bin 148937 -> 151998 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/flow.jpg b/docs/flow.jpg index c8484916e389febb10cfaded1675112150cf39e2..846f1b9404951be0ef0bdcc803968a5173101805 100644 GIT binary patch literal 151998 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 jN!b`n`wFH+G zeK~X+m^I&gV&c>ho>pJp9{AZ*qf@E@)1$`k1`hcg4c=UKn-CfB_bd zC*0JZ`pG1-)`{N1Bm!!_hflnViyl0V0@fX~_hwQ7K>(7(qT^vkuP7U);{Kq3rY1QJ ziz(-YVOnLFQ{ndM$&I=la2=^$JsO-k_WJmMz=u{~YDs%3YH$=6MDSY%S}?{MfM}R+ z-QM!Rm$Pk}Nh{kms%?ZEX$G?trN*O@`ZqCvxU#f}D~pn9Kly88iU+2?B+R{(&wCIA zZ2c2Q)NS;D3h*Gk@V?e&w#vP=%F&`V1gVr5$ZVKry&BZDn})zgm%tGQh`bZ`kCAV{ zehliW0ijlS4u?QI3}n>~g3D><)BwX7O4=}KZU%@4Gz9+n{FbUarp(d4XK ztfbP?A~<0bJJS2eicsFirFEWKOPUeXc=w80c0KDOQ(j_|TJ>~U#3v6uSLNx|BJ-Py z1Ns0z90jM?(BT1a282G1dtA5qV|QRui##n5=VY~w7s zx8+_AXC-%cmHXXLy7XM4DGw?G?j@{9hO}sSu6n|?Hqfc&*z2#!GnbQO9=M&=GwjE}!RgR!_ttO<^o5FhP9Wh~18 z0OL-^f3zg%xJHo2(K!Z(KIo|n@Y`GL6f*Ma7@21YR$S?p%B8Z3S5-$Tz~2!U(JDnr zVaut&?P1lgVm3yvE2BrzF|>aR-5X_Dtwrt^C{;V)5;08sG&EE6 zxy1}dzaqF3e7YU{1Q#bf2q~Rj^&^A8bT7$<30uTLzHH7G1qS<7r|&k&nR{88!3ht4A! z+^A2YgDcbO(_X0-QiGqq$0Muv;5bjGpX`X9^n-WaCe)AAxo2(B46lS-dnCnZnfWmu zA4#1tj2Xrbb96VQ-#Dq^A76^xQtji7SO>7#fbwc(73Wb;iJxZ?cy4e><$r7%8TEr>;PUo4@LyUQHQ>1XE>%qrRtf(V#5X z^}-3bvXE0r27)lrFG>n*2%;L90!aMKNo+;t1)a^+b%`WgZyo2&?ZX-Jn|7nLgcsu*1y4p;tp=bq|aN5 zq+=qn#CB-r(lsd}Appz8&LO|7!hK2BYu?6V*u6J6S0`s~qjK@o5Wi4q)B2AgA#QXH zccqi(fcH+PsJ!Uu_VPlU^aYM2I&%+vkEQoh(n^zP(c)2xb~XbZ!EdcQ_Fk_J)BK!_ zWtZQ-hdXjCx)EnZGiJ>bC3~f^$=v6p$8~XjZU$ad$g`Gt|M2^4`(5uy@RydWko*U) zt^noS4cabB1GMwoo9}kIj&!Qz%12F)L1^(i)2zY@UjG`lbTPc3$lmxt3k3rWPMX03 z;dV5oVOf(lJv)=|hWPf+~Umi#)GUCmbTb%|`{{f*G94hp{9u~OgpYckw|$9`h+(k|BoKETIU1$iUtu8ezxDK0bY5Z ziEyg)G&kxOy?i?ZXWBuLrSH#%RBbdopzfge$Y`kMsU@GIxBt!PJ$CWSAj*~`T`_W} z>=3PZv`EBl1@TIAinW)%3}UzHKjjLegsz@a4Y>C5XDP1 zP;vMHI}=c5xy{rA5D^;CRrBQEJYe-!w0wTK(#G`6zEadc%rO50^32#pO9hSJ!Gh#} zV%e8T#=6Z|`k2wURAWvm3)f7+K-R9!Xu(($vQd_v$j#jwe&#(E`*}JfmaeDJN+3!2 zQgzO%T{O+Uk0X!ot!cfNX9YDJ9{M0neNW0CmzIXKmPeLu(prvY&o6#k+`&CyN%DKa89{8vC|ceo}Y0JPtruT z?`!fc?IXso94rn}d*+g)2h@Y97k6sgjE`PYvu|vmdXjHfH)$u2^ES zT`!(ab~aR^j6JZW^Y%orWJn_F#h$3{{%qf~$#k6P`o5Ej5~ig@E2r)}zaKeN2GyF& z5%ka5;_g4r?*O6*2l zJi3G)4K*emD{SaS6mYn`g*LvA&&w|68x26!Ngy=kd?S>M+CPz;{!1(pupXMY0zQgU zeif09k*>^Z^HOr9O~sA7;->cmOrO!OY;9}5y?N;D<&?DB142vJB43u=59*VmQz-7md{;abQi=&NRm`qJL-@RDEb; zeb_J@_AvTG3&pNS!rOGV{@PD|yTd2EJ5?L*(rwoqB^{~@5AO`f7Q2%8y!Zs20{7{! zuLzGx)0vB(-T5IJRAN$YFP$z-@|)74E&38soK3Gf{;SV5mTtHW8a}c%Ol}K~7GSq- z9OP%PaYY4syyN>qMn?hKiCq&r9ZA-*Z%oRd>g$31TW))mT@xQzhLbp@`FXtYp$V&o%+{Of3uK>8vi`71X z>uX=*@yrF?D~d8^f?oE?AM&0DUU_L8{PSEgC6Cek&YTq^d)k@2=V~`47`x@KZd%iZ zjgTkPt1tVjE}p&WkP{ViE{qZG_vFCv+k8}ljE>zm-~Lj~2jEEi%-V6H_i-Y%S$cKB zXMS%F@$26h-)_jnT$Gn~lCTVnd|0PeBHaHle#)ubC0^Eja)Z5M>fN0>Zt$18)Qj(C z$mC94q`sQCHuN+8*yjpHBD>a}wBx_NiTrBa3clhPIh^r>_Bi@>frd0c<|%nDIQ>V0 zl45miYkalKoL`TcQIJhA`>vN#Hlb@u2VRt%dHDdQKh66;6_U^j452D=PBBSf-=;FmzF;qe+wnAfu#En znx*u4K7*_QxAlp&cRBmL&E8v-JMNEmeedG%uU!-z#z8!#;C&*J{RAAlWef1h2S!Ng z0%$+&PC{6IZNu*Fme)nD0h+~y{F8Zt7d}^nwNQW`mWB+E<966Y2vXM;@?+`H`zL|w zas1-IJEvPFyp}E)C-miRRKU?uVX{c<=nb;FbfXt16+NK@TJ6|$;H^c)`A-I)E>@NG zkzLZHC6ht1#+A7QCN@J(?qVhc$Je6W$(|&f;loL#btxq77kFQqJ9{+T5uFX0$wd*h)^B%?zIr7LA!d+FZD$`lB)X@wdll6AG@Jz4ZqU)^A~Wh)``n4@3G z9?2mTvp9qbr8vh;%Ip-Ju4$%?0foFezYhwkwxJ^jF##WEF;1ijKs+sNjx#UFd<$Hn z6qhGVJH2J?Y&da*gBIMOSC%2MSY8!k2dBf=`TCRNBoyOxV&eSB=6lYD&-qr&IezJH z$%{Cnl5GZS`QN5*h1i(VbKF%kT?^X{`R`^VIHSGgNc`oiso+#(|8QN%_#B)6oE|$! zyvKaH(2{Ru=F`uhfRB&O<`acqeSjjhAcMeku2w7)_u1q^ae3H)l0xfBgIKJzE>ia8 zJmLM-U=LU1yTiz_oP~Z)G>TX%i=L0aA!%m~$;gMFKcMg6)wbr2tZ<~^M9ZMbC+bRJH^A9`3x)bmzmTB& zUSzK)K{r}jnGbb`f%KNuOT{#MgpST@eqBBCpGZAI2%@Nx=FE%r!tx}xI~!M&WgjQ) zaUs)!Ap>W#f6U*ZXA!NG7IV8peVzjtL{l;9##KSEVlbwt<74#^mfgQ03 zXfvV^oZ%Kpv&_^Hdyf z>q9|#J+}^C4+VccEt@3BGG6~Yz-h;VcS~AL$9Xb2>IRSx9mh zKAZ0(Mi8XQ)~XUvG8FSJ8tr)60m=^@O`#=q^R;g^=duqJ2U5{FbH7v+X6m&!Z}~lb z5=oihOXGeW0lnTXU9e`1YFzqKG>)66SsDm7_c;dO6W+eW`4!3^HHG3$~AVF>P zp1Od9tv!}oFEch8e;KPGwn=%3p2y_UJf8Tg{m4)&=@sWZCjV~n-EUs^+2%j=ZKwr# z<9ivRD7>lLZp&?~!_J#p=UA4G??P_k-8wm6`aW0J-4`o`Ozx4arJ-cXTw_2veDrF*d2WyBSe0caH` z)-a*6v;kH&By3JL%1hgKw|Uah{k90ACl~$`?8v{S;0GGUrAB z#!6LpgKsd~Hf$G!=OgTB_vzS8P@_t!g^qb5nOx_tdf zG;z>i{jLV!0~An}`!vNaLax6b`ecR!58)$!LaDAm=`ig-I265DLe0fW$dQ$0A46{WL>eRhaSeyDuffG z@#Ai`w({IbEhDUlPS{t3EvTkDX??E9a{0BAe)Ja#V=F|ze1;{Bbe~LlZiVQ}rmw>> ze>JgVZx9+yS$+6{Gwsjat!f>+3EpM1f|~^c5v&Y?f{HG|TvtzEMV0R-eVqG{TyT0x zL%H~p-`9tt6;m(w@A<3{XoB@S;(GnpELmrT+n9LeNWu7er+Qt-(V~KHP?CEo`jKob z-C=c}4b8e^J@Dape8{HTn~ja&o`kjZuFI2Fg2Vh|e0$#v;P7N4s4)HmmV#FeiE7Kr z3p*>}t8-SB*9wx5+WVU6g;+gEYN-UQP|jR)o1=HP7In(j2H+GqIs%U_Vz#WfTF{`j z3?{I_cftVLtK)q~m-mStLY*Od6FGyK^=1&qCv5uCZaNM7Bj}oo4Z_}CSyyi{t!XCb zsfo(#ow;eojjBAIC%khOt$~`8xfimef64q{155j<7l=>4&4k2c$e5Go`fe{UkKZ9& zk2fucFKYgdqw)H1F)V$pZ?RBgwLtI*(m!~asA9-kC{SlcHBd|tPT@>dB!scvKY5OR zWn^H+nP=1YnK{%4nSC;nJoO|^%rR1rmSxY&zKzhCw6;oj{yt60wQoilD#3=|6dW8e zbKWgi+V6!i!R*vZ9Q`v4`>mW;p`t$9)l_e|aI$7qg?g)V#AgvLGd$reP1a039^R6y z^=5A0vx3oHlw8+a>D8-0N5sJJ0^1_AWp>Ynb=vxFDjw>lmffAA%+jCSIW*&YT=qDu z?lPK)6i&W8ut$p$Ph3%pp)9iB_d2!plrN^&g{EV6zJ5fF>{|6B68)X{mC>jqbyl}t z$xF8w%5l=UNwd9n+bgqI{ibR&6^>EI(H*=0%1@8PjWsPI^b;iLC&#*;^67R>(EOo)>bmr!joe9ko}O- z3m&u$K+C$ANt~jYS_tf_>3+UIy+c@m=qwt@s0znTjcZ+KR_%HB<0}Z7nO!$N;O0G)<`U-AIZI`5}ozfvMz;ku*IMRTiwU>B!iA z!C@!{QPj)-yH+bKGXJ?!hC&9q+$KV153)+yK8~UoOsVQqyd-no5mR0ySvAIj7QIVg z60#n?fPRRp2-@~Z7BWl;zbC6N$j7(bt-P@#(I9t$mkE3OsF&oCbM4o^ zc-9%4a^E+(tE3S)_GhsTTtUY&8AwlKcXv`L>w-$D3`AXJLaOynJiJj7qX4HXA+7TJ z)Q<7uZ}(+GZ}?%TN+G@sOns4!G7qAG{LcewStxWPGnEe6RHZ?jqf@I~b^MRk!=T;X zKQOZzs`e3>=|ADYaB+O@I@U-4tjiPY`o8KVnhstI$78ojJp`*tE^3}L32a2An@N7#{=k> zG|Jq(tVha0uIY5M^dTE2{C;v96V6mPxJ8X&K$Si*N6XhjBG#CiPTc%4!{LLH6ju_i ziGb8XHXI<|TEC@H1^p<7ZulHQX|ZJMw1)i^#9{8!<{4jY<1kb!1sUZ; zB4Px3=s6Lw0Qpmc3tgzL+kPEes0XQ-gB>(}Wd{+NRPyU-zvGB;?}$$=R|?y%X5v6* zL|t$tcJVqiggm0nh$lY6I}5-Ot5GfPM5jmZqn;ZOOSvWb{&DiYawikP;d{6~M@%&b zX^^n;^-@MMd^G9(e77#oDF^h3C!$;7C&mG7O)x=mY}oqJ@6zg8@K^KZ$yAFMQXn=N9q= z91GQh4^)Cz z--JT&XyCdC*fM>n<)n4-mcKqIU`t!kE7#gr7`-T-sW0fw;Udl`riLdbd z4&z&*y7@}H?*kOGsA2dvs+-u~Yhme#xe)08(BOvi*sH|u`!DR_ppE!UYkbf4SGY0T z=^NdDt%ICB0ecJqu!w#P+DmO23HQ6_7Uj7Sqy(+t#QpRz4IJ=c=8g`hFI5)|T)hFQ zm4B+>ri68c9yH_!e4u>Sg8PtMrU6P)5FHXe@(^z1U~OyyS4JiyYh)>|ofmb43WqOo zHS;yJtAqZXGdgcyUx*TtzXd~zDgYME#KhzbrRvFZuXNO+*t?6k9W*^Dh z2+78nd&@|laPCJk?r^wtma$~wcIlGeGrBi^CYRSgd*7E~=uDk$)Cw&CtEEm?_3}?l zu5#Y&!*^wCAY7&2fD&Q&7ZOiwF-#BEJ)`ATv833`CHh|`AjcR~fh)61xFDVzPPX0Y z^-V+=>@je=1Ng|&ZK&uZ9U3J5DX(bBUX-NJVUgk#Ayx~M*3zr9!VLFjJ!FwkJXCf3 z{2P|!sHpKX zZn_6i*ejD!`$1@fm@TV3GMG4Q>jI3et31S@KOs>;7~abzqs&XJ7DC>8_ww7lnebj^ zxhfM04j~q@7)OkagRoH+_LvBXJgck!hY@mdy_rxU+`N!xU|Tu!qg-^Rt)GwjmJWR1 z2h%b>CWbh=AV%;bEbo#a>K)I|%R1qf^I*Q(XDlb>FdHT}BIE9LoGsJvmZ7al-9yUn z1!*sm1^B&H@Yivo*kSy2^~R};&v`zuj? zGScTC_cKyAA3!xUHV>wKaU?x70Fs#-nGl8Y3+VLmUlexJKjk?K@uW#UNPrrk-dnB zXG>L%A4mJ7U1|1o#F0d8U#Go38H5ioVBx%Foc2ALV2qfX)L6&cuoHp|XJA!Y?9z8c zP*)K#ywCjhOiL83O{QtS&!ln|+>WpaNU89$ppg*=^+1O(^ASd>8-QlhbQ7NQt7d1H zKPC@P8or9iqnqUovfYs2*{9=kn=8yC_xw-aJv2K>ZV}nsJZ+~7-u#+1{+u9#8S+*{ zXb`>s1uC5+`I9YL`Xq>IKwnvGl>vXP*2UOZ@?&Mk+d3Z@Q69t41|N;?F4o zn`=<@VKBc};N(dXth^M=$Y@{w1PK(bBMx*IJI*Ct5KoS_ z9WH$#Ax()~w-_(6<9;CWlpIvN%l4Dt{M4w ze!*b{usS{iX}&nv9T_mN8nh$j?o2U4D9Od<6t%5CehL{XjP-}o|7|#^kMI+N_`1r@ z8fK`4)(-f-JyhmBaxCAXIP2k+4)_jZ%2Ym-E+c$L?xWO5KKKq9c^r{f4#q6IiX~=>vX^+8<5^>N+1`T&LDSVm~Lb8wTpVv-Y0n7Q; zn3|a%)ye=baC(%2fftmJ!3!j$sf~zOM-Ai!*&S-XuiItp;w8d#Z?VJ2)G-&ckIChl zOwx&DY`VrsIDuQgJgSWdr!gA-MLKZ0z{F5PK^Un6&cxSWyGL(}VF%dQFc_T{3_LAD z5Hu1m@rO5Z(t1R=NA)Utye7!On22P({Jm!wA|yf|>jK3M<3)M63QE2Kdgw=t;9URK z``LYjUPzGQK2mLi!8UL$0UR(`?Ib3SG_jMy=XWT-B>TuPeB}Y06Q{=!l`O>1s0Tel zijP44Z#!bEE=lw=QTN4x#`lo3GrEcVIlm4s1WOu`6#6g{^npdOJ)%nBdT5dre*v}n zxK|Q^!!uFc{r}i{6KJU0_kVn53}!6Z$v*ZaWD8}NvX!MGNr*&3mWU!V$i5aLd!>j( z5oH^)6pG4Lp+Q<@D^kM$x;>xm`#b-0Jm++t=gG|be&6?XU)Sq;y4=f(`zWxH|$nOtJU>FKbuqK3_$MQQjfK6gLxSbpXQrEVPl_*8MgvhZ_<);PP5i)@P6>CUZq)L#Zj5d zX{5+KC-Kn?+QIF*S@rbRk823+GLW?%x+`#$#d>GL?X5&W4a>3E z`ipCiFEoazb%MBTdRWk=CjfBdOtwA>L6oy(g-KV?m6ZFh6jCgybA262I590FHu2} z^teuW#|+4e=R+zYU8C&NJz6B$-QY9ZLvSOy|5TRZ{YXX#!w1jKjMzXvwObzclm>}B z{0)r82fzJ-OSRgtb6+~X0^JIy`%=g5-x#6#s`2K8*?3`$=(O0uHq6c3Gfl(i?bEZo z6L#9?W)*Ijqz3HJpSNj4>LC z04-hxA@Wl&+?@usK^k-zo=x{$&7_y`E;?Sk47o@*=md4W2mE$_gaI*j8p&XDSUw6p zhi{)hb<{^gB^*t#57!oUw79@yXEg<;`#ju45+Z6 zK@EA;`_28bKFh|7|5T)4`q9BKu`uK1*Z4E7Bp=ux=;9v zyP9Ak4(|5Eh=qe?p*w8iYKzYH0$|E)lVvX6V`LP$@0$9vc99Woh)a#^)YUF-?q(~d z#ul*kRdkB!^WPOm-B>Gpdh|@_7D5TP^#RJnJ{*w1JPt}+3z5@^TN6^I zP)TWIL98R=JRKiyCNE|iHX1v! zKl#&y1Lha5ek=7ksxtznI$5U!Fm-gJ6_izMa6lU?7ROKz4l;yMPFfyWR7cd*AjA(m zdM#Qb=?7C-O7AQj1fiPzOThc`rx1ys2u+^Wi_HPg2J|42T z%%4tXMe*v1nOnD{iy;n8(v>L)F>)Vz=yMd!?4^IeqF91T-yePYbN;I4yAAAHA@Y$Z z21Hu;jq2k5o#W09b-u!$TskRug*}+7v%uPy3;Hgv>Ka}0>V&E^`@EDowa}26?^=I7 zaX)+%&RD|`7mR>!VUOp(ce*WzH1N3eG@c$-f}@u@GuCieadwr7Ku6&ueEr9t& zhEOP{iM7|6VpO7I=QZA@%up{!-6En77|;P<=B`xH!;^%1Wok04jWc#(_rheyG+5dl z8X5qk;WE&oje}TTx^`SNHL&_O5Q&7pKqHOzgJYv$in(W1&re&r^PcrY>!F;@`yXUJ z*Oa6!a9NVdr-u|mK`m72?AkTVPiJ z+B&G`C~6;H&fXvwVKkYWe}5Ew^EjxSlXr!Qz$6kv!`9|<>W{3k9R_crVKNP~g>^)V zsg7ahW6T9QkzYLQq8Zx`TXtXFzMV(@kV~?6gJmQAkIqk6@u6vE>*bqgzq|@q{9uNF z0~P`Gr;#<;oW%C+6oj6^#bkSm6o(W0h$2h&ZirujTK9Q4;|}Bg9NPdw_8s6v4W!Fj zAB0IUUq#%W7)>DkH#p0k{gAy=7V@<~0yVt`&|#5CshXr;nRo2V+|$U(L<2wa(GPsv zA2X!OT{+G1_>i0+TS8d_(M74;_6I(4XD6r8$KE0Ao@z?a$H<&CgF~6yh<# z!jK>sLcG3tQHgd>>=?BS)38!ai#xU^Wu9TJ{dek1C$<@5c(xkuvgz3>x2`SI%Pwb) z49wz8L_&?_4_7K|fcbU?hGK{-W;b}oj`x1)CXCqX02!34lYm-6My=WlICg#mZ!-CT zAv~FDA-5U{%ZQ+Q7-wIcW{^QdCcCf74$2IE6Fk!>hKxKj)Z%>4yS^goqJ4ShvD@ujbtn8RbY2g|m)$)u41|r^wYVJz+A@S|~@9*jkm$~@|Lc&R5 ziRyw^YaB=Z=vmaHXG3=lg##ssPrcL6hD=FL1Qt;}N2iOw-FJ*aNeHvoTSWMFp8+;M zC*Nnf7ZzYEksc%K1i0BIqy|^op-6@BFL<3xztSOyU*-ab0JzMRS}B>q4cbh8NsKp$ zTZSt4^JcYcNu|z0@%zeKnU6D096s?nLHoty$a~P*ol{58V^I$#8ju=lM@wkv#WblS)-#&Qv=wYv+ic?Nq-p~B;71A0QmnIr! z$E3eREs*6>-1@NTqGOl1<|E?^&0*Ujs+r6Wv)Q6}n|30AN&l$&$ttT&m zpX|~GvcKGRuaWbCqwC8Bw^UtexKx+E0-0{eyecV~6(BasiWK>|m7hm$q|v7sxDqBi z5~ujKn1BM%0iv0sM&j*--Gb;n!kV@Xi z)vrYwW&=OH*ngHo-0$y-KbYPp|Dn*Pz}W+0?Kk33|BC*TP`q;8O5R>nB_rigut$|VywQqXYQ2eg@qLgn66jK* zJ|5qrab{d6M?az&m0`0SH@I=Ul2zp*wOstwV&ntNe%Vwub7jwTz3}s)F#Yxe81^1& zqhlc2+j^5B8~d1q@EPQyy~C#dV)aqt@OJz@1atqW!b?-ou@=br(+gq`+Xv4WSlwy68=J?6zmmn7k_0)PqD@d+O=AjvLH z0W%nri$KiW88Tl4y^68jT5-q7ew-9F4(KS577@mxGZpfc;!5}FMeuOtv1yn;WKH+? z!xbx-w^I-}>k_r~4bT&9&ElcKhWF=XlLEMa4>v;C;YTwl^Rk^=2+1L&fX<8h64jYM ze+KwLlLOV6yKqwEOL3=O-zv-Hhl$?7g)#a=h0tlMeWG<{ypf{OHRZaG25Q;v^ht1E z=BgLIWPFKKH|oER$J&B(8RvC#|netKRvZ^8^SyHE7uN-?TIj5m>H zuRtU4;JFE1#hVbxP{R6O1h5?K_d9BC`He2OA%#4Vqv95$U52A)B7Ds~E)xdH!QCqg zYQ%Ud+m{l8Q7?@NGDr6j| zqk?Da&C#!4hwq7YjDH^|fDyB<_d5=>^@sAJ<}y($IpN9KYc1?k>lI7O{w%ca4?ar%Q!xt8&5nbWDZr)f zOt`byFJFXG*z1!oTo=zDB^Y_rCmI1c@nCnO{g6CE>hoMpX0Hcs=YD_#4?c`888z3S ziYjYXI6iz9(rtQ;848}47Yz&GS3Qviwg~uu1V=6{V(tys=ls zYTGQQeZ94$nBh$*nm1PUSK0J^LH*&~=}P1+JDA|qbe`Cp{pH*Gb*C?A>I4kQi%~wV&h7(pk}wl_9jUQla!<*^7ekw1UHzXZf=u&%%-&+RCNlVT1wx5S0Fp(ps4zLZk;7tbk z$i9Q#?1uFkUINQgAuYR)J^40#ue^8S+N^z*)``2;oJZG02D58&qvLMQIPAN#osKam`SSrN`V&>*>iX9Y zNeE~E*%#o4m*R7F=9!$W*EM_Oqjzgf2Tn+1HR=AAv&}qzQ~hLoX3EIhy4+6j2xMc| zv}fW(flLDa`!C&8t@3x3RQyd9)vpwZ1@d^8$ia=!=Kw~K{sQUa3 z4RW(AAP!2<4oe6z&Py@Le>WK802!fQmNRY5nORGi_M;l|ne=bjiYAvZdTi|WEGN^*{P>@h3=S90YAQ!5yn|=< zLzi#ft_MPVM7$Ly!o<+C3OvXj0UiZi{vq1GmIy7Bav~;p@Z8*N!;TrQ2}Pp^rZROm z9`PH@A7`mIrygs2F%eST3RipyH2=L)3NHRRW&5euxwlZ?xd5nYOJ@P!rf-n|Ww*-& zoA#-h87sxgV-3=NSn#B40@elG3AzPce5?baZI38#*P)euBr~%9_s3kiuiu89BQoJi z)FhSvggsmuz|HRCa$HrY+K}|@cwG=!#t*QGTUTSV-T0KmM^%|x7mW)6VCAVdFfcBL z+A8Em5DREU#eMExgcxoN=QwEALO|JrSs#iBm*lY!2 zk6Yp1dJMq9o!N;w>9Z5J_b{EJAxK+%?_{$>rmzQfj?&{@1Q5`4+p5scC<+uAd&Bi( z`^l5DTdkNHHv2iSZn2!y**|-etAao38q^CxGb}QKe}col?uFEh@?-v@!N$;IQi^%h zHw(a_>mP%OKaT~PH`Xu60g*i6FhlpY3j1oK*`{Akt)jo{vG@1{fqrW9ox!O&?9n^= zV#r+T2t;}jjcFb6m+<{yF?=tVE%jT zdEQTC{y#<=ohwktPXo4O&E|&PfLT|}7T)BA>%DuRctNx?k+E=)bgM$|1Um9p&I`Fw z2nd$sn13Q zSAJxgOhIDa=TBg}+lqqL+yU8Ly-hOKD1;olY>zp-7Np__(u{_<&Fx@j~U=cIRppwfQybqFZe>nU0VUgJ)L}LnO z))00-KnQY2A69S*;1AqVXDX}yH^%|JNz<|lTntMron1SN&tj`xpxXSPQMsMCU$y~} zUMBiR2g3-*z3<-l{>4wax$|{9gBE|{!rQjSW)+=g@N!F~Ki^ay*^=U)(mZ?Z;YxA` zWbC&atSt6>V{GUY`opJV_Ewdq=J(o{nxLC9Gnv}f&_ebsHqVq7v5FGwwtybQ-YRMe zq3aFC+$T;BjU=gkCqT*1l$dx=9lTZVD3!;Z0+tNtT26ke zaPHj_j6-DO#7px)?7DO5!b~HiP{X!YWaY@Oz^HTf6X1J_>r;uiQ933*C50+wRQuMP z9<~%7zO`R)DUbKsh{0+TNQ@8Mb=VOKMR1AHB__RdlTRqva4{xs*`fe4(;qkpcDN|H z`hY85DpS%7IA3O$NOBZLNSJ1iIZR(qf}GACWt!ZBe>{QsO&vQ9!+Tym@UU9Sa6)bz zSGoCes($E0teTY$k7xaP9jj$Sb;kyM4W1bh+upo#9VnAOU8AnZSeaKkJ{p)}zeqNO zij_W9!b(fTGyU2KiM!x@H6=^%BkvFF1i{?FUQykHuD9-?wP&qRj#UUAocOMLRZZ1K zk<}nfhU?%hCzhxW%&2=XZ956=eg|2wh5~wLNS1E)DBT_^lxR4S4Kh#-26#X28aXoX ze$>(X+5h&%oJB){?EHf+$2l0oNo1voINaFFx|E;b>%W|>ebQ_*{{`?`oSeS`ESG)x zUj=v+*15Ry)!RO&(9bM>`Ze?VlrO`dmpdK2q3O)<>qVHtW4Gd68Nj)gBKV;g`0tfF@cenAc88xZ|+_b2lI9mW`1wlRLJW1KQ7ar>T=v5OtR(!r1E5r=c zp1GGP54G+(|Lu(Y3)GE6!lyID`+Yx584p-SK&_ndgyK|0{-h4FtpOV+gKc!hL7v=# zF8+F;5Bqv5j#O3ZmITcPW+6`YTn(QCmdqp1^>&zDS3ll{51oDVY4kcwAtI?7d>$Nl z8@VeWsy3JRWCANG3x<3!9(wI(O&)7dfzEu!(WruR&}hjd;FNH1FZ|fWm|7d@Et{#z zRJ=Hl$O>1N6jao#UmOxYn!#z^W>GxX0RPf=!4U4Q8#%nIOjoF|)$o&oaZx0J*0$}6 z-=9QxzmAdyy``uuh1qA|B+PmP`pS&0y$Iu2J-KPRiQV}%`>3%ahxfS0Q5 zgn_H;^}itIyC1$9T&tV{4rucpK4-WEKlcDPm zv3I&G_2L6%2TefPRk^-Y%UlbRLRHsxf^Djx+mO+AjnAWx9?-g+4Rma(TnvA`-s=&? zv(2tFe~Hj^A+oti#jR8JDpRuN)~Q=NT%p87$?(0|iD0DuSqd?K!LYgN$SA5?&t4Y{ zqI`|1YpzhI;nwS&mJoinL&i_H)Lbst{Wg#GGaCVS7{@JdlA%Mn8xCzf#auP6Kn-k0 zqhXHN-9zlr=VT}7ehDBOj2{I2SFfVvyjg}94Gdc10E6_Sn6pc};0vl9zB%&%iSQBd z5#zaXBdo_W{;4bez1qQ2G|%c*$b3ha-A3MMb6(^r!}ET%4!}_M1St%FaUm0r{8LB( zzYj)&u#mT-`iVUk@5xLg!>x5g{c0CUG^P7viATR)kahmU%iXu3g@tYSgIc zaPZmGs2lOik9QL}GPo&P(a`QDK~E5YNeC_21@o@0#qntA0txsjSh{PlbU6_z(SXK; zK~RY9OH-6Yq@TBk>CB~&vQLVrX;pvze`*oxEPnA5pPa2Zz9&%G;oN04oDdVQBInKk zNj*mENZ}yb6epQ{Oq$@ zO(*Kk<)^nu#cA`>F|rDL7=A;tu5N{7`H&!CpfhHhp}7sTEZ97-aRf;Aw0E5R8@A8^ z0VfBFvy(W|&1 z1#Qo+W5Ha4K2^_R|7ZUN%T)R1o7Q?2-0&RRbtJ@Pc%hhct?g2!s3J-0>D9hUi3G>- z6h1*{8NWPcM15~m6LYx5^H|Hpa4=6OJ8vGSLD=hEw3FSXIt<+O0{V1+h4hM>^Y<3$ zYI|2xnxo13dwv2Eo#BbG0a)b`I5pZ@#z6`WnyN2jkB&V)YeusyQY09vqh#Iof?(m5*+xCuF0hLXNHHk9)(s!(4SLDd_ zbVxNec*4z4xP+x@KJ~F@bMS%5-!g?^2kh=Tl$>+}Yz>XfeAI$>q3CX{#(Mv>3BU|_ zf~mo;>y`GS!vJoJVk>M8HI&(4Vn;o&q1Hyvs6AOYGcO(AA?Z zn5$&KLPCVtOc)8A?CiotTXeR%ri&zgwOyzzVpi*5aJiL9A|HH`#IN@EKFZb&0;{NQ zF8)1=bs>3BL+aLQi>6;jc^ZP01*<6{@;!>}ACm>AkAsAp_Rt}kMtj1hp8<@bc4@I? zbDSY3!qt%936Yh3g#|eP4tpFFPU-{ad+H9Ph9f`{}a<8L@vv5{PVB?$;j( zO-iD^XJg?c#E?IFr2%zIiqvn+;2;R%#kYaiK4U5mn4}2Vl<;mUto{ukeyX3MP*G{x zAc%9fe;@YadB|3cu1%)wCP3_gud>#6jV}K{eR)HU?c(BzZ*i>aa~sB&eUooxt7nBw z^kdSaGspN{gWhyp8o~Ja|KQ4UFQd;RNL%KW25+3^NEk6_aY+ug7Qj^EK>Ba@+%-J{ zCf>L7s%g$m zpvZLx!RPnjTT~Sn@~f>|*&G#X{K5a1a7m_vq2MrmkK)Z`ORm2^*!N3GN|xRyhBRGI zGJ>u`2PD^Bmg(#pJa3m9Il;HfGANS+rxVEaEh)nkl*Eg*6ue*8NBmb?;0-3KrW#@c?4q$P?pN7&!w^$nVX}+rnYBAjT2ySC{IGLd1CkRdMi!z9Nu`3qu=! zZoS*GWAc(r7Zi3hM~Ew0V1spkMXG2`7>`@ok~*jJKfqp`CeBX>!Zz;`pCae83^V+7 zvJ38HJ-afV)AmmNA13JGI>FjAzp0khqL~7?Uau-Uj{5G{3PY(_{`|czom7K%HgI za#)k}9bE|bL74Bzh4pM*;#T6%*|B#`#SE3v-{00sNdEUqBVqmH86uCh54U3|7~^-^ z!FXWEsrVU@sicv{$Zs8QOuF(%sBd0Bdzo%P!&g*cr~%CDpT0p@!^xyVS4nPyy<4{E z5FrYmw~q#Z*;U9@nWLTjFxS*PQcR9W$fW9Kxq*#t*3bO0I-d0*KaJxlDbPieLtV7) zeGYCyZjrq>7x9vdd0;G*BZyFY-E>^ysiVVc=*Se zbi9Hg)r%RUP?r{VL}=GpQv}9NC*aOq2FU*z9h;6z{I?i0B%GUhS7!x>K^l5foYZtT!8ygF-_In66tbnjunrK7LG3gL_L!F8cSG~!9Il>Q`8v?qqCPL4JR|ACkF#^eRJlH)S6FvjP`gag zgDX-20g#(2IqRY%Sgy%ly5d0dwJ9Ng>?;&q7hxf<_e6hDk)pxFLA6LL!v5?cY!od`%!S zAl-ot!;4uYSe>cCkOP>JIu{bz@(z=5?(I&FppcV6I{!NySry1iOtEv1xnGrg4=>`r z6P%bG#Kw_Wy?EB_^$-ewugYCUdJR=~@ zwm2d|rXXTHG)K$dwL*z`8IhvM!yOPB=_AGCl0?pb7PI*H?B*ssqBVVn^~L7PGQNg( zqO_3nci7tbzd<{n5Fq^*2BS*BED=2skijc;v@zV0~vW~ zi0u5Ggw)LTSIMt8hN_f8Pws^cq=}-JPiOWS9WJECQ-{52&BXEEf}{?tA3q(5iIM;F zHS%B){V!tuRu(Ks#1I;AUSrLm#WCp3dO5 zJCJ&)p8aeM?=+QDX{&|W&9@jf5=BeKZ)N^CVkQacUv!*5FQ9tDG>?mTW(CUpu2hdz z8EVUpkRWv24P0Q`5|X%5#R2vkfoT?;0tjv-7K*7q1@L|Fp zSl%6NLoOx03M+{)nz^-?NG>oR4wFY(ph(#6%O09l10a?0E#*|%qwvgC#d0V4SiX0 z4M0WEy>Q;`{}>FD@j{?pri-;yh~0xL6-V@GvAs${6jC)3kco=`61oRcDG7^HcB$Y)(l(7&f zmE||v=ATgD`@u~8Fmce^WlKLq4bUVzN32PKI`#@2=WDN(kAm{$zkn__77+uFb9GOo z85H5`G3>H(!~T3qj@DVDbe_% zGe!8c(-Z$!mmf|gYrmg7(RM>?AAG4jBQpsQuZB?cTl#rKDHP6D;bj1Fw-sYGIMIa& zij1~1KR4cJb5-TusY}*B!7KM8Bh*V-{i=0L*v(!^o*a*EoruDnsxi^MJPC(9NjwSC zs<$rp+N_K5GrteI_p8NnRrf_`pWdgL@E7B&qkTb-8s{!7J3TqW>M>V4yQBl!p8o_t z_=6657pWQw<$G|&1V1_wcX5P%sh)epLj5LT%dabZJMg(`utrt=JNEE`{@SeDU4cel13+5;u#bSb_?RgL*o}Q_^ul>s>A#GMsx`a z`rK~_8WAaYF&Pk3914jCLCQi%#1!smYwJja0Lz&Tag{i_qH!qSHR@@x5@3Z6HJ-(s zKxm3ZCP9nsA<%*OpUYg50Na%TdB3+XLWs5k<)rWtqsGMl^IABQ(DxpqRVWcXaibW*7#|YS0>0Anu?@OpayFE8 zMjPzKt^y(4b>HHTdG`UNj)G4urwf}#&3H-SioeLour#-j=2`Vc& zBbVC2fb^)LwOK@on0xD7b7N{|J_Lw&! z(Tf9Yk+>|<95mofx#6OeG`DzAU@55#;v|4^Q{~@6G`$IRpQVCykMaMBV=2h^$}KmB zUIKf82>Vs*J*bPz*d9Q}=P!X~wIH_+G?4|3O`kj)g8H8;c;#8WjlY4dT0+V$1}j9# z5#bd*@&f*F?v zWgh8oMYcQb#}$adE?K>{hzJmX8zKpaI}|CXr@5*MTzdoDF#8IkCc=$*8JLi7U>pQw zRIwQcNNMu6@=HL}KKv{muOLPBV72g9Re?`oL%{NrHfZ}aBNRQP?~p0ysu{|(+~YqqzRD!2&wW-$j&XdtM-6I z^xq8u_{0D69;hPC&;L~pOCxI+B!`rr1o|zC6_!d$J&;fR??*!&L7B>pqnE^9fRIHMsXRY%9WGGB zWw@Yv{0{uTw+Jpmz_>c9tORJc*Vh8$#yHm~WACMrft)^p>WN-Q*w3RdfQ9Z?UGWvK zqSe~{sn(eYlW zil|Mn>V9Y5?X3O)=4ZYMV zeh1E4#_fXh=0N%K3}*m=6dFjiHT7Q8@->$*kkjeRta42-gij{Ryyq;gbvtUJce*`G zGZ4hse#i}C^j)x}!p;tc;fLl=Vp!dw-*j2=s0JuSAtmCqaM-v8|W!zjPbeBNnv z#o#Bb0K4w)4Q>>Kk!y%IOc;~5tN7n90TeXOq&@(FW5e~&z^{qRgXk>o(WQ-|b0Tcr z_`&huVGub+5JBDY{r~{FNVN9hMgCFLw0;DX%kRFpTmP8F{QET!!Fn|++RN823%@P} zd;au=CqTU}K?uabKnQe0(k}+!6<-(MbeyO<9_5m!2klpzl8#*X*j_FO%At0Rmtmc% zAsnZTd_NQP9o~)jA2=drV~>N};U6^O4r+$Om3f2pIR#O~zi^1}1M_6(+0SXnG6m=Y zleAZl8$7|zCQHz_-jIn`q11>Ukfv8-Jok%!&P4DUsOR8U3(c$>H}NxXB!x-#%TY&1 zRufa^zzcE4@U5rBAg2(PU4uI7})*4t2A$IB~@BQ&G{maX@}WG3wH zc<9+hwfjbtpN26%PeEM>qC+H)1b9HM7`uKuv=B4oj(7=;M<6|IU*!q*WQD6^JDLx0 zYk#ges3e6P%)cdetb6FbHa(yK!q9;`wiakqUztO#m!#Re$mR>DD5JYdTG`1G=Y*o% zH-|+)pby;n4q}97kVH#Cdt^xJMFv`FX+JPeeFP}nG`xt&1&79vh0@n2(77!p&L5Fb zH=EXR`8O9p!fCU=1JM?{iY3~c(145yB3=mL)6bAx-~fV1+9}ZQr!mTh42ug#jDQEIUxmi zKz_tr(=YjA!vdX9qp{AuWBX{JVMJLhYD?Xr$1}`pys0|AJLY+uTZ54w8c|rg_m=KH zYELUx53kgVaYa8M89Doq`lxs2{Ud2NW>Izb`(@!n+Y;UPi6xEbY zVm&?r!EekyEbC1xibZ}?Ri5y_rijC4y>Tt-lD`}Ud>tTZMjTk%JBMtHqCj`}s76EK zAwSOV$a`pu)#l_wtqSRRw$8@iA18gMcc@4z0}}x##c>GZjyLopP??K)MmAhW;;%pL z9@$`b!mgzj^IXStABFR1N+M87+ZoX0BghftI%FrsUZViLy9g>xRgV`!AQre)+DCY9w(`&K=lrZaSZ7YDzr- z8HRnrm3U-Lg7QHTUq?GdX*?2Mfn@DgAd~``{UnIHD$jCr+G8LpnH;{)Uff{5}lKE6q z6Tpp*;#?rPP>rSM<#4zg}*ff!t#x!q!S;k6a_)z=EhOD~Z z{s&UqN@pVoW#sj%Ku|ts7hSUH8W76?-<X3XoORf7{mI*?4lznLd%yfBz|-A8 zv~j(B`>-0tdJ2ked8HH3)NOLuKa_~qF~m&O!01kM7y4ozMdaBxLkva<@BdAXI_vZv z>$8G!`22&zKEQMbq4e)X_fudU3$BiR<+kokEclwbUD1{yaQdHimf%G$%&4?(^?h|S z9-#F09knhSf%^}Z*jx9C622IwU(BM=PCWddflY9@~Oz zeCVos1my%*(a?CsBs4ytlJK>0GNUQhfI~vcJ$M0-C}n|h@q>+>%^i_^^N^}~=@Qo1 zzB>R!#Wx(YXEVZju*W%g&jVN93_P|E$zFm?02K5`@@&+yz>I-JqNsVikma)>6jz}V z)45s4Y>%l6?6_BVY`1NyhLSbM~VaC**;>pO_*tqdD3!mYFUknnx40ts<^VcH#gP@48|y=!@M&hTw6 zcrG#%9OR?p5gXx4Mp0M!(YpvFe!jE;x9Bst-1CoG^JP>3w7U4pV*4|=07}4CP)SxL z5xSM+g||RYYh6LWxjVN;^DbV^(V-`L0>HqxeOC1y&Su# z`%s*vZo?~Z2m%6a=QU6&hVm9r+ZcB2z6D2HD;4QvN)DABfMkvbsnrhMa~cc1bk-(b z@D37d2{?ymC1;k!q#)|2c5IpeUf@UEDgBk(vOk#L3z zJok1TYAbh*f{!u>6o<#--gzIteKG+qh9p(W$O)ioB7Qt$R@$C~XeyLjCsyVs%#GCn zS~0yG_e^&tLZwyc$3Er4q^&A#3v1gyx@3!Owgn8RoYmMxA2@f2{R27;2{>;~_&pPf zaCHT9h0IiUoq*%H*sIp}6!MYpFJAok0@m_}0d|m%+rhX)L-8TL`R}5|k(P^1-8InU zllKL!Mqk9b1bnoOsQ%(pi31S*iiT%Ls&^AjU7&%U2K5_#6K`8S26}(?61fQ zMAktB*D5e5{rxIwg#4pqD))^F{m<(o7~h@?OJ7U2rh;G(Oh)Injw_gY6VRN|rWUch zGv;$Ys^wS%s}rb*F&J{VX1k7%*5FTK$g=+Jht?;iUrn@rQIS%;_z#wlgje7a{0oyu z(IBoR8d$UFgJ-6?8Q?8iaTur030Gw(OAxSwoJ^z4KQ&)R{gSHqJQcUSJI~GLt;*9V zoX9i$>na1U`8=8rh4?Xpwf`zb8KwHc@G2BIzk{ES+&_^HE>tih=L^m$0tVNDg-X+@ zYpB(V$nnX$9sUwrMxDT zS1#-t1ydk*-v{52B#y=<>S-$y^xsG{iVQwtb!phy{e5$Iv;Nd$pi-K_jidXT*SDO| zsmQCUoH7v4eldhZ3Y>HRl5ySwq@xe{7xl0h!(3}`gLf=Fm2iMSisDR;LwRZj+d#sQ< zX18zzlnf^T)p#lMl)FvZC3Xbk*8cvRA@UUhuODoW2e%+}XpaKwDLB6?@8s^sK>W8Y zOcm68@rglwCgDd+DbZ!L@@Sol@5gTuC0%7?HoZ$G-X`T%FIoX9S|pL3QQG|X4!_6JH%jUgwbJ{*Ikj@V{<*RK>~*VStYt)9@jSadcPT9yq^6!UiHw28O{`FiC zBm1NA#;PT7C z4cWa8@;Fa13VifHS|NypqZko)#&fS0UU7Kx=e}0c#t~0WgUom<|`;s@MYwXC^)Hzt?!w8S9cGP zYLluj`xAAsNI$mmPW!A&z4nJu`>vou(D@xzG1>>A`QRstg`<61ElVpHUlmgg za&j}UWkqol221cX^yXY~BlgE!p?sGkQkhsg&;4F6>-z}5@M$Mff$h4nJm~PA`l3OhObG|*5YUe zb|K0CEJJLd!&N8NisLU1p=!?zE<){h&-a%M3Cj3#El;d{zFRkb86p(ap3qU8k)BZ~ zPsZ23IW6JHDEa;8bEjo}#_?IDTF+?mB*9?f3oT+LLMgFl|n3yM`mXsW(Q&5u)6nEfOK? zKoaoPW9q$0vVhe5MHh8gkzx+G3bJMbL)2El*=tbWXWmO0WTD)bf+q%IhhwY^TK2UC z(i@#n2M6T8#yV#iq1N>3)jHfUbLv=~&&78BZ4V$Po+rWU7V$pBX#32^m99|@0AMS- zO2@KBRU=R2@S+_jo5*pMhDaqMfk3OG6okM{24?Jn4Rc;1Ewq{W4=AOWbcwcLKK-uWL z{d*wPT^9G#n>`_It#CDw73Sq|svnG2>gP4}CO03nt3Gs0}K z3|JMhBO-gj$Xq~T4X%wc^-~KHMY@8YIZ<7VVyoGqGv*0SfXO@B^{QjKWzACy} zSVyb^`xy9(tk|O+4B!)y1`6J1kk8qk5|i}=+60JQ(`Lu+p3=W%gww-?>~w~!FW`5j zZY+|fBgX?@@3;L{wDV2SN{Aep8~J&wqkwGVz3J*~U**zo1i|Yda*|zo)G|WT-}s$+ zCJ8l}Ys+8GPc{G^wR9UP+mH(Seb;&c@|?al;}q>-Kwu2{Nzo!Ow6+47RSGn()v=vI zJlLdn&<-*Kvibl~-X1!anY1EErf)(AgPopYeLLvOAc?6SH9)8b@mkL3@9Y$|<*$ol zofly!ZiSOFXm}IZMneB&N;XR&NsR!9p8vbcVL9j)CMvw%v77p3t#6-j$fjIN3uLok zQCtAZ@4LIi60StX`J`MKifh`t8L_F*+%MCKXMu~L(y8grWPjha9TpHx+?{U`vlk(r z(cdnf;EVv}F42^lA9w| zL3>|*L0C}_&a*SPh_q_OTrB~OXgAEgDq~UuAq2mcpGz21Cm5&G;ScP(YjKZ3#M8wN zdX7|8T;6s?sLK8l>S0jd&(Ahxpxaq|MX=EVbLa%_<3}$WpixO74Q^WD53a7IuJygc z+XEm07f=jY(KQPAH_#=W0Tbp-l!9lf!c|QQ#sicjcmuAN135;1{cs75@KC3AHi30D za$^Ns$Q+u7!wk_L#Ppyu3@>2zg_anZha!X9YP5rfw}cfKoIBaUP|rKgI$J-rB}mli zmjC0cyrQ_Rb%}i2?cDhEpOAPdGF$BtN_3S@uc7^Rcy{?J5wDFNZuZR4Ayim7z{B00 zel~EKpmof<3KZh7wSVmlNsj6#fs2B-9w)(^oZB=$D}){}Jl{zhg7@toZog6RHsGpq z^mgGEpz^6hJ{JguA;34Gl9O+iR9wbaunU5Ma`&UY z*klo12W2N*j>`7>vDmaNyeT7?aaJV4n*H|X!R<3{+RGoZB|5)@5dnUx9x50;5@@a} zftaxeR3?(pRXq?H1%MU@;)Z17artmuqm;!K0{}0GCQ*N@ie1aeRA(p02^vMgC>(QO znVbOZEdZ$8=^VtHsAN5Jj{WwT<@+1e8=<+F)0<7AA@-_6-sFBFTb3(hW%HOW;d zuZsBbF;{_+d6q(%`7cnsJ_UcC;)`2Myvg4<>X z`xWVCkw)o_83E1@4`b?q-B^PEYxV6_ImJToatDA#Rr!!qyyH-#i)9}Hm!|;;!Nd!v z2TIWmwE{^gH`aGKM*PEUpPYu@xz$HaKP~4Xd_VOpLOvq71>4ikfVv$(i*RB;mn_1{ zd7G~K)~r~OrtOdE9vM(Tr z>-||;whu0@{oyX=f1>EJdl!^DV9nm|k>(6d4Ah7jKO-4C#6GnQiOU_u$auoZ-fS_5 zyqV|{hYr;(JR@Z00iMa%xAF4Mxv+fa5H=al&m%9?C^ZRGor4=O&A<<~CWv+@R7jNJ z@$9w3pgIO~@1U;(@zy~@FL4MR#k_1a+dOuCt@&M%*LOk&WxYpw6Q)-YbIBPgf+ipu z;XLgipmggL1mzbJ*0(K%k$xWO+EgRf!#?r!yGt24K7(rd*}r(QO52)N69w-=wSaZQ z#jqRPg$jV98+mI(YWwvXtOt{2W9+v#MUd_;!d-?WnD}u{l3r0q7N+wwP?`@p+7l!X zs)p#1X*9%>zuM}?rK1`8yrwG|3W@V~y(+Bo{y(bzJRHit{{zMugRzgDCc@ZLwnB_0 z$y%1|Dr*T@QyE+KEqi5)$WjSeqEZdUT3HeiNwS12+4sWpKD+Mk^ZPyj+{bm?$F($b z&iVPg->;QUN!9s!&azA(a#k#{js|`BIC>d{v7^bSjI0N7Q95W80$#r<%Vt8R2P!jK zk(vLdhZ|hD+v6}joJz|QMy7{BO@I{|fsiS4DDV!}wo}4xu9g8`Xj`TPop$JdmDgUHf{uql%fNoE~?+O=GgSIMRc~^H)#@z6M zjQEmn08Ax-sWdOhj$+_0Y1jqP&9#X{#m836k=A57KOVzcyf7UJUu2MBxZ9OrdFaSi z<2u&h*NP50RPnSH#RTW?nC18=Bl&3dePHTLGnyD^NCyG${Mh-dytWNSz?_6{Fk9QZ z=PLPV#)Y_i6rx}eT4FTg8d*~y;@z(+-$JD~MuOVquKncJ$ z;iBRT|I_^Ul~s;3-s=!a{-jIj=40S}Z1~OvwVS-*@P@{>0m04(1Zi zVB)_N$Cmg|5l1SbJedLQRMl3>nYXww|AADnm za4KZ7l8I3==n21-pC2BtMrknEGH=X(EnH|EL{ffd{yYfE&u*c?P(^+(M-m)T*K%D+%LeF)gOZmJhgTf zAEpb}sv?`)!s~Z4*>&?F3o{0(_xfgbrSB*&|D(NPX%R41NiZzqaEp4hiqD(KBprD< zBQ_bMQP_#@Az_6uIa9}{*eXA`nj3!HvMKEr+&@3##Bx(05Tg0MRu)uVgg!&d*k(vs zk26HE8ai`J+nR+(z%S(Kw@NZ&4`q{M?bl&QC*ytw(XQe?r3!?=5d`xtPn;h6e7W@0)yVU1kw3@~!{WKd2=vZve1s z()Tq(*hvUx-9 zrd+pw+Ej4_ygVAHDq-x4K?+-@(eI(lKb*e0L%3A|KD0FzklVu*G)tZu@i`h0K5IVz z4UtN=68^!Y85C`W?m@+Km-(kR1di71d=qcYsGAs1t%F#5Cxt!c;2k{3>3O0HOSmF zISJNc{z~EBT83zbby%WIF<5qgXm^*qfckGx8$J3LMoV`0OU?h-ZT7b)88K6I!s=yY zUJjyj;QQ}y%TcgvGjONKw*lCWa|@tVdP07$E}0%ygBRfizMuas2YJ4Kk~l?tfygaL zI`c0y#_^wp(*W0iL)+5j&!Fz3mFk;p2&A7)56e~$viz?C)y16dFX-65yF zWqhd5RuhACG0NB*+i(+euiB@69x~Y=YOURx`jV@zKqE*{#!twMtX?Lx&}4={X+!a> z+O5CD$;8{*C=1q6=L&P-2U$t~w!56KUS*PLpa0~UuU}IBxA7<<1Y&^C#Mdi%oGq6M z0jH^r(STD1iSb5sE;o8JxOl^08`lF3G!nHfc}oSpzkkFVGIH>|f|0t4^iVZmEd`kR z((6744%sr#zkjz9t|ERi(vU)hM~i0gXA%61Tk~+^o1tCY=a#*1`mTKA9*OZi{wB1ARAkr{GUYgN7vw? zl;oa)-xCDNiDJ{B@JV^OFY9|(%bFfJ0JYOcZ8$B;?(u(G0D)hv*g0Rl1dhem)BM|T zUQL3$clF4QsCJjN0h>3BcHM9xUy!i{qSW9UeNv?)j6dc+)q@(%4V@%qyUJ_&1KdPi|4Zd(FU{ z&|3o^GoT-DNZH$ z=B(%gFuqDwD%Lt;QRy9D&zZy zi6A+jSCwG;_U5BhlM?fK4ywKPAgV^lVgTRADu-6#dcOnth5Eo2|AYbh4!8j;7p7a` zV{EY1f(Ct)~{hAs&M@I~<2Uw|&{sRUI8eSE_F)m2L$)?(@2 zfMqpg{);`1a8PG+wo^k-VU|FN7agiH1S6M36%zf~*8p^Urkkw$9f#_9NN-Jp-6!i! z3^(!`L{qehkQnT9%B`zK6|(jexjQAW2a$Br-OEr=R-m})WRt=nXOJeDw!@Jfvn)rH z*SO$B`t{Cq4O_7zZ^!1%PFW{l`4(cICPTT-qeh|EchR>#;mFM?Z9t8eW`myAnLN_F zt1U3}&i7yo4cM5_L?_- zoRA5U$^;3Ix=RO76z$#^7krHWe{mVlyDyi&z%TPYMH^mAgC}qBebSR)$eVyOoO>H` zD5=F71(be)riBn+w375{&>pr;xqvm7nZc^UGkI#N3j`|)c!58`e0Bx4DV_$Pkr;!i zDk>v2U~IVz4c&+yBTK>KzKE+FEBpZi>4=x>BWa~b3=LqBxP#YW7CAPmQc)Ki^K{=J zbk1^Qf$85x>$DHLi!U%IK|nXe1BM+x2!mj462+nP2t{j{0WQICEBKAW7K`uz!po}= zi_{nX2eJ%Az`MifuP%*19dH;=T&<{k+%^rOsNdZ(TFk&InTJOJ?EdsHQYa~+Ts!kP zd)(qBF`mg=G5~4;688CD@sktuAeaDgT^u;>CjQ^_!W4#x9*~HDU*!N?wuzcffhhpX z8Aj~P2$*2bf4byba}(Zbkz-rn>?+a-LxG-Q`hlNzwiGCHKf`-=`9RuXX-G3m>zc~a zRH@tdr#S}t1L0AB1gNQxvp&g&ITI>_-u8R36jfik4m2G`kkj@@!tP+2JqP?~ z^{Z770#oAPLYy_G22)UK5R{#y7+li(m?f`+kTl@TzYBDMWFw{KiWdR+YwC(`-2nr4tLq2kRg}eE(}YLE$@M^dia#Um(@pU{~~-O^C>l{A^q+G z9a9b)eW$8ws7PWu#Xv=Y$glZ74-n*hdVY`HV+kyzgj_7gZTfcN|hm29{8dc;^uRAz5*Mxa%~AV!4fUN z0VR+~p&PuJXY}%j)#E?2KS%{sgOuZcvL=cNu6#FQyPAT>VX`8WtBDxgm1e9lZ z_IasxMCR=XGds(|V~4>$zm+EOrV4>hs(GNVOl+B#LWFpxz5@BFhpaeoVxEnZtWFXh zuX~u+I}-glODDmz4iF3fjwxh&Q z9GQcm**D(KS!?_ zbyOA!WABIBW;QGFgQw{ij(FM({a!^fh?IpM*BAJLVBO+*7N;a)9Oo-Ud z@>XXKdY*VCbTAO%ew}n;nE5a#lF*K-I??CNRd({e%1POhtdNu%LZ{g?oBnwZ(osef zEEo=EQ5hg?0GAmeHM0B*|DJIjfn1i3n_mtr^uCBe6_Q6+COasr`wLJL`-tgmKu8pc zu&H=04a&msvyE`nOL##5Tt>sxf!|!a5F&IBxXeN8%a6~(siQ+Nu>c#erNdcow-Jw* zz*&eWZvi;nFCm9v23^Y3OSeqP3d@H0Gv>LV=sQyfl3_wi5tqu5Fkd%yp^V z5B9?Ec^-bx(Z-@YA(`C6Za`m|ueQOrXwC^$PQ?ImUi5o#zLcYK*mM8rikVZToG@0D z((_gfom6|r2NnHLfCk2O$xJw2Bx@BuI^2-GAUySlk!oBw!TPSoHPlU#JeY2BY^T;v z^d{|HNv|Jhr2!pGDj3ubA5`k&WPwF1o|b*;p9&)t@w&3Pkz&vU4dmI5jjF&_qhW$L zC8Wius^Yic}s@cqLnlSfR-0|1+yR95!4SsXOlpq?GZiC0{tQ z5R-`JC5j$ag&8V;(Kw;)+F^Q=&!FjevQiIXi+iJ^`7!*@Yox%o0qVQH-s@e$Q|XIB z@;Ang3#YiCYq$BnafvMst~h%_DJA6-uS-EN5J(1bH&H-B^*vFh&+M}ksaBuQ$w9FEvpVEu#s@yQ*&@a z+qjfKHMA7KUn8;I=`zu{3a~*58!$MqkLt4tp{r?FFi5uoY@8i1k#vo;&A}rS5(xE) zHnrmM!}yGIMw~~|HZT{yHNl=L3a{SFvH%PdRXhX%JPGEurb(kq)j(lKr~KS;StO@_ ze~L784k^`vkuUc1BS;s2`F!?FJlqua?^I3+R5ikkhKGuRQb@?{uF-i7e`yfa&;jQx ztg#=>f|1&q69^IsaeTn@InMG2_W&L<8IAMA5Lwik9k{E4z;N(U}KBw0#Fk%d7AwZR%Y(7Xbu8P#q2zp9vD_wi@KWaq>7PQW>~(a6fW z`@B#-?EK9f8Rh}(BHNSH<(2LB!xg&VJ!?7}J^+|SraA=HJ|VyVkX(8SR>1jO>Syiro&vO<^74De8 zc{T~kShpOFgwPwN2!&~`wl|Y?nRDg_obRb$;y2-{1ON#+->;5$CZi8(k{4noVSz<+ zaPru-B#1+iERV67F;tV(Vh`KZlb}%LtVElS<^fL;Jop3y&mw;O^-rY#{DfzU^pLeJ zi~?ObNNFT&{LzV|y8ZKR?&KQ8_3OdVVjjz#!%Qwam-j#O$?M;(p<~39Dy}l`uErQZkSuenbOhU8Z_ed09nS zp)US4Lga+L0C|38x|k`RasXSoa+wJg4p4~5pP0L?Vf@&9d#0aHDI_hQ41#R#k#4FG zEQ6dP$1q~_S6t6bRv#6mw?V{s<4ZPh7eQJVXB@YI+W1ekyMb8t1iex^CYk z`tUCQ$ni+;Y2JRa$kYj-?}q-Al%>7Mj#GF(S(+AvR)j=JBiP0v=^Y z9;zd}a)~ZB`(YxU6(^7r!7LJfiQ zOIR$LjZ5jl2{edi&^DQIH5CBeBkAoVkx>z@qnj=ZqGbOTUZ8{`v0_WwNLz?#Uk)=R zJLw>~5ZHI@K}alAnU97Z68B5#-{J3-yLU#M8M!Ub|7s-q^2d3o$SAVKMrxEP%0bB6 z=xg5Y?n*E|9=06F(GhYrA7M8@rIy)6N#>cj%Mh3B8?_sH5=PXqNAm$@|6u5JDj{@J zwh#o&=K#Ao`V|6@kSzDvU5@O(SSoS{`N*qbA;dmzR2v$&yv=Sz5cI0weymTQ-2_Qp zW{#-e4(z#{qYfv(!HRmX!=FbXNiaIp(r^$iv_iuK`~$Er7E>LQnRvPZ@VEg8neU|W zE#4u%igb{-3jBHOHd7k}sA;O9pSw(@!n4cYiQb>Sxu#xyd5IgRHI%pZhHfQFUsSH!Rxe5FkeU_cf{iQWN1D##J7u~f8LJE;SQFDEkj@T?tU9(Wy zPa4t2v|(D*HrpO=QV(7mgF7aOWypey&gb33z37Sk}{Cp#}jMH$M}=$Img2-raK}(RxmT zoxgZ*4S?vU?9&j(h;$k#QhGq++2-pPEF2#n0#$?1zarPj${dVM)J3j;v#S_EtYa)h zI|zn_A|!Y9O5NAQX02j*!`!RYvvs0hd@t4V4g7o2_8gJrL%S~z+;ifPwpHKe8+DX+ zN0eX#sn|AjltQdHq$!Gy-hOFz87RuqwS--+t1G~?EJrcq50c%DmKJVUbm2!tTJEQU z6$+kH?n_6#LIDa(!;GW5d~(zn=A}<}F^>YIHR7}u zOYv$6iU7Uomoy$KIwdON6kXRFIY5r~q;c%SN0%X$2eM}2dnBMW!u>A&zDOA&ZBdq~ zY;FsAhLd`NDJL&c04(%Cpc8e*>*gu4jNihAA08I<11$kbjD9X(VY5kZv;*ave(mlI zNFUSTH(e^ln#B5pGG7W*_Prz+ATPs)UH@||tPsLqB@^BwQReI)elb88MnBug-f+8r z%eNxX>Ubk59K$_^QkOZ<0&?Vx424*_-p6P0JB^9PDi%DPmU2V)a{U3wg+b5 zcW>^10e_(y5|fTz;Ta9-2I!i4dDg+(k|m6?H;j7aVQ531ik~4Y&t>Id8OjOPg@dC2 z->d|euYSII|9@(EvBS{~pdjRg@8jy54@lnrjpuw*4PO%*KtIYpccS-L?7poFEF|Y? zLiV&}s)_w;4Xwd6*#U4 zmNh}-!yPGSyJ3b5d+Jfbcsb2GO-8~2GM{i;4{!;?=9Jk63_g5l+8tqiFedhWpe8={ zDQNW_Yt!e?(J3qXP&14;!LOc|>?5E?KuVqtGf_!MP(@X`I?OmKbS-Z9v)%Z?GFZCG zKwWN7xlBbV=??XGy$?^`DZN>qJot6|%r`e=eRZii8HS?%37{`!{DuGNQWoo~0i6!- z+Py~-L<&oSjhZ(We^hAE1FB)#&>UaPY;NDV1PxdlQRoX0NP7(FG8!C(RM~+X3=1fX zUYJBCM~xNvL%?UaLQj9G5NI|m_>EKF;gkDP0G23by3{Fhv#h$!Eh(YK4uJ^LTkYH{ z!9`a2#}sIepnUF6i&bNX+BM^t5`+{A2Q{Aj0oX{KmF0`dkHAIM8^&7aA^W=Dj|0-+ zjgO3d>^@aDFesZ`dzZuDENTQXV7?mqy!xUsgO!BUxDXn$H?^2XvD{NL5YEUe_x{F! zi0Xwu*~=8LPS3Zj93vp$RrO|{_<+ym?f4%-Tb~W~M09yw;(g~`iqdM%5 zUWK*bZXXQ|3Aa=ys=P&m);NI?H#xnJ5=6`0uLm-nfyUt5a(?F~8w7 zgV3MA(44+AGpI>nXJKO%x}RUkbjkReW8>L(4|@k7<*8?S9sH6B66wqHUwvnN-7jnh z>Q+9w=3{safD@ncH%`yrMG!&2h4%UT@H!Hn&u6M2%^A5G>u1A0f!5!HMnmzmAfN*4 zOi$q4!cPlJ*^CKOGV-$kPv>~bFjZ^@K#-64)K{{LYe)!e)75V-(@=cV;1FKhiN!JU z6Ji&SkBv?D3u7%&79K@W^2xB@nzy>YbDctRsWVqE9E!WDXg_6wt(x2Fl=q#UFt;)oHzuI3S0y$dsFif7`W%3+{a;T@5Hth@fv)8=6Dw^eU2>!jVe10Io z39zmn7e*2k(I*FsTIrSm`LUV(O2Iq(iGGV0g&~>Z2CR|Fh*?ZT2BDMJ>dI+dOyLzO zl_}QJsrTY4hG#E@~ZY&XC3Rh`{!cr5S*o zGSYLm=5+)|+&%cXm8Lm{B?BZD;<5;KKD+uje^EZ^#F}c5x{15}OEIOnH{SoAUD-<~ zS6#9pJi9@?3|F1A8VnRDk;oV}arxy(kG_X(+dVlHzU&vvRFf^cKL=V1q3Lcr;}F9d z8G2$d817v9JX+C0_|U;w<4t~uerVm#1`p~bItZ(U!GT!l`-_-Gq-$$pxnSAb$;t!a zvT&j5T)a>pI#gzyEt514huXF`OfG6-GL=2Z`F;ya(m^4aS2k+Q9 za@g)Q`1O`{|5beZJ5#6ZKECjmqr`|Ca+_I%?4@pq5#>KQveg_%i&&Y)#3TWqK-c6) zu6cznyn7?x@c*;`#gro=tTYPXTqvmk1$UF!1+uD*yM5=xWQJWj;LTel0A7Yj<}>$J zKnL{73Vk4G=l-?IsfR#g7&!J$RJ%yM1thQg`E8rbl&{mQ3jO&iDehDSobmT8$SG0hKF@ZIel55ZUPlA1NI){Cwj01l@mEzUu=&bLlJQH`Haz@ z>6KnkeAXmsRB`<-uRLELuarFaojtQ2hu|l+rKeSr!Fm89jz^9uA{4w44ZW&vl#F(Y^4NQa(8x8RKDY;b1|bYU35#GWO@IyZfTwzJ%UHpry@& z8cqeJK<4RZQQPvCm=!mFj*u@Q7b2_x7gMIzLt3|if)OQMivUTCxm%?~jz zHa-cc@Iy^|xAR~zWh0T1{D$NFHN2;!GB0!dMqs9%FWpIhx(LZd_bTAO$+v##KCr!G z2u(+@B@Jn!VlQqWQwm3f$5Qh*ioQmf{Vg@;cyOuv=e4tiN7T4}E;*libmd)h%1e0l zBshI#STN43*PCxF2U*>u%CpK~mG%SiYbxT|d%YNeQ@3$J<>+Y2U;D$!!{TicNBtRs zLFsH`T~pa*a!bPA(}~$2Qd_n&W>u8asqg==FP?FS)8p^Ay{b$da^T-Bx^`2hscZEE z!W&yozrUX*cWP_btYVn01)>!U(}hNqBWb_~xq19yt=f2beRHoU0ynyWfV&qAz+oD+ z<6)rO`lE0$RZKxu@On%p`3KaQ3n9W)_-R~R<<(-`D&MfPY!N*|vKBfU(kv=N;MbUc z$dtc?G)S%=R@m?^8Fo?L1v|E6GQEsyGMS+_fmgcp#3%yu-TcuM6<=i6aI2*J%E!Mm ze;6HOxNV6ipBeBnjqp~Q{2lh~{t!vWzIUGg>VS1uXX^4n24sg`r)xR184b55%a;al z?g)~YQApaXS7b#QCRTtP6u?0IIj z(>BHj3DWrRK;@P4SBC`lUn4fH4L^ygF&R{mUX9`DA!V&r=`tffPUq-2ejV!UKoXC1 z-FmFL@T00h#9owa3yt#;){(27&qr?vW2I5~3UUPxbPV&npFic<;*$C~Q z+|eZkP_8CMYe>O&$jYF&@Mm+;vV2(Zz2S@i+ZRdbVbYm=Fbep8yDw!Ehd_{?OtY0b zR6Ejt4$a(H@(leWP?r3qDNAQ*FD8X9aC5Ini!oDiRQ1z*Rcd7 zmD3)|8~x5LcyX+;5R!+_6=>1f4JK$zlJmvae0g@1zFg7dC5UPyMC>tp2_gfkE%T@e zJ_UrEXZ!=B1Ox&6%)j$$%@@9VgTpwXD(@*c6I-5N&g7=6jR1@Gf{R#pTCcc}#$@=B z@WxdN{vihh5EKnU)uZonBtI3tF>5BF_6dKacr_til5}<<)+!Uo8pWPseZiu}-tTJ)b!fh{RgyJ;Rmyx71>X8ulsb2Z;XO zRi3$ctQ#JG-~T7b$n{+)#sa{^&^Pl5@b^>EOn=mQ;f5x&ms$N8-HDxkll9~<$D$vN zB015SU-pYM|6E!A)O+IUmHqhttW|&V>RuS-zR8|WwYuhL3&%_toXq50R&MALJ0VV? z$qk$M-u>tHtB3MA?141NIObYzs&4mY?bQl#bkRlv&c1MCOxQfUV)ZF>0QOj*V&=*X z9?YLBij%hRFy4AB>=?^Hh?kpeetufOL)RJR{dSQ5hZT@yy$k1OG1yU-^}>##TNgT1uo|u zTTuXl&ds&C@yo6(e10PR<0^?zYo*xDig}UBIzzbj^SuM8JNEvl&YG=2=&?D5y5-V4 zb>d0*nlJ-8gn>e~3eDwPSG=i~b*EXy%BrL9<0rcEcBMy-*n924S-kNjcdo4JX9r1_ zJO^*fQUJp@V|67q-4zONbN>S}bIu0bt+n|q-Kciilk^U30Bb){i+MuXB6>X94r8Wz zkFJT@={1!2JQs)-WXx+e(3FWtIT@&-t4Fe@8p;&?5Hp!M&a5JzGp~KH8hTlDlMbFh zLOITXnYQw?&n#1pPR@|r@)2&wPPO$u)AnFncA))4OHRzJOdP*Ko&N zQlQ+JKyyNeJ0xvY_ssp+tP~Anlr0ul>&n0FC%Cq6cIIOJfVcOZPm84>o&x1LE_3nK?$SwZ)8T>)j3Cl{{Phrfpu!-OkWz&Zp~#FewG zda}hvYbdSi>f?A*%aR4SZUS@lb7>5}0^-_|Y3G)fFLQWMO{IW%T_0q+{p#h(TAzDK z19WI1|0~{_uTP~3=J*Rgsu9Z4Ny#<+Vhuu-x&A%wk`U4MY4}m)4~4DjifjJO0f`w0 zW$6^_Z6?jsscA_wO8yH}fI|?&qcmk~BaYQvLGdY2YEoFexxREEW#hp3PQG;r_PsBq zS_F2iDXJ)iL53(zej)SQka0X3MB&ly=E1?@(ilPZYfIGszN#(Gv+7&p-LX|_m)kiu zIrbVvCJAiA*|+Boe2zvH+CkZ)`J;nUFUy$V)}TPE?fk-v>>&Q%K0k3Z?akXcP1{Gu z2~GZy5nrs)!*ETl5|F%##+;|EG(iZ4$PM1G-Ub+V z5)?reO>0utB(%3T?|=l@=L2i$joqIN-EgvXaMxevrr6 z&V_jL;H#txKmW1Qcgp*7ylP$oWa%Uw^6YeKBr6WNXJ2DRU=xPk1I25QTZ^>u4PWax zqEDA(de4U1b6!Eo)qj3a*8f+24B{9YnKKb}1ApcTyJ9LSx0Be1{S^?dU?CIF7k%Be1|NOsSryY9_1+$ zZJ@zBc7)QI19zeRZ!!rIjGhB4djiG+(n|Z`JBnVmb~~2ku&Uec8dinHF*g}FYEn3G zR2D~8g>Jh50tmNQT;@-Y)1r-2tgJ-E?RVU7(Eik`*9toFr_mEaZ#T5r+&gVllOx%V zh_n;fJbr9}qVy54T`LF-y~vf*?gmS7&4-meak^0zY&cczM3i6=gBC4jhApYF&I^7V zrX-P^DBMRpWcYsGCqCQ#zsCm#s!Dix{8b?bWnx-8dd^F= zaqz=H1e9Vzs#SUqXbE^2KH_0Nn^bbJ9TB!)C)~k0M!;OD2PYet!{ko}f<4X3tN{YL z?q2i-F-AK$Nz;T5vT(;a18)8M8~V)MDP<%@g9YhNngl_JC5}$z=DgYR3%`Y7K|l*E zjn7zDk36)El)j)O4FIhvjt3;J>4;Bq4EbluUvqmBKxt64=P7#Olz=qNpi-kFZoZNB za68PS(;c)=6v5%R@aKE+Qq(3j(S504+2L#OGx{ns$N|0tbmg7nS1m;D`vV`xXP0_! ziuEGVDdFUVrq#oCSBJ13+-;v>AQeAs__q+{#Ep5`FZ0JeUai|0y4qXiq_h4#2vju1 zi(#OS(7G$U_#%^{{>j)4)Q>rk0a%WR0kd~SnVVEI!Alwb(cXCj!JPc^bk6vR+x}8Q zd9vebgZX(?Hlp@#Gev(`?cOxo4eQ1y;XqmOj!sKC8U5`&X1b1M?rbv2>=jTtHw6x z)ASeKuq6MMReJ_eSC*CF#IH)c`Xn`4mL5)Mk$WEM$_)l7Q=uJF1{7@fT=g zs!NbFA?YlBVXbkMDv}p=Xd8%v^zOn$PR9a9CuxKU*}b8SnD&r`1Mp3W)3;z=xirzn zG~m84u><&=xHMHG;Q){PDXVA*@+eiGIji$Gso@YH3ka$9!Ux_JAOD@1CyyXT$Zdkl zU*miHzJv3{yKiokj5R-3InNyY8rB52PBN$x%M1@@^Wv6Es=msII+x4eW=nWNOt4D{ z)6%r~h!40GN*mqxbbtLzc!SswflV(@^ZaNZflJo`ur~{SXOiUn?a0)-3kZ+rRslUH zcAN}9w!wjj0$0H*APjg+jxU;ejrNm{;DJ5sRso83w1nFwey4yS7@?Igx zAzfGsWOb7IvcK>p?n!E4`x9Zh)2SLb-Z4Tl2^6$2wMD#o3qZE@bG0V_mX)JTXb&O# zjR%d7=Z~CK==B#i7KG6_VN=B;_p9G()_NrP#qefyV?1xWU zX#k0MO`l2;kzx-ZWP5+kKGhd!_(AexXSp@QOniI_xiT&ezD(&5n{o-4NOWn5 z7T&w)ckw`&PPlF-&*ov+{}I{yr4Jm3EzVTZQEm=EBvi$1ZcJ4ppPyNZk=pjp`%>$A zcJAU1^2Xn)oZgvlQM(VHM2GKaIRRU&%R=?*vyr;FSfj}+z=n0RIK$wMD_(Ly5nVN`&Kd2tnn)7yLigG=rT6k zlE1K$_e6E739MY+LHx2`P5iAju|Tl1x!{}U1G`if%TfRmD7hJFDRX4-__s=lXYjJg z8l(=iW`}0^MZ0wTT7xII+xZc1Xmhn>vQ%_PuxhEyIC{J@{uMOMQOV z9vok(?^LNfN$y&Wz0yiUbPWLS5W0NN0n`C^K`28r?=x8T7VO%WLESv-TRSKXA)GrW zQU)mo@_=l0#z9;{Jij)JE+9NMl2JwtTsD^p#C3LPd# zuWJX(9m*4|o2%${7u9=V64P~qPg3wv>nu}@P!&T(GnE~5gRf{R0qI%ysSyp z>NMclaBfBP;p+%f0oKr?4JJzVe^)ev;odxAq`H2u!U%m^J&{SK_Dak&!`~bL0Zso7 z-()?M{{4&NKsxsXQ|<`_TT_d4pR^r8-8RZSHiK*XArMWx%vdrYii-igu)gMW@N-jM3BQ8xup;k_$)XX$m;{|5^+c>=BxnV6ucl=AN> z$NygQZ)v#YJk5HT^m0fw+3NLIoQFE*dWh6c9^a(%{V9yq*L^q@0xQOpOJCQ2C}q1F znmx_heVfTU;5qouOLWGCRUA(}xui5{`}*4D8+5~en$Mo-6j!K4SVrLBS3vVw+`0-9 zyASweLV@8X(92yJgbPE0<+8nVpvJupncpA|Qbm%j`rHt6=)hm$|&gVW|7La@FrXq!4=tC4z$#C!qD-5@el4G9>fYHNzd zpe}1Xs2MD9Y`qQp$60gcRI_%~Lkd3;ztwt|^q_AYr%;yB7!gyvwYIezb7(^sB0IaqjWGWLekK&Rp_j%CZ$qxqCm0} zc$dqc9-fqBBAy_QMRA+G&orK@;AgrSd<-`oa98FN!LhS@>ok3%sX(%nL>FGSgXl)P zis2e%jmJ*v1Y|}CEHwQvy)6+tm%=S&^*!|{>D|Ly0TEm``uw9D^K|o&4m>)CrC@3t zg!1p~JvBlo@pME`cKpt5`az0yHv9Zap z{rB_iqf|-XUwseZ>_HA3(TsZcjkXC*vU(^z(BLL-d0qOod7#V)K+Ki<&Mkpmpq%ZO zKh|XCc#-a^P*8;_Roc6bb+J}H-}m8{ z-Lz$Y|Be2~)y{QG+sR;*XRZ$6umU+c5Ox{!#Xqy9756%R=$?sZLSmg2N~pdDZ+?Gr z0dm#v=>-#D{tr9s=sx7sY~wYKYo+l%&6v^B$T)ULfI@=@?d}o`MaCeorMltYU((g_ZYQb#@dO<+8JVtYYHTA5;t)grRQ|G*ai1VHjc({08N;arj^A_ihQXDP5 z3If51mvmaRSiyUsiMbfD)sJL1sN?^(f!TMa^cx(q`C3P1Vl@O5yzj%5=z4_hqQ8L6 zEQIaSUqwX>QsY81+1IGDeI|x$L!bG!MLU6#&`iCpwm;vd(#K0jmz8gYdh)Z_YPi1% z=6Lc$W~`;)k&sglKrIN#rEd(kaHO)W0fyBhT$?qIg!N;~TS*~`O%obs52w|GbTqbT z^0~XL(V$WKMa#TX7jqWr12JdgQ)YpxFdM?iW`x|p8 zoz5v+>AA)2!|!8Z=jh574?oJ2mHy}&I0N_m(Tkax^2D^}$n3 zaL31D$B)|njfQ@J<@=ALXe)s&Z#wUA@$ig5Z1EK-zR{P{?6%`;CY;o0Jg zb=Jd*fxme~ERf$>>#=8oSi}7Z!kw0!Zv2-u)yH=-(Ii4L5r|Byn}g)&#;%ZuOVrM9 zs=08ZHtvHUg0)8S7%?WH(s-$t&FM!kO2P?mp?|9e@AW;cxC{K{Q_~F@3cHK;0fL!5?aAk9Kfk5u*ETK9*)_Ya+H^CBagG8LWvsDHFzYv(Vx0e)-!w zEjYMItA{Eh8mlQI8GvfjpVHIxG9JCp$lF_b735sb<`(YrP-JA~p&gbCnK8vC_!n@* zH2F_neCYA*CK96E$~J-;H6I57fkhd#2MYI>4y1sJxgCmKi)QyXe`Ewrz7yDk4G(IF zgQ;kisnJ46zAv2I5*2JiPSiu3D9$y$%BT!9Pb7!8b%mmH!Y<=NQI9j^)tK5INa50w zTyl0;i?x6Jqjmg%n$)}SYxAo->bJu0XIEDFH8Xwfvx*))-OYk!CXfE6^4zpnTF+DP zu$erem2J@jBcR(4o>$Km_Of@{YGms{o>ACd#3V%7Nh|Z)lVRe3t$7NUrUqKB=+UV{ z;r>72MuQL4n@`DEX$%)-joNg|i_;znLg_ISo%#u6LkZv53vKB;(Rl|JE3-o(Q|L#E zWX9b@x_$qjAdZx?Cu>Z)(*1airEQv^972d){e+aYi~GrcK|kCJ9)NYf!qcn&u)W;I zH9djLC@AQIi7JR+FCe73TEBRjwR)U+<2>6EJ*9LP9ve1yC3kPP%3}FdfQ4;+P!??B z(r)Z#4FhCfVS-+h!0>NC$aYTTY~F`R#|}XG`&SiP=z}Vj+lzIF1GIKNjz02l>6pWh zxo@;NacB+gkDFLb@v4Ox?IG!xvW?#+eT+PA+-NL z-L@oCZS98N>m(U$YzJdRD1|142gIf_G$0OPI^`vii02li7H8}C_X2P58uH|+hZyeW zh!|MBhE!@Eo38Hlq#1TmLKK=X0-o}*7$Cbbb3!9>K61mZ7G;w1|Et1+dXp{S}g&Cc^ zpjnHMVw(g>YG(O1^$n+x*r~Vgi3KsMv0`t84v)#98WkGwSvp$;w%X;yZh%c{-9syD zO<0$P2&hF{kQAKiAJSPI3GVY{WzGT0|J*aCu@n>|zw-JUbnep^E`;ID14Usq-o(q$ zIx;}aNGS0k%G0%kX$UVe+GgvJNNQ|u)r!m)k6Ndvt~C%dvbM9uQqCcVWR6p9H@@xd z>yK{rMl1SWthz=1E!B=xv8c+P_(Zpy`sBWvL4i+nAw_X#XAr|X=<@HChGasF7Ysf> z;_+(Dq5_7~%>YIGi&jy`8BN#i@41PfL!SqKe`E9$$ze=Z+cEa;J%rm_DhS;fTVpVj zMGUQzWQN1N&8M)2l6W+gp z_t*#OzktX<2Z%7_)*`XSLE94?d zWJ-qh+W_Xpz8@fsEN?Gk!bt*>d#<5r4n51%e7yF{)3%t)PhOt+IVtxMzobiH!ZPH) zxv-e#7k>ZvArrClo^|UAX>m9G_&X1W!rXJ4QpPjU7Atf_7EQG`vz2G2{n{j>`pVrF zgD^%{4XLmd$6KKW&ODJ9GR3H}>J4MOJ`w(^N^04>(t67>jta8W7)3P)C2_xmL0YQE zxb}Iy%VD0q%{-kuj#N*U!HoR%gM$Pw%PrFtVe zqq`KSQ>w{Ye~?2i?P5~Y1}4lt`^Omk>3KFa-yDefKP^Bb?IHrlQQcW?T74f*z1siB z5?IkY{jxdJ02dSFysel_(M=C@a`tQ^y7r5(;t`N3G}7+-h>OkM-HDIZK|ye^g+$Sv z2#)-OtvZ_VXo%LKd8}R5If_(cRBXw<*vAO`>Z?c zpH?v>4j08hs~XV%J%^qjuRD_FZmuJw*DDXt!F>B+D%Cg_b=@#C=DIG2%9c|Y(<;rG z(#^y%mOCxfTN&EBUdLio%6jk zZI^!d^5$lk(yvE;vQzT$2R-Vx!s+p^FUC&N7Z?+anzCu?@h;2wl-{XVS^j9T09Jjb z0;e2u@Kn~7zQ9E8Je^NoK21D=zu)*R@?XoLbu+JkqEEX(FA846!7FCyna_B`JGCg>hAeKnE-zhiDJ8ynHe`klzQ6+$zeZ+v~r- z=>V!(nwz#vh+Kil2OCL?AY-tIK9u>Gr*793(?c5gwT%)~lu)QF^poqdVvk*W&}guV z$qc*^9O)Qgl`KAWMHpKG8_$^XVI4*YhWXzuUTGh7_9mDW-l1`=61=wBJ^}miB;2aF z#wjzoaA=iB%vFsr_&-uutgKCn@;#z}kdHQ6(SpInR+V~*r$ybpC)PmlW4CxXedJ6; z{33`ZOB=xAu3gvCTz#@RIuW^3!>RAW8>_HgwUkVai`3WPA6z?Gi{8DKxZg-QLOH5u z4<#$JMHRANEQ=c*3BQw>L@?4=ZHk5GiP5dU!3Jf}b$`E*D`hQOcsB9c=%z*i%Mk^Q zZRiRwyAwGbsl)%2wQ_fgG^oRfB_UX48()ALN(%FX2N#mO^$*F#{7 zdm$T{a$IJOAt+`%Z#nRY$#zTW$jxs2)?O>!1dI(575aL<#=XwcE74+9Yw-}OSxkM6 zZ=+@Z?V+)#(TjB90xAvh_PVedB`HWxURi6g|!Bv#wc zjPi0MKuFDlc{V4EOi*Z-QH`}sv%I+G)zfnloh7ni%464+I(e8oO4hOrd#x1y5W;0n z>^xu?XDOr(75I-=%aglEV*cnFy69xAeO1p0=`%;k$}cc`6=~~W_dF>-23th{VPCNIfw80v%Z?A zgQ}C)s#as0t!s;0#n^l=ox|rV1WtUP_#Jd5(eFDcrvU_AHH!f4-cuC#EC2Q*s6IOC zVs7BWgi!!U)mgGH+)5nyH>OsymVZ~oM^dyOH`ze*gdR zaB$4dLFU16j=i^IkL>J*WK)rllf8G2aLgi`6fGGIvbSuKm3@q|g>3$}_xJjJ{@3;H zy53!v>h-!`_x-$|<1vPhhLIcoTTM>|-+@Mwek2)9+Su%cNuP(n(|4hqY9=s~`6v8> zWH+qTP~4eSYLg}sqRd*U##RF*8DX(=AFHfnC+UuzL_ATNNB z(yrauHI+J%z+JEEt1ag@R$cmalR3TVPU>RSu4b?@4C0YvACI2a#Wrq65^(31^?^N! z=%$?mPXYtC0)-=@?=}H-@F0s5U&hNnz14 zBfhZjvq}3kvATs^Zw#)s+)e@1#66fhREYVB_LBBwyJ3#@5>SACdl=z_ZstYAHZkz* zCF57xOmPg%*F8jGf8M0@7rMlw6JlvHIwblVVP7O?)Tod96gR8*NiQq!nk)^Ww1(|t z*hgl~S_GPWbz>J3sznFWV28+qD4`wJN8>x0uH{4>5^@WOLj#A%s_FG-@5}`VxPA(J z*;U+&xY-Zovjf75Rg|K)b+vh2E7@X7_j#Q|D;Zb=9K$Iq^{QM5 zO>6q(aG>G8AZ{?2`ihO(t_aEb6O?{S%2rw{_fJnHoY!AyzfRjB(&J-bjz|FUIHB{% z_epn-l=%C-WbOp?uEw2F9d23N8OOV5y11M~$jpPSG`f?j`7oSp9Hu+=V=4 z*NKo3>ab0HeyHoxMz+SfE%sn-1Eu8XXe4R#@Ajo;s9rZp{1z}LF!{olr!-v0OgHp2 zUH9?AT#-&IOd2tO8T@WOYWOzbb4!1ZTvHji>q>!XlJ zkLZ%4@unWCJ4JVGL|Aqg3%tsr%CPB(Ktr(A>yR za?g!jFA0UZ4adlygv#H%Pn7r1e!*-33<%s#&-NQO&*C~~ft84dYM%FqMXBn%%QLU7 z*3Z4zQgiXp%LLu}bSK-;4e!$NBSbq*&Upx8?l$Afp(0o8Y8x?I)i6gq&Bt2F(RKa( zKeo)Nc!d@IbQ}A;;D6Xd^+*DA`JvGfq_2%6Gc;J4a*w?4TKkK~-S3Taehl6Dg!4?~ zd*`myjhm*QH0=G{d;ly+_V3E@wtY?rKr~@-jbIWaKgyzmgS9b7m)S+q9JmhH;hW-) zfd7egRcP+vE2X#;aXbSZ8zjfNM@skR6$T%y1zB{(crpZNHC}IEyYQOv`2)d-I%}$^ zokZsMSc*4mfRn5d8G1tC^uJE03$VXLFV%kNv*`HE*cWe*GM%WVkD#2J4m+LlK0F>> z5czsCQl%X`-|m?kn6HzVBe}^Jlvhn-KYr@o_NiAV$SV^0&OwV@kC9FMpSUfOhrL~q zDXw~a=XNd?m?-ri(%HFQXDgaHL_RLLaz@L>SzD6e{X8g{Dvn(T#s*V{!RP6X4SY9| z(e26Y(+~YR@(mtg5jqjcG*x|YHwMWi-Qw)WPXp!9uW-{3o9mu1Z0Fjac=)jj*PTFo1b z@mx{wRPVB%YcCF)tv$g9h51fOG>NPQmJ`kS$NGfL>GDOZ4`52cD00I*mG`vim7!e+ zyD5F=POeDdEGB#VQLV&`2(w9O19D*4jg5f~AOkwsr-uto1orrt<^g(pxi8Du75G>m z_CETSU9ig%cV~Q*!pH*gklC#`VGQ&|taI-%4#3(iG$#-gf3Z112tg2;onnvlJn9Gb`3zlReYVd5y=t(tVWIg0ikHukbV;4*T@ zqoZ5Ngl!H=EHD;Si&eDR`VM_n-+A{QRN@4+yaeI_AzRAMx-&>$?96C_RCT$VPj)r~ zFyd;;`o>4B%83SCkkyb7)SLN|*}#S@HTUgvMrfUhSNrHd$cRuqJZehvR!$Htc-57 za^I3r;_)-t=IV0a@HK4IGP|R-M-O%VXj$nX@F6l^0Ic?hGe;v|A3t>pj*=%6VEZ!u zI_`tDWrH4iCeSdu13nT{S~dSzj>)d~HM3)Iv_zyg;stb=GF)Q^-q6=|Ur?pVd*t;9 zf6%8&hY_85CAFf?>T2Dp=7_5w0z3%~|NTlL;CHtm&IZ zQn>WsH>oCPW9=A{xwJ)bsbM>zZmoLxY>^S#nf_Ol;`;lVpt$mMz2@kPHRrXmajNEmV^*|@GFTDKmqM&cxV-7~+x{2ox zClk>ar3?7R{`TQj7W0s;$W*Hg<4ykNah=w%*EYX*KJzgL;5O+qh5d48OYhb9&wva0 z?T@TK!rlPJpD4qKZrknV7y)NYel~;v# z!bq;mEj<82btTEDIC-0a+rvvQbQvV4ukce52?@svH(B`uVUj)~9c4~nmDZ6JAoZ1( z)!@rkSYWg+GP}DW&xChx)}s3mpj!~%XX)E9W}Sx#Cif?+u>XF|Os^E?Y}t@JY9+Ng z)|3lxGg4e%LuEsj(TA*gV$}92;C~9)Aiu12y^;WSe={Y7LvKO}^-22K^SJGZG=Ooi zQR3SO%(cd~)nSDP>svw35KjK>NHapt z9MG!7vSIve?6x$J9)ns;)xPxY3kk9=tJ8eUzHrL&>E>hc%~7Qv;^(za1md00`flbf zZIW~9!gE+=xoA1S&D3meZ=A*1o(%QMonVIEnLj$7yBz9_);?+7Gpygw#DFA{R;=cI zpVo%o?w}48e&@Ydk|c$_y?BlW_hqsxDB_z)leM7MI2z_^EjZF;!5K@yq%7^h zV`-k&WqMnwT-=^#HRG%A+pI!ab-dPN)xJ?k`yvFM7>UHA-!QTbd>2+j_|1mDXzSBp z@1MUk*XmzR#j1D~E83_LF_X-AQp3cyRnb!_PW@9M&yJPUX2kOI+Zp!w>zCn?wgaqnpRNbfL0P90=BOhg~RJ#JO#w)*R_nR51hj6OC| zRl!VP-&b}@x=o<96w%#qwzNB;Gfv>j8P1#R{KT{qrzKnh06ZZb5wSB5=)L%7h3&DO zY=_PkzncM0)1l!U=%uiDqLe|V{oBKnGay|3`aH3%5MzHt1S*6Rp~LF=FZ6KHsQr0 zX0C4>?|M^iSDjC7hB#?TZMFNw(Bl)|e}f3h;}5pE{T@A6!1sV%Bxh5*)r?qHx~rnT zz}NSuBl&2Uau06CJaen`WVpA+Ls$B9;Ljh4#~3r<+(EdaBl!gVV*;HL8z~>(J06o9k zj@SeE0^u=UgculPgw?o50A2CtA^7K~_a@W>Biiv-vhPO=TV7gQE`}2M@gA4WHEq(z z633MIJ|{hIKhTpF=5z}WBqGl>{`LGLM-;bHuC+k23kY8`q^;R0on$t58$WmSyG^)e z#7=B&cXV6bkzGRJsLbEr-dO7EDJT!xfiFoGnL_JFkt5p=a#IvI9|(U_-}=_c$j*J! zweh%^)2V9ag|Tc$gel$M9P+t=yz>*M^!qR&ZdsrrWe|m~Ns>6H>5Dt5YOL$}oUu8* zoRfThx*c3pw^o5k=ahn*%J5*`?qb*$qQ_7fj7h$Z?X{^!N8y3F{W*%GRYoi` zwYL+zsb#&l6jdAS&WuCLO4UBmZ&(-d*Pk6dbgygHQHr2hyKQV|6ae6nOb8v1iJ8Dh zExv1*L^OboKC=uh2!+r3UA1bq>ZGEc6`&9QM9=S}5@AQ)q%NMIabp$zASbc)kAhJ9 zxMlB=%ju`g-JNvnK8%@j&R&;L2S%NLQz{z8miA3${!}I;=O#h-q?GHYE~FWub5duc z7ivh?o;(~!Q{@J&?r1?}G|2iXvICyB`&SDvmwLP*>9a~ui@OA2FJ2_Sa}W+3u`ZJ*|IrBp5F23iGsyawrRN111s7NlXPCp2*i zv7oub6-ZGK3}9X3at-7CXMfZAcJSIbh11NaX@1-yK4rM+d%`iMY249YP>x{HcnGTPM%66gbPQ?L;GHYVF|s&N0XDfvCsN56+Y9+2yCB@^$fCX;1I2YQE! z4`cQNgK;}Yk`+u#{$)qlRSi>Z-6LZAR zAI(Ry*TI-;EZv@jjq;VRJXzj%v!lHkdx&GY%kZP$#w)Dl`F@&Nd$>cN?H@;vTl6dx zglQ*g{!v&6+M}Z?J@^wt_AgE9ZTMY}l>n?MSGl@LQX8}XkRD(4LocAc=)PPZnwX^; z8gf6z|6f((ZI32Wrm91kd6sjI?|$9#<+Fik+Cw0HHI{G;QWgBK0T_ef6|`*0#o1r% zxQ{xQC>@%9bDb00ED$dNjudlCtV*MD-!ZXb;{L?DkMtY^#zoP0v+e34${C2597x-Z zH{=SNJltHrTATZ3hl7ke*kR=yK{S*&OpLq7L*x%c>Ed08Ws zv?4M{nj|#WuG0?Tv0&Iydo?NVRJ34?swTr~Q#if?+E+?@yDX{EC{iOXaPMv2QO-oj zSLq<7tdf{Qv(=d!r?7~|$cNFST+NujkL2wMS%U8}L(|8Q3P;ZnI?h`49s#S}u8R=u zUKX7yKIro4@yratM>r7uUJlRBNNpi4)HW}RPcz#u;*3jW+GbD;#rFAMO?na;7kzj0 zjjA&vne}yRo(Cxi=T{^_T_7dJ122s zD!v_{VWM)W(bFh9Vr`-orpaUmLRs!%!*C>`fI@% z%MOW3e{peP>B^!DtK_H}n1uv?p;IK01~k55J9nLCTaoZF(eHNrSpE>fHHpa^{Y=Bd z#_#;}a+TASeTqV%27$0WC}$lcd=$vqx`hNsm4C2psCUm^^w&|WP6u*64WYqBY_0Ad zgnGXDaHdy{=V2sAHRa*uHHKLuU<6uIP2hSR}TqpyaqpwL&EWo5Ny%+-mkzxxTH8!JM8zKg zR8%#UQ_sB8Ve-9g9+U6bsHvZr4V(hqC;lb$gFr?ZUfD1aJR@RYGO@{upuT|_gjR0+ zkoD)2=}@|8CR^7TT7Ot;Z!?W5Pkla+bl6IElEV*yCWxaP64(_-POaX8qM9c>fY7tk zr?ym&qZSvtkNkGYpnfN369ZTKk~|VKvsuwCDjJ~Hm}UBkQyVJbLDoK!!>sHBXKT5@#fcuYfsE zB;k)x6f7u4m+G+4Rp#eR+leyhZA8!6p1Q=>+#eIrWT@Vsv7N{lT%T}TGkbC{Oi_QX z=zWw4kZwJvx%-(xrqq>RIp6$0EWn74?$(1}BRUfN!_OTst#cV>C^n=dl7?zCQZL?B z?VCC$&%62tHtkerL6HdKI^a_j5nuJJoXhqp--b?}M)VI)b$%j2i* zq|@x8zRgyJ>Eb#?)OQ?tX}wXbc0F;}E~)mEFA~4T293R7@0rWhUv-Ql#Za~qI@GMm z_fImG+t8DV(74;J9m!`kGgIqF@>MF2S3flL7X4HAXhWnKB7p>kbgRlPlOuW+4JI_N zuL;G%iJT{^W-$1WU`T=4=}u8Jy#SAWaI4}F>@Mc`0R zM0nN``abwMKXU9~*}=vR&GsXwAbA+bzDqCN#Zl%F>vm*KQrc$3{A;p&!-4DxtFM~Z5 z)yJFucIL|SqBFf~*0FaQ9ppKE&ix(LpGSu>5-8>buknV9cO=gS@#vzT9zwTZ^P;t&Bo;2Vnte)H2_iXErL70w~cfl5#xn&Bwl|E zC^;MEnv#tcZK6(pOC6(#`*4T09t2+S^&TM-5 zgi0B&2jPI4xvbZbNx&6bvLwYM8wYW^ZDOr_A{FZ55wX6K z#{K@8nQ?0Pt{>kX(CXXRI0S+B+`80KlT!1L5OUX*;~N^CN@Zi zd|~OtZXM$?l@|G76XPe59v*b1}&-@`V}ADZLemx;yhfnb^5k^ zNqd1USK?w`@j1Bd>7qO0WaMl8v!)isC{8vO9bLW1t^Ck)5?4U{^VIml{UPz-IQ{sA zQw1lQDuF~Z;p$j9Nb6-~ZY-SEw36Mk|B?PyGe6@H>iGl==eT$JOd9e(dXETtTl{X* zcZDeoWh&ZB5TGUzDsWXiGFN+Gw=(Z^_|=@~UDNl8MIvt>_gdpzJPIkKoe;@0U+Y(S zWclY9XCqn)macOhSgD>d>=h_CJboJT_saZ&)8AF!#RCHD5~Stpee>|0;~ZPzdM?q% zh-B)B>ZwZ3G#m~8*CbL>Iv#1;nAh=PtzhN6W&Wg#@G!+9aS*BomuM%No`Tie19Rtk?pza&)($pvGRt4Y_@?Q61Z&1umkt` zH44Jzac1M2tKTj`G%_wvaeC!GLv=L{F8Q7XI9YR*6D^Yd5QW_&YN9iO`@Ca%{C=^f z)SqLvq~k%lnhc0JR-yeqrX{e^zmIKKO ztATurKd7tC9wn2BID@DibF}C3xYopZ3GXcfQBK=G>TtzSnL-} z-;@lD(~=%p&5pU2tBW^NH}ef8-ung8=nOp;6R`6(%^zpG>1V?cF|pL@cf$d7`U^WB&fpOo+Q%5q} ze@&90HQAAzk52dm)r{H7(y#a!TW~c-Vepox8K17}z%c*IhK^{p@||Sc(-rf7?`%(p zM5`Cf(p9up-(&wYd8~k%0E2kACk%R5;G(bnfppdoAI}&=KHYWfZ+)$>qCo~fg0!uDSo}x4o6I3x;cV(P1BDRofmrV7mHBEN3A=J zmc9p=NlA@t@eaqR1w`3?m?iaEB-nT&Qk z`{X-LM>lJE3_i_Rg!amej&a2xz*cGHT4#3-ib<1c$|*0p-6(Oh2Nl)NZe$4a$m&&Ve_!C$nbC9pj*=)?`yN2)~-W zee%$Db-_&XP*-7m6|F=Mt9R(Jg|l(V+N~Wgq+Sv2G6o1gdAKqW*ZL3Re(9lQ02Pbq z!Dvu2X>gE>l&+%3@t^Dz;_XiTwNL-1H%OmNBT~om<#%_dN}y?yoJ}m9G0yUbx#&Ai z32#RQuhZ`6YW~J1OyA&iXb7pl#(r;`%cKjF((j~kEyEW@2>|nX@^EiQa5)DZ*~q|w zRZx@!{@~m2Q4kor@RsV|c(@?Ws~;{GMFy~ml0G{De4+I}@^38P8`WW%J=eU#&&y3q z)g8r7y+l7BJrze&OQ}*ayYHQEz>yzx@Itcg8LNx%`>b2bv7F)D3V!oC7R<%h8WT+q8MDTCR&$mP5hrWYI!OZ=ht}IO z=V$v<8^&v(M<|VOyUi7Ib1S>4Y*O`P?(*n5^=IBTGR#xvm|!RtaaHrZ0B;2mVHm)} ztYw%(BQ!1*!pg#+obk1bd^uibya7xC2w2HIwzw|nTR;w5{OkN2Eel<;DV}r5cg7w}pj;9x(8;s6- zwgz1)gnHelE}9Fc-#`VyKybb85Gio|?C!;4xJJ_4QJiCTG|!U#;09#oDgY$#7Y>g8 zyFs}yt~FC8+q{$P2xr^!9#!uM5@#aOlLk9m)M>DU;q>UG?RQJNFB7@woKDXK7LuqXQ3?Y zi_bc1`cdOD?_!OJ{{7rfHTcj2?QR? z-ayyj8|}NtdQeH6Sy<&Gz!#JSt|m-A%;A;;W7v*91;%a)^*HCN&uxewueY32~?aJ z@9*R0fQy4U*#|LoC=u?e5N=256y0iC>dVQ3bIM#94`oEu4lzldkn(NS zLiGb@>v}iMswU8<^xqE6HdLV`^6&fZYl8EipSei86 z`aU^Qn04J(3=k5Kg$5$H-l|K0tk9-q2lyVie+K1Y+uql&tEI=E;j&8Wn=<33G*7Rv z_!;S4VKWImpKj_Go|oJ>z7al7$aqwIUdP0i^m_H7?Q!Pu9+0tckCz(PPNUYmgL%V3 z@tz;4>h9D15_&Av1d<^cqi^1>i7~i z94+qFt*3gfCX^!VY%&+6eSBLF0k8BpX)17&`$MJn)PpOcm&2ukt>R0*HQ)Rmcm&KZ zXS8_Gbns2;iWSgZM+hu@pR0J-QP*l@T4*1#Pl#GB~Om3R#-Hpl7-ydNAS|Q zh!1fJ<0U>$PDV-W^!9Pl1o)Z{792VxIwMfDqC-Yo$3ahiN02^XdzZ&4YmF-F0;{Ufp^=xpe1EVkQ zqnq?H=wNHk{VQ*KRp;qOLPPRIBb#C<&Y{__)8FGQ;gbpwb(E@Z|8Cy5A@-;ZM}rwr zoH*To3If@Cxhv#adu_NV@C56wN3Nh?I`(LWjI9UN5YaR@n3+r;J2yTq9!i6y8g@k zr2;-bZ*+l=5YZ>)1G#c+b2Zl&(Wa>YkV`y9H=6gd`iFLH2rpUF1SPku0I=f1QIkF~ zpjcsFc~D=MD?&GRm99fZ@uZSz8`M#Ou3d~W@cKL~gi8BY=d#k}*H&}6j)>XQjQybY zhk3Lyo}^54b5-tt;F?URSof+E`aTHcm|MmIT*rMjrCeXrxZvXLuIlxb2 zHtDad7o~>S3EdL*9Sl2AZa;!L^33QoHc0`VsDQkHXk|wV26UeOCX=_x;ApC3B}(am zK*^>!!pcLlh>t#88sIEcnrkE@CO?MO)zBZiZ4W)dDmJH9I8&vSl6rcoetnYJ!r3CpOk*SsL62L0E~wf@-1)Mc``pY;ivu}y!LSFgMt3;X7@ zyv%z9K`Gkutt@{eX(h)D6qYT;qt{g0vAPgiKhh5w`wUI;Fji9z>slW%H3Ia+F0r!KUYLuj^|LGI91`71&z{u>ww7Fu_)-X!$MW^ zeYpq@l$!lv_VV~Vxu#uq%qJ0MZxkK*K40k7wKk*U$DkSVWAcYOoRr|W^ga$l_~yLfe_lJzj@Ws z%DbH3cEE}p-v%6BvVVu|le~Zy#i~i{06i05@6FL4#j5U_34;m`6-qCS1T+7D2~@h% znLFlC5Z$iF>0&>4e)#z=1A8|(U?m`w2Cj86RN=zRvfnt%yi?;cYy)@J&CkYa?j-L~ z^T?JovbRMvcRky~mmhgiKHO1xe-J2ss_w)8-A*tnLC!t>`gIgj8NH~YjK-CZ_<|A%f$d0@AmxaKjI1hh#(PZ!eu&JtDOI&!ZM95XtSF!5pe!l*iJh}#}XGb zyNQL+Xj-U;h0{;(2=IE}1{I%1ZIERIL43!i^udN@L&03;!&0n+KprY)Q=<5+>2k<# z6iX9tm5K&;>^0G9?v@qc8RZ41g4t|k)8NP1+^dqaD(MaFdopb0I|2vhYgL8-+YT;? zmsL@zP%pENPqwmzUaqB&T4vdit&>YTy76OMkEgK;+b7?$zTa?O$CkzNkjm|gpsxyX z78vvEt=GI>=~Y4y2@xjTzD|t{&)qy$+_devTq3qvRms19wQN$jtX2kd=w zIk#$We0BLLo}%INn=_9!+*3$K1IL~U7mO+-R9F8q4l1l>g}7|i$#t5%S~%NpsMt94WC*>y#TKFE<1I(;eS z<%Hxp_5M~uJCqfFP@Ll{NsA?n4qu~mxo$PX@pezQsfw8iBsDHIz_n*L`e`aT9}BIN zjg^O4*-N=!M7FyWSCwEfs2bx3XsRYaH{jA>qhj&2nTSN=%TowK@WJS1QpMYTvf?WA zdQF>Qv~cyN>*XX_u<}ndF%$Q*+@8xbHW6t&0SNKha1+$ifP3ktJ($PqpGUuG$s=si ztsZ0hHXQ0kqkeflfU)_Z;v;c7@D$1CkHt{5F0S#=p@k!>GP`iLa}WI;WfaG3lxQ0) zna-37E<_NkS&l43w_B{>Flo*f_RM(4X~Jop@g4RdI=ff6_<89*GElA(!->SnbxPU* zDrEiNFD9elcBLHuI-bS2dOLdc6oi0>16Cg@dB5)0>83L%6yf6D zC6m!&^UVZWKgUpxzAN8nEi4izuLhj89L&wL%!R#=F2cm7F#h|&Q33zgHn?w9j$8_f(74~{tYB~%ZOkCq$$z)+NMOT+EF{p9<-tW!NddTmzxzc+X?a9so zsakG~@fNdt_E?L_``FZmxdn$N7M(<6U&6zsXe&)+JB0R0wA5u&qCONNTJu`tIIcyI z7GLjN@;0ps=DdNKUxp>f%L~eI4p#DunkTQAu0pz4bV9b{geLn$Ro4ExbP3AfrEj|5 zIm_iOSHE*ql;Hy_AT4I51sUGB&&r$PZ@+ngyhLT_*-lb!+KXkv!nFiYX zNu{FxT_!*(az~Kl6&98oX_vKs_5v29rItc2>k#s_U0Uru+*EWSWvm(;l9`zZgOerS zwzkf>hP~b}lBwF}RxmHkDNu+;DMUGIYj$Xy9D|dpTxixUb|0Yg%boIqN3{MafM#5m zYI41WDY0P!y50kIn*1fM^A50F`kJy*ge~H>Qn(GF)5#yhYKF`_u?87bY`Q`XthOK&$P8f6Q{_PKMx(~|v4yQ!AN^8HrN6Hg> z&1SC8&A_gC>JLl>jhTJf%Wn@GkuOA<0K5T^AN2XtI`aOoYth`vJm}5qL)|y6f}Tq7 zhdBLK-Xs7g?5FrS?+#?X8m+SWe+d<_-26fe6vZZa9n^o;!qK2_OJg9OLqj!`ib=~pwEg3I5KjC zEX=;?+bh5pDNNpcdyzPd5p+TJqTj2tE3E(H*_$uiv1-f&-8X%k`Uz;g`RVL$&%0{m z@BOKngCo7E1g`4}g~8{-7%r{F@A`BJ6jvz?U{+J_h9K1VnQ&&|eha|9;HzFL@6Dok{p_?O)L&L0DXI(ssjs3RhIM{(kvLG8;$ z_WKVmbjQ~ss6;!l`fF7LybrKh8VAnwbbQibiJX990puOWU!iLNMAx-5iT;yHidH_L zWZMpdeksV^fQrzIA#7n%t~xK!kvHiJy1PB!<^x{;Le^yi)eK|NelAR`didSg_2giC z1PF+xyEp?yLL&o`skomb!_Xd6s*R90f<^wXQhM{#5(6+yH;{`-jtzqj%Z8g> zw1{?W#lZ9tpjkdaapX_S`&G}Iqjg$kr>m?bYzHM!Fcu-OL-bgqTrzv+0$m5!lQ{WMfLv>|Rk$j~VQt=pY4($_Z)P6>d0`ER!)~*UR|$jA zpBfO&{I?K5oGB?esXwf_<`79~)y{wX-4a)kmnRWRBY5dfu`45Zv&`s^^R^=xGiFodR(-xH5UWLx);ZB}_emRlM)*_elMgDE| z>S%v+Q;3Xo&daU3Ozy0v=`UgYWaEErde8bCi<8A!&ulf&F!0(fn(o^E4 zZd`LUF*kS0QShz+jN@-fGt5R3n&cn=J(8S#^IXO-{|z>ZeQCjF`N$nm?X9tGS41mf zgl!wz;kIm)%Q$9|NmhDj8)3PE@+P~Gj`C7bbb5nY~DNP%G=)av(1m|nGf5) z?HU&ufL*)l2Bw=*hYwQ*L*KrF0vU?yJIR<~30iN^s%f=BPLdre)F!)!lbZvX%iiUy zwB?u_eMqJji01d_-i@Wp;BQcWFcKz~xr)@g_>eN57Hb7XE_q5uL0D?U~fOqQew_aeYD(Y;4+L}E^@QE3@ zb!dSd^(lALYNCqEOnqcrSOk)yDf~pH06j{fh5QM5`XaH2RV_IIj4pId+BmdVAW@_j zo@tHwx1AMA1ux<-<-bD8pBOU+UUqI`)BrTRC>|Za_(zg4{D98c<-a{zPygQ@^?*vt z;G?eVpY+px9?OR%1I-}$CZcANyeyxwGJ7Mh0l;B9h`d4bFy*2SvS0rQ^t=N9jXOzY z!#IEpPKVE!Jvq~2q@jI97tGE8=*cF!o}C=Z0)M{v#;~D((}9nUK}gH}{fWZRRLJX; z>o<_`%qneoeMxp_)6hCp^?1C04g8_!4XA1W|ZYBFXdF&EsNS_p0 z{JO?Afr$tX3%?B;sN`py%GbxKSwI#}M5!#tZRs{E2LHsNOuUJ)a5g{KnxwWDsAvwT zvw+*%1fYkTeI`_`uz#ey5q+s(ozF1yF2FqL@&@bP6f`45`}`SdyTJM@&PCCiB}*2J zZ4B?33%}{&(D{bHxU03c<$+C&kYz@nC@~62jG6_ApL{BHh)Y@+LP=&~+vN)s7Y4lx zpY;z*9+h(e!Odk}&=}H;MlSpM3CGE&`Z0lCJx)!DqSerKR-hLkj$>(|p7>%YT> zg7Jn|#nA*=0{a;OK7|Zfx6I=!ifM56?7)p``}u)t@V90nL0nTD1I*}qI_%zQhA$+w zYBiB&#Na22djYZTz<1=mMe_3x&ERLje<|0%LoEEuaS>UO#9^x+y z+{{mCn8lGh>41#JFY$(9xZpeA)>76Z03Vr>q`468<_fvEIl&9#)UHFgWu2cUorw|) z@PLQVSG?6rcd@JKDAELFkue0|!{Ziwv|A2rJ!{zI7Bh3T7E^xgYvvz;t&ab4x%Gcv zQsk)9plJBeM)HJhO)f;&fCD$K3f_$K5z44fnd z3KB~*JD~>*l=W(_{^F?XDF6GXTj@cR+_o+u!>Rd$upy9`RsY|nd4M)~I~Mj2sO0^k zpiwzHaEyt}gCh|hjYOi84&|?%Ov{VBvZ1YsV9f^q#ro-VTfT}_%YdYS!8A3mLB^~m zIZ!F??m33BQzJ0q1_lc0UVC9Af1$LE zsmt{buq2R18NKB1`t6*q+)c0nDHA1K|`D6|6-083@j1*X~`{|+V_hkVtG*ycFx7- z(LyTrIH{;5Xekd=>ZYebxzqn!j-3B3#}G<3>^qP@woW6_BnOhez8M$7;F{#GG`~`& ze(d8Re!w+5^8xG?H(M~ke{FHW!H7eFhOs_fr%}Odx0ztHzS(HCmKx+KO8i&P3dy@n zc9UT_FOw@^l}t!~3z|XZ)xbrD*^OBF@0q^yUbtc~wb_AjnU+kej`FPH#qlV=iYN6` zS-A2X>_qOE{~q$$|NoHOvRhUeBq)rUU)lX$*8NrcmM8Lq>cAwjrM1{IR?QGLn+qiV zQuj8detnsId@b-Iui{fZj6GeFYcLgz&a5JSJEIG6)Ra2_o=k()}62Fg22s8vhYuvp2IiGrbyO-u%bQ z*&ZCq-z%_i){9e;;8U9^a2cdm!3g4@sn)mY ztaue9(I191kp{#M7})$&sF*~(3IWTt!zn2NAwLvLo3Z>IJjqkE+EWEYT|AQx0vo0L zw-YT3PWkH4t2p>}n6Kyy=#8hBMma`V-Y5av^80Ln`DqP=G-X|e`S$}$uW5aJ!w{Tf z4spr2gd%CZbYl4)V2hmxTVs92rJ|s?%LnGk^iS7gvkBa?{A@Y({KVgR!CO=u8-M<9 zH(}tN>9(KV9Jzt;Ey^cA_3)|d0E5V>HDq4r;th^9|Mv!_aWu{E1Iw2)6@5$T*@0SZ z&W2D_I0(Qm-vU+5wp{IwShZd{WJS~Ktct~Gc5ivoEM$|MWI955|d@W(!mrXr3viK>vq z;yZ{`{QqtAKS=QO3P+|N{yT*(@Cyd|BrTg6|JEkxO1l8ND0|bNTuQ&=S)|gm3;6FS z=tAc8{(BRx|Gmj)r0iDhg;+)U9edyxvtb@JqYdtne66_GU!F(nSufLtFVHcI+l2+1 zUr=nUA@UbD2{LaC+4cA4a}YQa;FyV?vtiAwuX2?E%4y?!JUAjKrmxK>fG($ z$_*|qqo=%Ko(;n#m(t5I&iCs^gPBZ;6ci&)(*jD{1r;1~lEM_|#Brf{T}REP_hn!G z9$9G=CI)Xe>GR9&f(i>#$(;MZqa0=h-UtH60141_yy8X~xC?*wVF>Y%pH?@ofVU+A zK`?CQ9+=qQ@tt6@4U(Y$&`3oMobe*FQ53P)rnL(hwl-DI4~_Q?1nw3YBJVX1>Yy!4 zq6!IXKzxwH1Qv;FxF~xA8UUPHR7?jGJu7TRD*7oG>1IyrETE%gNx zmnpdDu#@^*|A)`V>_Cks>N^~AyzT@hE?&JY442@#bm13lm%6P~@{Nm9&J670|NE3M zD8R-$iJCP5gvky4$`^1WS-RoXdQkpqbcs`_(Yxos%Pscbe)J>XP%QF0; z#GL_7@b;o%a_YF`#R}5oL~Y zOdPLu=OASHGHt^vbp-W*04Fxkt)S@Ecdo1zrsLQ{>{f}SW1VqlX??76S2)zY(J)xl z3gqpCTpS$BIvA=*u;&?&6UxN#73BZLgLMOV0!WE}6@s2?f;Ew;F?MP4wH-`FaP={Zi8`gu-TtLt9Jh(0J5q|L4llFc;=1X=@+4Vb^9(w#(^3p!M@G#2D_H z9PJpW+^)lU2uAO4`yH9f{_f0yAqZD-oHu$9(gFkOSML*ppD~2Tvc{&JctT(% zVV*+8zD{{JUD8ds!d#o=c%gzkNe=@vQqU`I_^y0$cdcR_u;Kw$9`OE0P>ac~kyFq* z=K(Uy69`D7eoIFKx#4mcHv9ot;*-*NMn-EsA&5i=W)x zF0LQxx<@UD!CLdJ<$y*E&FKG#_Q3x7B^KzFcxX^k9VVQtBDddCaZ6d-P@cpcEE#i9 ziK9SXt3rInXOJpbzz9 zFoD$`$0qLL+Y+Qm@)B>Vbx%Xo$gU+)(naZs@=X}LGXl&^?Lf^8Im~%9Oh&kglzp$i zNc-b>XIZC87G824-2@HM@Lt`m-?Pd%I^u5lw>0~BOu3H+1_Ri$u;z1GzU>X$==)OU z>q${2WFZs-exLJw?{Go5U)gx}-@!gX2nAD)k`zChg!DK>?emnU#O$8YvPMl`=R#E} zm?NSwcc<^YflS$Y(yl_aNYre-2u09--gNAHGpJQQ?3=Z^x|_g$sU^_AL}p7n6C6er z*$?rGobTEe-omGY8DxSCMbep64BpHZp^i{7dGgTGol_}h}4lI z0lCNbe-iGUNe_a_ypJY%rIJY7!;freUr8rnz8|dnCi}|$PoyqXce~`$G|^PFR7Fm} zH4wYmc0YV<_G6fU$1=^gZJv)RSxc)8$XTrDDZG*Pyoz7w?Wm=kU+r}F0^QR4X&mtz zIwKy7Aed)$7!?2uu@V_~(m79OcEQH5oU8$6cvOS@e*F{*gc@pD-TI{8IYikAAU2zf z`zy?IOM*>B(Wb)Ct;>!DNE>&uHYt!>?hTWbrm1$K8Mu^90;0y!R;F^ugR$f2bdTq8 zCjGHK8WusZZkdT1G3fMD0x^&lFunoZxs;_@5NUF2wwbD{qGkDdXQs(l)l}BT?Kc^w z5E=30y7FBB6?FXSpT6gi`)xn(*nJ8yxl+L0U&x>=HK}FJc)nf#{Y8H}Ksx!SI4sZ! zNO(Yt@gm40w2_RK7K}gggmNq>j&(WUj(z88?0ty91=(GJ0^op5jY1&a4)ak#cspuZ z35fgTxdzb$S%i!f9g zu$;B3iD)NwUeg#>f>;s`vvYErlybu>j=ara><=V#di=}v{pCeD5*WYZ!QDa-HfuhV zt1*%;ANE}>-!TNMLbhY=44TN5dthokmcKtd&gOs!lF&|eLzId&6 zpb>^QBT=hO@)2G_y4yKBVF5R3ZxWxhI#Irvca!E)8G-y)5ecl52w)`l6Z3|ed zPC1lDmOQ2i&EN1h@$We!LO?3qi)6n^1h}n7g^~)^e)oubYZuvMB z-B)4iV>TVn1R_seHgtVsxa?FVtb_JkJl!m|EqJwy@49*SGeHl~H8|<&1wAZ>DaGrq zg)RDv_4lTun&?+2stRaXkYv5*Vp-F9l;G(A&s}Jb_po5{h`E(yR~|B{V;Wg{5!a*T zZlM;i8ZHts{+h`pkNViHX>FYwhyhJ!55T^<7C0uH$WdURY;8fvB)z{4_4Kx?Yhh}` zYr1jgQ50ktA@iFwNmB0ltRSB*127lzPACF~pz;j*ul~&vbmuH~*K6(yEK55}H^u*= z%8z@d_0J$`!9}(czAIWbVE1W?$A>qs9#ULa>!39pL?Wrf0s_LTgM7wY#^{Aj7}lPq z&-0{ObH!8qQb6gnKIFzJOW2D|ujUFWv{R3aemA*0HJrvFN25#rU}amiXb3tl_W9i( ziH}@}m3g7-WwuU-c=!csQXM<2&YyI_t7qG!H>Ix4FyJqc?gSGjhsk#W=jC+x;0g=! zD(bH2;OYBOa13`g{11}iUja&*7(>opOl_%X8SsQn#Ks%MW!E4qDhD~t_s~?coExTh z5B{_V)8e~3+Pptp`?z^`YC~*Z<+qI>8iXeuNZ5Qz*P9RaDH!l!zltNN38-`V2_oX> zzNf-y5?a>MNrqK`5w4dwLD}JfQB*G%ToXh!kJYtXkU|-0@(v#&hv+BpNZV6z(xO_i znmlsSCYHh2IMVJ575h4TMjhGF*d+@>9wH(V(9SD|LPakqWw=5@ zNZ)7>>8O zdB8WQlUX+$qqkbPE!QFAb^+#pZ(sEI#36M;!pYo{e8@l^gF;=2L&-mpxj8OmaiL2N z!-O(+1R%SUE}M?L-${=b0ttdsXBQL3<%GRkx-+f9==>9p76gwSG0)UCClRebVpMH+ z2jZ!`y&pMOtDwC)Ga+Y!3#G%2Rr_iNp|M8?z1|pbwUBG5b|iC%OlN~h2MHA(qMb^u zuAQAd$h`Je%(PA^h)U%_Kq+QfSG9~_&3vcrsKAMO4?qT5GUv7T@R^CyUke=m`8N_#Vh;)Vf8Ha)up|2_nEPn9Yfc3ZvxLqY81Eb=5!mlW^5m z-wh1}%LsVJKD+GyTAYN`d;M+N0@uM1$hlTMOKz~07mbu8kQtkXc~kuHlleLY#`ZwH zyJ$UF~aHEv85#G5`Fb)RM`) z&v72Gr;`A9YX3SaeFm^0k)$KrtLJMzSbS)1{ykih=zAq4O#*f0@`U7K@-48Qx17xe zG~o2>oJsp8#Pp|oF~lX{kj+%w2NU=t`Uy7g8LZP_q^6*^={EH1J@tjU*iR46KbN2l<0*;c7u=*5iO?Vn`}B^^Lp5!PqqS-V-i6%uTySR)Es^qe<_TS{`2iF!Z$Vwh1?~PS0TCj456H zTKRP~zWauTLwTTMAi~_?r}qXi^+_$DymK27`C$c`Hh*IztcAHrCt8#H_E8GbkGqC;Sphx^ ziA<6%nY64!(YX>j;@Z>^>;v4s`(R`1gEQQN!J2sR}l{Go#W_2&7a#EUb}&eB9Y+Gj@*ZOS5+?+@vP4Fng8AT^RJ88jCDedYVF z8R|YkQ=1%zAHHcc@@Xef!z==4#Pnxm!()Oezcj?e8CF1V!o^mQOkAI29E_9VB0xX) z`K}Z{u?&m{Rjj>^JQ&MdG8i{~MGV({LS8DEsiCvr3SLGgh#KeNyH$|Gbeoz~L5y^t zmpw2jG?W4w-E9!2{S@SsloY5SEj7sw;^U_ZAMj2oaZm{nu!!Cdk$u-_Gt77QCj?sK z#f?7=CoRIx%kZn_z~)imJ86IeY^hd>YI9%gDpOLsa<~*!Qa7Mdy(VVfs~8&ut0G|# zr)Mvpra*cOXrME&UD{KvzrtenA+rNUVaQc`vHBF`vcT!7SdVY!kLi6M53vi(K#VG)#1|CBNTrYCEY@mk~tv2*1BH z=uTcgh}3rI{Ygb+i<0v#wg4 zVWssG+M*+G$d{>aFP~{?JK}onSQ$aDDsu3*$egayJU=mi#d6M6*8~O9W{V;ZwP>vp zz!*Wbq?V1ES=+TCb}VgqH9(M#U<#xbr&^(1+f9Qq_%+{kVmzdGLXILLzoWd>pL~h# ze+-3V?a&sOp}n&3_E&nDpx-5c$>R777wPdUwE8&QUXPtx8XpalcC(psUh6U8ixb^f zdVgz%?+UtsNQrft$(=0Y%Tl^%KG~Ly{Mj`c7VU>+y>J$zQd_{=#DXaU80)>{GoQRG zn$j2O+lZO%FqNW;Sl4$FBWNEvLcVu^1*FT)niEG+At5j zs1ckoHaxFZ_82(Gd5%CLRccDN#?0pdj4TazaB@;uAIxU?POwvbFu@0!@zUGmYz%86;hyC8V0G;8AfV>L5U$U3H=cK=r3Iwk9k-0<$`Fm%({G2Q4S@}%-Dd}U7zl*0Vx5f#ZB!9 z2ZG}@QXcvhaoymk&!en?#45OYFivODnAbJhn<`_zDkh7>0Ow;77 zTR6A7yZcSL7p_Qg6(fPQE48{*kYh+MjWJVxfS4-knTVaJzINiy3TEf`c@oq-Pq0;+ z3QzS-kXuBhg<|vhDy>!-h#;em_RafSC5xFd7=e7CBOtC(??r9n?b8-#_TEF*RtQCL zkL9fRi5vdEwN`-{)NmwUj3Y`21vQ6!F?n{;e8$)mOS@IE-?q`-Jt);FQo ztXcV4kpXN1{@=pPkPz^LXUGvnXnyqAg7OqnybF+Tf3Rc@)DBgL3{CA#?o^+FCM0mT zrj`kUG;s(bhiB(4HDN!S5jHT@59{f~Oj}>+p}sQ|XtQODpKlo@Q)-e&&14 zVsi5dPK>g&cf}5h@nrqWnn*A?f%jY=A77(obojP zkYDgS7@JOZ35){5PkGspeMN}E^)-b0BS-_NTul)J-vPu@FTZ`i+q|1%>i{Cr;dk94 z;;Z_0ZEg*rMNAb`FHL;cEinZi;H@{2qUbXxE?Q8tel0#-aEjnQk@PioR4O9V7HX~D z$glB^BZ>zlCt=P)maRQ|0MtH}^obXpp+HZ<5THrMc)!_u^H>m?R#HkWVQRuGU_#yq zsKf76>x|gLC*#}9y_#u2phPKpQ6sf(mc;BCcI*!aC#r&w%{%s9|<2In17US{v05Oeh}(bFBSX z+;vXs5U>0}@gCkd$t&}pK@eWVRE#9E=`4x>6&~Lmc2XeK`-@~myr_%&Iiu=xr=`p( zi2jPdcuZ7(Ft$R;7I1zK!=S)-9e!}5<;_mUA*iE1DxCQZi>pe|Z{eoWJfL|U#)>e< z2RxCm`8U0L4xJmMzFO#X4s5O67afKtAubqjv%ug%N{rxT(-}akDn}CIG>f}-b_^Vn z%~5@dN-;7E!WjzV0{@*?Y#%mPA26Ec;-@ue)lmk#h95G{Y3>UqsXz^i?;kv&xP#~( z=;Xf)jfloZw-ZbAXX;~}ur`l@2|0rxmpo;8=cpAg5pWFHHT_zs2CSbcQaDRl(I=k4 z?#TR^O+3u6wQW#w6fdx(=ZvcS7TrT+CzM-I;L1#KdyWbdeKwDNj3BJaVQPYma;MU9 z=1Q`R+tUX0+q`{H`m1T3G4UG2AyVzyt%7{) zGB-VnlH0(ZMlpXZP0agx(O-}|tS1%zH5t^4$IsrMDhZ}UNJ->S(A83KQy-7E-Cl#3 zw0H_+{ZAeN)INO*kgKYra_6C1X)Q+Z`U%X97I!`MIsvMv;=2t}c!bI@#D@pvvN3{p z#X_I`ONOs2uB&knxR|j4dg#E&tfqwc=yKAuwx4Y%0e<}kPiM8$Z*Qiu76mMd^#|YP zm?hg`b)5K>Z3~n;@coBB(KmphVg1|bFhD`-YkKCue*;P*@ZS2h(;h0b2e!g=M4|Kh zgQ@$sLFI1SCO9AwTr8E?p^p=M!xAHTe*T!Py%4n`8sF7pmz%I26+Dq2Eh>{{U_sa> z82$caP6w1=Y|#mcyr^YNAIx}+SDu4-Mr-VYn_HgY6B(2l>{yT@k*PUWD%K9b<{p=H z`Zb{2@n)uT9ir%mM2`S!btKb{9J&B?TS{J6>0MPiLwc^b|MSPS^ZedILTsg44;-dkE`rxFK(-dsB6^)9ODUO|sG~W~(Iz#7MC`0_s2fwX>*sE979ikQxIW0I8=V#N6;yBwLST zZ{eU#4@|2K=(8JFIHAav*8+&n41ia)LiB?MaAW_u{_vSk#vF8Q)!a%wzggVfM~Epu z$Pu2TeCPF5C4-8(rSveNo?CmNGk-uy_I56Jp_W!0l_SNSKU#M4_w&N}CWP^3qn zlgCyECGZ4xUU)KG; zOgDO?jEeYj;*Pv0RB7w4{8>72$W(&YLw?j%QKWs1OXhH?iunM^bxkL1WEPtih6A$M zsWZ+I8t#6NiWckhfn7J>dESLzp=m2z7j^g9V}ZW${5Wf5HnTC%S~gwYI#ZzW8LA zm5kjfG+~N9cQt!8o%yRH8u~5JQ9AcY+n>PIbzY+%&YM7+g4`VxW01+xYE>}C{IeX< z6-|Pxo68WORwboKMV-uI4hRQk}Sin@7E|E9Xyt_69F?T(si% zGOk0_A}I8rYF$uK@3l)bKJalN$m8YKOx6$8i*FK2(_Pao3;- z?bl^@;L!2g(dJwUuq=+@f!Ua5H+YSW~5ZXZ%PRwbx<%dXl$Wzz{VUwbVq1aEzNoRRU4nSOR=wDw? zrL&mNLLGb(xt$~I`H`o;LMdH5SbH{uq0q33?@^zwk$oI(QNV?mznDe;aY^7Gbr3^S zviK%~88Y-RMgfnAi%@8UNCwI8%x+Z}a+XQ2)az5w;q!rml4i4{Qwm;%4(;PcSnh0s z!~JOvgxh|2HK?qR`}FdzO@_liv(@>3WvfV@Y(p4p$ot|Ieu_<4Ma=G6&LM2LA4(rC=Pm!&_WyMVASQWmlo2Re z@H~O%uA76k2aL`6quh4fKR{RS z#IaM{Wzu%_3iP!2zxlUFPzP%*xN=NK9-WR@$JojkGlmyT)HJtqWZsUJLwTT;?C8!9 zpkeNHZ6NHNZ=8p@c^S+Nd|eR;5X(QmKNy>V282%XU!1#IwG=5!IT9rW`08ly8}5vF zPlWnAy_Ye8!1`hE)$djWhIP?0h!%h~kJep|Ho;yPhAGW=ACkB8ju)Y@vkmS>P7n!V zfkg^`uzYq{`)`{BYshr^!mZcG{^D$3{2ODG3xpMaGL@|yhUGx`y}=HeXCNk`k%1Mz z?-rZS3$O+}j9vP>OEUn%CDIl_!+M8g#5g!lSbeH8m@i!Y&aT!5h@erhH+X*Hf5(7_ zH&2lf4-oMfSexGDWu_6BrUBMQ%FQ~R3Dnin>#oPq7}eL_ zn7TTtc`E~Z+GkKhHSygVMvCkt>1NG-u=$=}XXOH(jA5v!GL=KI9B|#D-a1@&%qrm~ ziB4cO$8{cw44nQ%|g|BfrJ841gZ1ChqY=csP z$A1>!UmyOxD`%%@Mou&=nXga$J|Bz3sLS+-Azc0#Ce@sbm%lk64S!%s)wiQbEaICb zgpL#wPHvtq7Or>_$E$2rbQ2}lhaLL(;2g=b@$j#nV7X3RPfvOIYyh#2uQAoiNYFcX64{+NO>K-2wrC|n# z@$k>LC-rsy@EM~u0r$C{UoUPDVf#d9>KCqBwmdOyf-3;~V9*^-bbu{ZhYqg{ov6rat!aB{CrGh&oYNN6gy06?UTpkMbE<7()*nNZ)WF+lXzgsp-zTMN*sTKr+qI} z5hY{|l0qSrgOc)q1H#F%<8A~{Q%xU$pmX}An4CV{BE!#eXE83)Firfdk#vg5f7vcD zUw{3SlftAKS22$@{iI9x%6G5lsHF&{2j$*;ck+a&a~wCSN)}dQ{fB#@kyk>3$*J5q zb>V{1Kkwu)J}7#XacJ=0naUD|%Z~UKQ}ITKgDEuGF&B2ezB=woj>QH zmO(oV(wV~I;$lM`^_TzW+z9@|m=+lP2Mu{Ltliu4R=og2ohylAGP5b}njfDfm-3$< zHy_T=M;D7%Y@$RZI=i#)Yn=uz?_6$0F!m!ni@H}lzz}?gVL0HFmlkhlUcw@a6UFow zjOctC+=;JCE#gj!Z1Nxf_cmeU$Sy%F4>rwpK}9d-`DJhLtbH%+nhOixozypIlBy>i z5+aBYfUYe;8}FLJO0pY?1_vjGZ|Ep79#$FpI0yKA=+tP>h>=E2!Tj)!NF?1B#Wa3RZ0h*IY3CUn`_ejt2x|9Qvm0D{xO{ zM+^UBay8Yo2FhHBpWeSt0$@oj7WRv*)XmzG^xSDZjgh>nAU%`wyzho`{I?V$Yj~#|Ytu zr~g>5PVryY3jdZ=<=KVS5K5Q;z1YbDly+#z@mhNEVpO`frSHkgefY~&ILiH55d8~# z1{2%#Pj-*p<3~C^FfOmP@FXfxYlV)-ZIi81jd<5*=A&V3` zMp)-fb%v4i3T-lwV9jndBd_~{! zofWc9n*V2=N=K=k@dvZ$Qw9m?Zn`kq2jSn-%{rS%jHa{`*S(fYQy|MUu|uL_0G(!u zJcjdiBri81z%1h=Wa{<)zoLTF55egwr2ors$+GG%W-Ri=y2xofdztmIN3m%mf}J8& zQk@l08LWhP1@v@Ow?~sjpFytmDT0aD%KXxAgDRu}sBb6wFf|4ElQV$f*Y_3;cN|0D z6$4OOe76c*ac;aw2*qK(X-y;mb3OJe(msaJ3Ie75J!YIZ};XGLFB1+`>gY&RWyL`EvrJC2qrbp!SNnvJv8QYo*753eB zSA{Q%UV7w1CQ7>4x5ip?AX2f}z2>;8)|{2FH`*$BDw|kz zzFL}kq}g6mlWn^+OQ3jOF^S3Xx>GJ}Ms)&iW=l=EUwr$U{r%@p)8cKYfIkB*G%Cpw z{q!?`;~m60-z|cGU^H}Cc}|hB?rn+0>85^uG0iVhA!r$8fWtAqxIpQ%Z6`c=deyk& zl%4|pcR!+U?LGKrJ6oLNBX^gS96t2E1@0iVQJ5emgKv8~-dCETBs5^(AXXJk;QSK9 zfk_Im^OC2s{FWY&PNgoZFLQ)2*e;zX4E@Petfn+94dbAJ$ArO5SZ%Br(GyN#C9Z=- zy{Q}++`W>p@+JR^;q=D61*Pozl_ALu$u0LAzRjDy8ymBo@k!%;tjRNBT5*{kw>H|? z1S<+w+fIh?BriX|*t|)ZyKo|pbR0EyRn2Gwh)>~U-0KMD_z6+#Tv60Z2Ig~yl{}hlVp^WIx%pL1Fy{;r zabfUv?ik=M1;fn~H{Q|`fbcKH?tTBX#PR;}c5;gU{;z)iO`sa~F`Uz<>UrRI3#wmH$Buve-W*yjW7pdGN zj-SFdA9QzDuh`k;{fh6cYaJ&-JOXHU-07Uo(8S)OV$BU70gi_7{UrhaU)K>T3%T;k zdo}*s^)uu3(^-}BK|di^9Rpm3Hew5!EFvuud)=Ow-(GyT|Mk(;F(h|ahz9_%amicT z^!%tr5Y%>BO?M|oxh!e`%@~8)oAt)M_ue$DsSDCIQyxhOb7|-isCf3=5lacH2G?SR zj1p-47U3w8RXWN>00G}i-v!}R!Kv6D6<0x+^xp3JEUZ;Gm$s3HIX5!(KH85Ly?4qA z+BWozr40#}?QrsALq1sv>#OGj?0eR@r#D6u#=lH&Y=~6ML=6E4}N{%F$B07wMZg2|PeVEUxm%VS^7d7L>)5Yl6jf~Ul8Q+9f ziyICbiEY6Yx~BImjgIaph5efgV8u$G%p@}PHlJuA$)=`xS=Dic&&g8H6UYPW;ljP8 z(3e(MF#6?n2d?|sO{CnYnk3fKdug-V8gKgodN{1E+JZ6l`%y5E$?F^9`F0wLNfS_p zP?p+tP|*$>U3xe+`;I#`{60}r?JR73$ojGo%w@cpMHSm|BI=Y%S*CXTb>JWP*e z9_?A1O@5`N8?@EE_TKcWqfo_h&t?J93iqH}4i_p+CfJN@_Otf*op7yWJoQMM&DaU~ z5_wocD&`;GpYlON08-b7w9w&10WeT`y-Yqc+|HMF`5YJB=Kj8uUrfFFAvAiu{|R#K z-PIE|1xwfb>CQF(`QHEKN7aa-E$JGf*i(J5zdH$o-q{-cklF^+>!%vY=9EK7GtY~8 z?#w-U6epvelGewinkaJ42rtCQsp=eK?Zkrq|L-ZROVo9gmo85~UXI8)dn(=QhNa2Y zpU#B9piZB@a#335OXj#{p^Ew*l~xs zu#x1PGyk2O;=O3`#G0Wlkv~)2O^&$7i3y!InuoimPYqRUvx}U~D6PFxeX&PncZ;rY zDsx&lT4m5bH>Zk``^*{oM-i*Z?aJQ$ueqK@=&kU69q!%3Wp@u3Bqa#x7Nmfa8cwX- z^rSK$F>ZR*9c!oexh_lRSI&T?^(gr48QYxq-F{{>1SJ6t9y_bXJ#*F;f)Qb zEu~e5A6r`q%Bo8{z#HN`sWyLNSm5MUzdyMrNxlGWJXbfw6B$YstY4e<6$;x7FtAvd z=($NX(qM8efx28I&UFV=nd~RXN}SJ>;qiDL9iwN(_LZ*g(_>{yO-je@m9}(r^`&Qm zV1`ygsh>pBfuuBWbLM>J`+P+$s#d(AzynN$1BU3caoGb?&h$=kbo%ve^dgUiiL$c3 zNm#}T+Uotv!ij0pf7kKRzM;eQlliGOsTZC)UMG;8hYtM7^gE04k66c=h48L< zcU61;qN}>EUz==}YV_DE@iToU-kecAGqQGlr^I`ilrh~7v(1zC{n8T_gmIChxOv1W zpYog~&??jxYzA2L!fz!_&3%|xDcC_Cw{B>#Y_^rJH*Ql<#1Pjr?WUEts~~1s6TkEI zxc`nUot6%lyXD&^pbM;-Y%t|dXm-0+QWS+c@?F`DN&Uk)QTs(DVM|mkVc~qZ;1$TA zsl%Y6DbTr6OfiPYY};opfP8f}rdlPY=JQjd381*z(}=yLv(sZBI)CAA zI75OmuMb?2nu=a2;6$$pA>KSa?GKQjM0a5V2{YNL{mrWfgTP@g$$c7zCYD-+FeZc{ zzqks1eGKl=)pKtP%S_z*BUMPZ1`?? zM_g%!yeoWq#__5zEwjT%Ctwy#xL@E?$?XA!y+xk3%Fi%W!RE)K@P%>l5dK&b*-N6=5>uhB zIsOx-tmHN&+ktk#D>=?$t|&N3E#m6KG{b?*I1w%q<=!&gEZoe zWZjsO&1MIu1>x2>l#4V;ug9+>ZL1pr44QGAEbHaq4HnThDb6XY_=uK5aYcm$k;&vohrl*z}d?;^$Z`i(5|DpPnlN`Krre zMvb;s&^a0L(VJ~2z)U#nXR+8U-$sdeZbzUr)VaGl$=sCXv$#8zJ z>&%YGwD3gbcjCewen8T?um2**;$i5EItkaYhaVp>g<0TxjkAB@Mz!pIK|_#xbk_Lr zE#=c|PtSw4xv%CvsvT$p*=3?1{T zYOM+Nk$%VJk7=$g;XKCa-F$mulu)ya$~+&!{DXAp22H_bomXskAAd+Jr?8gIor&yZJ66ZM!R{!HfE8NCUO0C)7OZ_*PUGAS!1qDj*L$wR#RLnfg9s4*Bc_-0 zr&(hdUNOs(uN{mx$%v{l`XbwpI&*ysl{?of#gi29BEC1a)p+rJ5;u3shv;Am?bp2g zlq+WPf;TK#qRcHI{ir~iJ%`kIJoqU66wr*LEL*oLtTSrF)VrvdE42h{={otS)1pa) z*7>f2>3*2lD9n zXy85eZT0@#TU7dO&~?~UDpj7Gu~d2?CzI>mGBPXHeiV{d zG=3q<^W+uuN1P;MOvYeGzjqM3VATw=s&cw1lq1rQ5t)IqKC^)s;8@i>n?cd`)=-XRm6$_1ohVb&y@-&3JCftUuWUVJu<2%Mu}A zwcUui?gl4l9c0xCk(2x1&tf*si_h(=B8ib2H5{mFhndq~9*$uj@6rp$ew7P6(L5CR zJ5xP~Eil-KyGy}5W8l=>bW?MZ0kiik_zDY_->sNFN`j7p9|rO5oKg~2k6rCHbbg)+ z&*D9+{HRNSqO2AAAn7|OjSi(f>GbbruYKV+U9HLJX?MQ(2ky@#&(l?vqz6fHj%;~m zV8VNTFVw7BcubrtJoZ^h)p;r-Qi@N?xJ;~rP&VeVhKf;Ax2&t6zU2tp9pMJCvu9(x zrVB45P|&gH_Kk?I+l;FD`s4E-KCtmsyqwWe=ck7?koIOgr^oDNwk%Xxds{i81iJ(6 z7ED!nu2s9!;%5ExBr{bRMvDkksUeBBY8q=Xv-UE}6L+KS%$#`Q^(LX6kl!d^z)tZK zCMFGQK|B>pBUKwt;&sD@ZCHvr3<7)ca>*SF!l_El&pRtjtOagdLS)>W(N5X1`{ZxK zVmjp|22~3*k95AJvNBvFuUvf`tOIQ)Z`l#Mk*WAyo}FozJL5N*AaUdl`^fhTNl}FdArZ2z%O(pT#k^2Z0FYSMrSc*vx8XKnd4@tils#I zm~3-cQ2U8Q?*v-r-1qqgrypS~;~jjOwI6lj6iYcC%^&6QDZ^_HSvTBle5yZd-?Y?D z{Kd`p?SpahcDcOO3;!RO^jAJnDLeb^4V(C0IBw3FJUKTruRQ!ws9=jk;&-KF7Sv6JGk>bG9JV$sI2KnP+OY z75u9_?O-GCDCrNE54)Y_d&{%Q^_3atJyr4cM7~=mzENK`rj{9Ge30V!CwH&bs5+L48 zvpbrvz{=(J)bmZ8#OP@s|IPA|9`m-jx%p@;kMU#jx%;bv1O@7LQL}WEc&rUtt&-M0 zTj`yS_}+GVZ{iJEyR?B%HLB zF&!DxY}KC7xx5c`GsinF3qZRg&qCfg;!e#kVJNz@J}~G$!*k+p(MoU52ko%m8^Md~!>9gu>6|4PdNG!D0r({~<7eWH zX^3hq{eN|trFUTJp*DAmo~JW7EoENF*LeyOpY)a{hH#Zu-zf>T%rtqPC;0A}@f2uZ z45M{x(hRO;U0=CY>pE3+uUqiV7|c4#umw2JKZ2nku#HB0C!6&Tx!u{XMM=YCq;MLU zGyI>IeA2)X#=pb934TN;hQO@asU6~Zj+`_*bI1KpSU$DZb(I@uiq()2&s`hRx*-}@ z{q;IWZnF!6#fGw6Z8&8o&kN^wZo+lido(0Gr`rr|i-zAC&;DMtqIcreZ+F-l)_Y9( zZ8YR6S6)&3D@Nwp*fX6A7!vBlPV3y{hu|gv+ji_!#<=q8wQXiPX?l<(th`s$o(-cI zGLx)5*Aqe^Qqx&i)+^=LT6nHRW%9r9*1uEk6A4(GJH1sEGsmgUQXTBhPLPM!vCe)u zf0e3aG&+bn!1xRj1z7C<_B+Wh3#VSI^Sm`}u^}^O>v`Ga& zch&37YJVoGgPoDJTrXS^D}A?$P0vF{dau={Jc#khkrzKtxc6x@)jw?!zfyr$7TeMB zZFa=nBr;wPxH@91B}2e{$w^?BcW+ocgvDp~Q3A!6u7bu4G26#0B&y6VHKG*~W%$=z z{Xqi1N!1?jRcV#s&8AyTFt%m*a-RFl>GML2)-%t$YKz0sRlYu=-i|4^gcX#nS-Fmk z*w%C7rGA_cVy^5B7fz(LK2y;edbZfwRXoAg=%737%L3Le?MVIE_g@t9!)N(3>b!d% zSG^piBSFf=8D?=rdwR_SBziq`k2kww3knhAB6YB%@j0rtSudSpN1q7PwsYGYB@_;Vtn zhF<^f9mh=mkfWJ=P8+6r%HcQszQ-`|yAgG`yDDm=`kobjWOYVPGN)lse2 zDCa2iHUazDc48V;#Y^m3*#zP^f0d8toFCCL|JdFZFNaC6FScbie@T|%1!nsaT~K5P z#u48DIFB%mwosNK1v7k-^4t4~QwO}!aIkgVr&f2tc@~XzWqFtJH7+ry3VWXW_Iv37dvdmj}{;4$%&H3Fy};zo2&<&c>$Y)8Z#n4SI=^}OK|RB)y}7l@Y2K#$>|~2e*K(4!{hP+X2_Z{Db+Y@l+AI@g#lKHG z)HFsyEivs@-1IAPGdoaIJw9eMlkXTG#K0}q*OQKEh?V1a_h$*g0Y+u$H1EC$p=7Sl zD`^r|I-_iJk>513#_q#9vO@~|6ruOYI=j;(H4=!k_M9SRI(gTBjv5yf25LUx*$pbk zBGQ~5Yh^($+LBzu4rE$&xsHD8;I6;YYq&VvyhoCZ}D1E^J;|*w0B8TmPub6k|6o-8q~jRVj43VQ2mF}3jzt_eT^nn zb37SkX4_4Z7KB79^b!#j>ZC8s%O?xyLMgP%XK$!ZX#B#51#W*Su@cC&Y&j)D^m{Mm z@xHfmKid~By^*!OSlP*LVqxpiUd{4ZUe({3-|6{o&8eKE|3Ew_Q6|lk8Qc|_wog$s zVJimX;(bcui2Sk>RBbuL)I)+V8{R4Ovr8Vs3ZBoZ^QczQkPz0&CSheu6eHOU@v5L> z<_UT=CFm>huKPV58|z#tIjznvA&k+wn0UJP`1C@h#fI<=}Vx^%$)# z&EkOi@&~(F#30tlEf!_{%JS-HS!mr;=|p*J zl$ve1Q#i=0lZq0?%t5XxB3A0=JAycD-zuNkg}cQrbfV&xgia^|Gxa$K?W_&GDH@Y5 zOa7?e@AB~Aur0OwYNx+N-j!oCsJ%8PQ^=ig^KIb~uxz0J)S>C7Y-q+68U;_eg=JW4)Q3kCfU%Ch(IrT00`|~f_ zCM0&6>6N1vT{M!zZ5Q`bfHrGGXfks=SVi=3)754NX;6lZS+&i#r5|s4Pp~JLiil8h z3ivstl3SYHJ}2qO9g3O)cats{?`uJdb`noC`-gfxl$jFol;MF@1@TczTH!j`qFqFCEF(L2|1_I5#1AtR?xX<3>{~J#&Vweb)7%fx~xq&d$6i zvZ%!vwUMyJqv~*!Q2vyeZ<+Q5l=RuLoRSFy`L#6O1JqJgWw8VTI<5ou34%Z`ef{7S zPy~xf8bKJ@O{=m#geKl{`Q-7w@B_%RhC@OV#XJPu3rjvWIXylEbtu~D(O^t%x9Xnz zaNKP)gP|&N&I7<+L|jU5RyJ@I`OuPCForbwzMtxGUvz=7`OGdpA=RFdo{ZaiZ|G{A z&tm94X;NTAgr?P(WOvZioVl{0E^BdEHxx~^XLaybLiX<3AbYZnN`b9}pXRbKNVRUb zejRQN(C#8fw1$Nait%}wEho4~Qtg2&jE>9WMt3uZppForn8|+#?1l(Z_UzWE8tSTW zv8Q3n!tZ<7zP&6JOMP=ghjT(207!8Zz(oUq11YKaKD>?G-@NmU((^UVy6kk_&t2!X z16Nr9Q+u)$r;?H{Zl+J|b$r|q+TRcvHakhISQVmW&8~JE^m9_lsRjBlNo9<+l#R@m z(I0-or)m->F57xx=|Es*?&aSpo*2C1IeE!-KyWNCq~h$z4&UCDX9uJkCN$u-5I_*v62+r_IcA#ycT&N^&En|OtWnpMLUCrApv5sp3^ zkQh;k7{0GgW14|e!IB!*zIrCuM&hjaLTc;0I2jK}(6x;6U!+M>p<&gDd&h!J3wGKc z%Fttnc$!eDGBDLu2t4|}OM*Vf6jSl+QX$?+JNw~g3tEsyj=_G*=VdtPxW2>%F?{zy z$}ROHh+F4e?%WB!TH5NyI4at8VFlmWS=B8}V=$lkE4^^C0&mTFNTS2)FL!cWj+4~b zou_9Q1t3@gxwlG%b0S@oD^%N+Gub2^mn0(UeovLsIj13hnzUBaW3H+)WW z$+6m;I@pv-7qPymY=xUsM(bEwW?g=CLB?5)&?|*IkJ%~`bLXLiV^BwAd9@O3Y;9yIh>SGY{ez4?*)l+@9xU700^W<8#Hqe4 z$aYsG!hrn5sS;nF4A5KW11>je&9W7k;9k&D$H|YKP8Zf$12G_KxaPw7D8>Lx1eC}t zuJprQdgg6D{iH@E7r|-}TjrQqc}slh;TV!juxb9L&W&t|L)%bl;inFmRm(?) zilr$aFW_)PJpI)A7Mb|9#skwV?8rrCVEH`;-A6qlT#RhOMABbx+HcOK-ki0FzIN@2 z#W_#)dk=xMtks{fI18~h3R7VxlG-~1&No#i=s08dH_oJuV)Bbzr>?Ilq9{v;|76x z02N5Wjn=pJCAokK6Ls~X`S@gw-Zk6Jb3F<1APl<< z?Ys$$@6Wddm+$#BbzfX-0c{a@B(CU>2Fw1-V<}FZ4Um3vE0>5JaHV{=u!s*(tMUit zR}bji!glegm*6T%rgk5BsgLxSE5?eE1E6A87+bZnWy9`cZ7lrUSl0eGhgy(6lN$2G z`_8eRTEv{^69k=@h?BDt3K2TPxE`V|)b!Vq1ylay)rnE+F81U#q|OYuuSAyaCdLP} zl{W2sWfRAjr};O^tpkZke6Gq-BPy7lnuM2Hw*zmfi(3wFY>z za1%UuID_10Nh7mhP(|E!Z@QH5*W8BZfrKGtpYbWyZy-q&t9iCiFgydR=_(C8r~U7v z51)g;t9GAy!D*?lWz1714w)k!(UDYl1?hq?|MwF4|Kx28MKHsm7?WtK>KU(QtNRSo zR9f&hmMtUk;ww=s67r=7wOhM4>}xZzlc&!3Mx8~zv%CkK!lajO_M7!PqS1c5tt$ij z#`Y1n$`(CKD1J0_FYf;gEg_wF>+3PV+Uy+`x<0YIl74pgV5mn@Q>1oDzT)CT8zRLU zVHaU=k8PhP*vm8)6y2l?%cMQpH?W4T1J~^*S``^1o}j}~el!kGAL-p^4QS`QlWJ2Vdjdv+eS7qsT#_aXJY8fd81n=2GS5o+y!cWhy zTl+xU+g=LJ>bTMxKVlQ~EJZeny^Uzy(P<;Gegji1{o0X}x&Yc)-!i6&wF#UoT9erY zKw~WJv6*!(jc4s0jWbZp$tQ03oZMt%U=GiTjWfD;e_-_f-e`;%ImcW80PV`HXK^3Y zAkMvnA55q=OE3;lNN9<_gGuXYDsOc$Wy~%Bn^jVVVzTi2nJCir-R-rS5Bk=cRLg0j z-+xK8lhntBPMV_nYJuKSXPj{k;#L2yLI7t4&4f~#GDFd0$VH^tFWB;!rOVTryGn64 zxa;Ziu7b;9#NA=UU|(>>4=w#drRD=I-j3BO%7v?FT92Q(Q(U*k9CKq&GZA~c^%iUO z=7=t+o;uaIh3yaEhCe9G#COn&#?Q2Pm%NJ>mPAdQsxUkWaWFmQdD934Sh{ozS$s3z zVYfekWJBymVGkW#pzdb)7>;A>^_%1MA=i;Vr3Es{Ho>J$yHokT@$}4XEZ~+fu-Eve zr+P)Rf+atxOupl+#BzP`hn|9A%V>J$g>zS$uOwV0L=(eF=-*J+ZbV{7?k}dvTQ>Wo zMq4&~NG`CLr%vh!x}~~g`W}8s+JHOkfz8QW$k*tR3%Qe4qPFzP=qVqiHIOWNBv!Ry z+eI5teYWiseXLy18AM0g_1-J`6|E5QYeH6YDRBeWmLsWr#H(7>#%d_Ge-=-1aV9Lh zl6v~AF*$9lCrM_yU-ak0WKi_uze#?0A8xDdIQu&8d=;qJF@4`vNcWJy$*ca*>yIo3 zK$Aw0%+{0f0$Lx3)RoubD?7ZcK(zAwDRS7XP&QTXgk1mtbWayc+XbF=M-f52O;}g` z*^W+t3iZZuu69CCd*m5DT?a>~Q{oO?=B_)IX6)jdG_Qm4RS`91R*g_6i=8#a?Faw)~kksxqoq;1`?`y~TI;S^LRrsg=k=U27+(v5rE|$I`KC73R|67h)AI zVU=Pm(grWK0HD0a+!o3ew_OfpN$a0~NTH}=Y9`HBCT||tBX-H)y#yMXVDPTL2fIU( zSo*7I<(2;ljk3Jy3T`F#(1Gg0L@#-acL4)bO5t)g}rY?S+?+yjktoW{Kj_qYl zLEhwMpNdYjBx>Ln%L=NA@1A?ivlsK{lcvw-bY>y=p^Oxaxs7Jot>W{B?4lWmGRTCYX4noO8 zewRz~!k&C8wHC23$i;nW{(2*6y$;-#1`ABFG|ptVUEXQCikQkxmAPgr zv)i_^-92&6o-wi)1S(qh$=#v*zhZ}Ur50_k^1tlw8JsQG|0#UVBIQ-L5^{~p?T4F| z-8609nN>G#&?Rans)G(Rru4!`JSSb>m#`=*l-w%m>Wlb%Jt@xROBBze$WnoyQv5uN z4RQr1DloJ6CI-$p@UJxib#wVvUbf3Wo-cddIhsPsi1RS^d%{`nbNiAIy*6YAQj3HLmlBQ`+P$8Kj z_X+BZqEdCyV%7~t^}n9ByfltGF|BxR#!Sl*r}CoIEG#%pKU9F@;s>2yb)Qe<>TwfJ zRf6IS)0(;gT;Ru^riUKiZX`vI$<=~#S0+$nMblK&79orr*!!xtS;G;d=i-p>??>!= z)yRgZrp)7>ri`?lctPHsj}Jo7?YJra6Z*y8la=|OElr!mc#t?92Lr^bvm{F1NN&7UlKKI;Zx zC(djJ34nJ!smrcxh$vX%7sm+pI8T)*c74E!O0PxP+&QgW=NXW<#>>4}iCY4PAtJ7w z#F{LA3yZZ_8sKM>Tu7jMoDve^?#_qdM4>h#;)Bhm5=fqwl?iw8BEE=$E=U7zDy)9T zhnu73dKyr432YfezG5ml!^df(Lnteh=Gu~IiIK$P?F*1U9dds5|uOzfZ z%aRV7VQ~h5w}`z@tZtoSVCIw&VqgyF>Pf;Gg@%H5bgr8f%40W^J*E3^uPFk-T;=Ey z6f(NuvA?asQBur0>RT1h8MOTb%VHwTHc-NcmXv-Wr0(d`BiH+lyeuOuI73r3oGf{_ z$2G@yAkf}O3n%B;RV`BOT4z#6Ju49pmLJwOGDW977wH-{3;}q%B+^e?}*n zV5U(?j-uDqd3EBuz2xZab2@da8~)8{o_iMN>a-$Q!vUB-@5&wF=uN%a=#9zES!pMCN*SwAHNkxrlnc;j8m|Xi?P&%l#9XJ zAJo5&2dv6MRXGqd6ZdXnFyW*-i!Fxs0RZXMxtO@U#jv0S8%{zB@T<#9CWGbx5>S*_ zMMMGRlI8ZxQ{VP}_Oot2iZlMa4KR9Z^iG@U~s>6cbx4pyAIr5zr7G4qcgKt*`dNtq0GCYvz!-jjjH7C?@$CoEav zAa%M$?r!qkAzeJpGWJ2LvO+qp9vJL7G4cHS!96`m(nfOTC&tc>cI*j=lo{?!yeL|7 zW3;=(Sfp{n@*o|%pM@=&Ee;)i0)3$rSTc8QAzoVE?7CW@-W{6_hd^;=?TPmn!G?@30R&-YjY5Eb5^4Vo{(K(RDspP5ecLr``hj&>UL zlU1|1Yb5?s;I%^V!JgxTa=M0Q(eFHDnXm7{%&Qa%Z0AI z(k9+l4U)nF4SSBZ_Z^Xx_)!!S_-cxqe#uviqJVupK-tm8X?#%x^E>p;7>t^N*>kxH zwQlgb4=(ocqrpjU3Qm6h_WHH!0G)!45F7)N*W*4P{l2KCB*!9|s0?RYut}=$v(g4+ z(Bvl`e6~q5$mk-!pPZggvBOsZgj_`|^Jt&c!ACk3*8#arGp1vM##VNad>Rr-dggKw z1nNAbA#FVNBd-66L$8PMzPKukjZHnEeMx0qbHx&_H;uq&GZtw<;GJ5{ln;gh0~|&9 zAApLN;f-gF%Wim-zi}Q=v|`PX=VV5kNeq#T+0vhB3Dv>_=*~(W%bU*UcK`5Z^d|L7Ds9ZnD7r&4}1#Ab7 z3ld3X`m((hjQ~@IDtXawK(OSQI%oNyDl?VVJ-AdKC=KmPT0|~tf;JB2+r2J7Qs)GI zQL;&@_eFEC9n;W30Ik=;t+yiENG8ouX6L4$)ugLlW%q6*>0dpc(Cd0d{Py^5-Nok7 z1y||(E#b_W8CU#5&e0)V2jD`~caeTzwheE;nHc=o^-2C-AMgA7=nI*&w_1B7MXDT@ zVnGRguKnhD@!g!!)n`55-e#H=`nWIdYki4yBm~=rv;nJx=guNwN%shB!n}HUa*w^k zg67j5D}+s$*g4~l`y`n6J+8OQ*w9kj87U)HzT&&t*x1EA8$8tM_wo zY$ze{g0AyfF(k(cNm3MYF%09IKHmI->8;N9n_UuNF~krz+N;x<;)6XqVN;#Y2JL(J zz=Id9a!wpQ7%)LSJSq%TwM0*azPsXl<$N;hYOW9>At8e@OAHgTLbyB5WgR z&y@40#>9c=EWlWKl#ek=gfKhx5G$*@9!{qrCwM}Fi?$(F+kjV3ptQ_+(GNNhs59W* z(c;GI49C6OFc!S~Nw4(W9pn~xcYSA?fD!>@o+o4sw|Aol#zw0}P(esRblgvoh%zXQ zr;F569OpgptAMB8;ZD!Ki0cMVH7DttSLH4TmB%wIVx2wI1gD|;>>{H*nHNB%xExRHUDEeJPYCInh9Rx-N214YQ193yAp?OD2}sRTGD6Hb zLV_1hI}0U+w=~+hQl3NKU(Pl_4B1W*LVkunrHd5EUrojG%@LAdd|%Q^!{tQxvb;e<2`; zxWQpnXjfNzc#WM^B$Zf*<^x(@Fsp>*QYzOD>^KQ}q5&l6{Fh8UWsp)_6`sWPoHyn6 zD8jNVWY*lrI`PyWv?B({F{hSHsMgD>WbA#A0-4PH-!wO$e_gjdh(kIcpLCZ^yCl<@M zbL?f|i5ehw<8ua`GVU6lSoYqVfY=;t?~lGTCwqUYFr)zV9XLu2&*DH1Lrmrj1qP0% zFz1aO@39ox;VE>z$V3hGQUMg^zQGA7lkox&1^LN+I{RAg5X1mG;ZwF`t!=FDN5!gh zotQCPyUeTyEa?u|_#=U8n$maY8nm zmiSf@IT{m3dmAKn7qDAS4SXV#FkqIr zS_n##uO{!vcrp@}FFCP5XbYj>r^%`IBtR&=qmXJgVW8%61mE5F_OY(iJ}ao&j#i#d zwT-I)k&<5f{zq;*iho!B`hs>qycpz@Y6n^5n}?2iATc^*yahJ_D5_N243pnu6`AHG z=vk?%^9?~)gkYGf5SAz^q51{(-QmXyXmGH6V9Z{ujX5LKOBdLs zbhN!`)3fw(?&m9_EIrX7P#%Gi;L3W{mp*cEX7EzQ3eW)1chtEES~sw(_s@U@f5Nl? zOcpf0)en5@UIjX__Wc&%fyZny-o_(Ura?DL1(=Riq+|>54xWKXu<-!i*%=T8H2MAf ze10OadE7dJMb2qm8BcH)I%(6+Qxp3H%nA0=OJ| zJ-~JjKGzX@?-BZPst8+m_mCuDx))@Be_v1A#mWjNs{bn;~h#(8aT z0)zN?mBW_cDO~zxPv3^l(2B#e33dNYUrtGs6qv(h3qo_dV=S2;GX~&LF`ntl`!AN= zu2)SR9&BMP<0+0m%t+i72$*D2iXRpk`}PRJ9^k8(y4{__yR{N9JfI%FeWqjOhd7vC zBodB5Q9?*l$I(j0!H)K~8FF{m#fTv{dR+(ZjGX>3*|Y8+Ut(L)3Wg@)i~ec=G|zCK z;^E#|s<3V#Jx^(+!PxF^Aa`7VAH0H}rEH`#fAu(v4&Ls4rv=QvGTH5rY8tG22gc#q z8k>1uGA`}C1cC`(@C4T9VEE7Jum@Q(D=&U_$y`;vq5#oMFZ)5JdrSG+o1U}c;`{fK z(?c{J>wb{A=eeYgH#TM-f@lYreo^LDzgVC*0s6!IIvxW2U@u+H+%RM-86L2H6I`(r zH07j%4+7rX(FclIAE!z18t)VaKw?kgvj%LLL)3Q5Djj$a4U}da*n%qyt5@1y&%0|| zb<=*uL^IpY^e8J6LWe1a&(9m55q@#j)jXvtoc%`frZpz``LCOVny2aqzQI{Q`Qj=V zB;^WF>Z&q9Q?Vqt@SmlQ&;q_|E?w$*7bEYoplKr3auB$7s($6i`Ztm$&rv>ZpFS?X z_2F&Z0qCoB#CK8k@4ULhMu3@xCmhzT>z`;U!|rv1u)&oNgo8i7!leg$?1^6)^ART- zwsdJsBgL7$Eo(#{x!J3&2h|ltQh@?}$@iek!VOHF)pJV+=)4VE9J!=GlZrsVjNn4h zXY4O2B4UNdz6T=Yg9`nx`uFH?x|0Fa`c8mhjbqz4=-rs~HOAQBO$Aa6@`D-RdQ}fR z-|HqMJ(EyE7#E8C2yAWE-6%beREtZ;pV#xfOqabgPu=ld0g1V$0-!Pn2cD&?FvYn=pVY`%ZGw+u-)sqQ|cIOD*{Kp#wV;8B`PbF9pBe zIR+*Nx`B}hLal|2$9ifZ$Z@j5f*qg)vjB2=H z7*siEZ%pB#?;VHX%->5iMif2>xUof^Szawcxd@dfENr3BwW5Yj(HPOB=cD}RF8FqyL6^OyQ0;cUy9;8B5G;G#9l2(D?el*oC9`J6|oBNlho$Jg&Pg; z^hKoYb%MYusUmZxv+`fH0C;(A=fIVmka@ayAGCsq=NT?fC;`x9-S=Y^w##6|Ou5tB zf^mT8XZeCGualOS7K5L+jvxCYx0v_9)yk^H$o%GrI>9H+3x59)(9 zN6Lam035=6C3U(dkh)_w)i5E%xwIb)U0!@$L--vJLylKCwO#zhW1rqU5Kxa|Q^vi3 zo;+u22f9nAma`+yR^o#=gB=a_+`*zii~e|NWru&Rzd8aG)@9zn2`hdth-hZ0UGE42dg-CN2Ja@w2ZA zKP9f~dLa8`aky zhy+5+sa{uR@dwjZ`SsIQ@sy>?4>;&kUF=EI#=rD_Yny#_(G|5PV+Z~3Ui`0XZ5sX9 z+a896&1IOl9J8Z2K>zC$6qoC4n4sw`nGU6CBQZCTH-jE0w*OkhpJyRyV7vo?Ay{+f z8_Sn_w3V)R@!Ls>c5!FBlI)C1p;J>WT{k1CDz8aaQ~vAi$H#q+K1HxXtBVg_bDQgF z9%6qj*cbWFR5Gcr(xp{u@;0boKzaC_ zhxiWPVX>AZs;O6P%)3M;%^|tqiuC}_Dv+W7T-kqEd(12?V^K+{D!58@d56^KAYIKR z!|JNGq{PvwD6%_nMiAT5W>@|j5B}dDHUP&wxAzIbHZo+~8N+eKC{r7~i*&Kou^j9X zdg_qu*u3!}GinUsYj82*A5aDyOH7E*EUUGB%1Bv0!(qJdOg*|WeC+87%2K;=%Dq80 zN#3Q4_GbS`GVasX#XX0zCyx<|Ra9%+(! zGaa*C=fj7`$H)JJ&ZGw+;l$)UKsw~cCVD;^_Xi~ylLLLx-=jTCw$Gaj+PTxm>HJub zD?300e7`*x`$s}(14sP|pW^veUkT}v5GP~!VFf>-&YK;~syo28VL47!&W4YyBL0Jz zzY%TRO$hmLmVvO$Y8&t4{x^wZ(<B z&I_p85a}6=7!j{BQr}=4g4P+3;mCYkN!tB&RA-`gr|$BDFgixge8)eb`@bRW-#3Oa zIYjJi&r=E-fD(10S34)f$kjmnK?qw^V_DEs&dz$_+;aJJLo=9qixb1r7s$eAvKo>DwM8`Nt68(HHeQ{4S>R`OcWn+ zNio<1?-Np|{-#I$-*o{2Mvv@+p5jFlI#vGGayS#YDjGXT2qKaYt*U=?V!_?95`>yi z@3829cErICVnAT*HB7MgZw&9ha~~Y~E=|A-6NiU?uV_8EyQ0h^L^)1K+nKl}lL14PdLcN6|w!}!0O z@IMRF|DJ^Z@kIZBQb*`=Fo?>zS{mG)lCC|LIdb@3Q(&t111FZ}@6h8D4|vV=35$G} z)ok)C%b|9i>R-Bl{XW6eD}RkFmm}X33{eIFcE8#UuFn7qH6{q;3Mo@QAN?8(B|T7B z-t-EOF8!kxy|j?Q&z|>B3myc`{gi)ZpxgPE0_2s!lr(=yKEwLzkE!6~25M9Ei5=+= z5k4lKIDEtjk?uqVj!UDi*x-VUXW+o75;uUh7O-i z@WZT%@S$-Mq<{v*W9ZQ4meaEo16OZ~-|tHhfS4$TfBq?N{PwcLU+XKR#glXe#-;qH zCI;lKKcAkq)|Ya+aSqS_MvjEqX5EIykBKu~e_l3A`+A2>B5S4HgyLT*YK#}%>z&&c z&mBci|8~=nyHHR7qR=oV?w6V*hDyV;FTvn9qq_Md>;G7h*C&26>QV7xf%spHGK@sN zoBGKgen0z<^;6{pOrIxy`6@37Gjs-U0h0k&e?Rqj(n6>;f9@>N%D*{&%HiCA z!SBTY^9r_M#cw{R-SH3Smgx~yO1s}|`afCgcOjjq0gsV7WO>UgZ{39-k1d@1y;TTf zXk`$r_si|lg7m(>Y%q!qViMe6Osjd9{I|3)CIU;DbP7iE;3#hhq})pr;TD|bb6cie z_1T{nJ_mcQ?5Hqxfao<$f!Z;{^cn1o;niqdnf;@Z7=^naQzmA44d3* ze?-9yydN(L&(qvx|Eh{b-T@mSUv`m~iT^hftWhR?WGrUwIS71jRFY~pl z`{SMCr2lZ-^BqL~uR1RfYQu|xkm+A_A_EG9pfkN+XjA)t58uyU|NQXT4}SoV%^5CGNk_ICm={^4n$@k1Kj0<-O>=5pFW8xwq4mlHibE`YX2cLQHwvnF zALv%Geqq}%q!f&yHQqanXfy=LbYxCWR^3WX@;W6j{$aNL-qYsbwL)8CoX6hwTJG6S zLcoU&Sa=X81G6;y@(y_KlV=5to*OXmTK^k;kw10uuXPFrul>u2I7P<;)?Z$5L14Me1fN09-@zU#Jz z%B?*+ySr|0?zg&1;kv%(DF(VHpM61 z4q`I*Bm=rqwzuDRPu+8rNj?H;0h?C)Ax6JW1Zn3&aUQW?wvQcrah;6eP5k;p2IB~% z=Mf7^hXMc#Mm5Jjw6us_tF+TFJgqc~0a%1oOpo=~z;FN&|H~`7Sh449VyiSs(*P=+ zYfuXy62`Q_K%@^v1zFxn9GLR`sigXxC@8Xc>z2(~wDg3LgDggOxi! zV7yC$XDy_XR^1GjHSn?o8W2o-o$ z#u{^AUMtuXh(4E)U^SGNjiowy?yHNPe5<uE@;W^U1`6yC5&i{E&@pQg09d`wd)% znEXnV4xWl)o%xxYUL5M8ggcM_sLsNBR?U?8gc`6@N48+=mjW0cAP11bP6xkV^`%+@ zF>}XcaNJC}I@W+Ns^grGNGuV7873k}_S8>&NflMbu>fs6kuCXB26}IZ`0Lra&KfWh z_o~53NEGGzA-)N%kq3YSl6m4YYoM1d`eRPa#rhlSF0>2hF(rHI3oCLP905V={`nq( zFbST7WLC1Wu_9J$Y7kU@hZn1fz^4ARoA+664(nC0d|F9+_a#;wfw}|qh?DgbK=xVz4yOz* zZ|=cI-u34-Vnj?y!oAf1fg#9u(8?biP0fl2+w`)VfJ(bYxFaiMnf`TG{I|P zVQ)NxThM|q)c@#Eej+=`MOqJL4qkh4OT+tO6+trqr%TCleLi2nef@)+S8M1U0LRDO z+6tz}JPN>}Eez4Ru8e_NO?q{^O)8YCEi&PYRKPeyo6@YQ+D zOxN6BPtYgV;Ps@0`fK4vgFcm1^;Lq2G^qcwnriOH zC#+0QSj$UO*Na433ou9xaCfQLu)p{^a-$eH34Q3%G0cK-I6=(K37V zBM~x$d+NVCC@7DP$`z$@UMAFGyj|wtH|u&a8UR?L7OR65^Sk;B8VGw=c`~K~fT>F~ z*2U>v)Bo~I(mn=1D>7G`M79I1Hz3*-BI0Rtjsk+O*@#D$?Z3v2S*5WAuAe#*fKS(B z2XlYWFVwJlhW}E~Kd9+|{nWy}jTp!UXRLxSc=VI6{D3%YJ+#F!sBI(a3rAxrODP$~ zhPEZXt>57B)X!+qT_Bl@;Qx8A5{(e3D|lb#ksqF)JNZ=JcQsFGKf2&F_J;hRI4RT> zw5v!4?bR(D?(g5rjUg?)5Gv;htV03AXU|$kJ!G z{UsDhO%bm7NP-h$gst5`7wF6k^mv)3!LwE7}S}!vRvb(t48^6_~*hr2gwp z-O>|XxX|{#X{BA!#>U2_h^4?~ZfASASREydBkGhXlXxd^)|JVUTH^!e1s~pcbJ#~DE)puc!GYh74KdPtW=x?X z=^?bW`d&yZIj0fIh>#4z2~7YU=YCfU2phk*k;n!u>PMY%E^vjxiC&YCJXA%)Gwy^! z6pLE{zPy`6VB7ThIdP%97p{)fUh4adT~J<67HhJ}G-b5_wCfXC z#!xcXt4j!xn2;5P3%m;NB)xE!`JM6Jb`zS!oWeO{{m=Tx4Cnp*BzV(@BVmiTZQK&P=6(an_?(1 zdM1Ll<&L0IDOR79{GAppZc^I5OS3#r&kM<$j^^=5-QC&Ay|IUD?q(klEMMvKbW;}` zDDe)n6jBN_47Ph3Nku}!Sz1U2hM49BgbkRX$&v=+V?M0_5kl|n6@CkKn8wob=CU%8 z8OjB5(yvO8gNn$=pB$D4!0{Z?;8EjZ0J-3W4#&4U4IPI8Sql593)k5WO8EwJY6rUaj+VN;Dc})RMB0OMgCVz6sV7=-IF~i^rhu@G3m(Ua^sh-Ly^x-ku zV^S@PhiO(jvv0nHDBilFr)0*uf55vN z>LUrtc)&Hd>#LfM!mm#SXod-}klj7U;C2sg=v{i=V%EgM3T(}s+EqF5hc_U-dy$8< zJ7c!-=gBhBPW$F_!AT;-4Cf$E<8Lb_g5{z_2({2hc!#ZPiJ1Ob(QlhE zK)BdY_KIRI@1hc?kqvAv3a$up{RPV}x9=yp4_1Ix^1k_jW|YDI(aWvG3_-DaJ%9WH zn|?O*TLnJic@z2h-qZaOmjl*sI@M=_nwD1JV59cEY(u!XTDB4=rlf&`25xEig{p7M zm+fQcv|*njkoTL7hu?E{*8K2|)op1vW_auL5Hq1@fv!#wdfNW*gT2B2_b!)WD)d8U zIUmQCf(KtpeqlvHf#@Ec`YfyIZ*AeQCBipvA90?C^a5J*97RhgaT(=pOD2N#Tv7`F zA?#i(SKmP{P{Nq`erjrz_Z;o$m{)gWS4TW6EAfxRjj|M*vS)ig1XOZD+wYyCS*&Vu z6bA7B8%yi&!O-@JuVr!?X52a%jQcx7TEdbOUZbNFUmq)IbGf6kfSWf(dto6kaBP2f zO4xUpM&~Z_Bap?kew0AaCoRjJoWMmb%gTH$Que9Yk`7<59N|gX$Jer0{VY?~%dP@B zUEBu#0E5fq+xOmNpW5T4*Q`E-m3y=a{frdDX&6pxPZ0yBh++Rpb1V?svB5951*X)% zuS&H{1x{jKY}2X5e&J|0Ra`#_VGm*D+nPFmz)fd#0b0q9YpJsl725~+TQeIkP-`Un zywsvW0k^S9BXObOm1OiCQZ@)rTg}~-G@ZKhOZO;l)z@xpC5!b$p&$2~$Sn$gxrj4{ zMt?ifWzGzOU*kq92`Rrfl@&F;^nQ`~z0WuL8XC45<~YMh8qeIrEwY~4`i*!8MdiEJ zj^=PonNxQIg}k#9Wo!E79wJs_?uybhkY79@tWJ_@Pj?bQsR@-+7crx$3DG1tK>5a? z7V)=@ISH9$1030%Ixob5_I*chT*OX@mL2)5 ziuG9$xN7nKoAkizkSALgTfGEIOzD_oYHvIrX?8JPbhuR_%)i?0=yCzhUPoEcTRPZx z+G>9D;t^;hP_ zuZm{8VGMVHacnY@4ayVIN{#y*LJ$OQuwiKRQ{>=!8OqAH4KLW00e7g@_)k_$Lw7>y zTjHnH$eY3!gQx)qS2<*^-u2F&{=Ijfyw{%BO9_d5C+*uurY<%4^c5Dw3ZSR;H@DBL zyglM619G+v(P~hjqi#l<)**rJN>X>6?#LUhzQ3}Ua=4}*!5bnW(@?c9z3ZK)D zRe>&UrX%1#g$him3&0BoY?Zds(FG;o@ zg+|&`co(VAg!SN0?Z~;8_;#}G#BA|6)}jfPmRoO+c$ZfZ=a@TjM7r}7VT=OR5$UIw zA|;<(#uHhz$ep zDVzgKLEI$G3JE)f+A!H5&U;G@ZQ#xdKNAz+UiFPk2I~y0QV-avyE*OgcD2Kfb!|sf zz=f~heaCe~Masj|DSV18fjF&u&eh%9 zggFqQm; zQ)MqIaf|cAciZ4!%PLt5Rl>FN>62;@eIGna1H$I$>L= zAY#9+C9fAc9+92%)MO`J@rdjLBQAOV(??`Gx;#Ai9DlU$%~v$yqg8R5LC^}NIHSiD zT>@Gd0p)?Isq0|;Z&c`}CdoA9n|o(zkY#n@^u?+K4ioGCQhFCZn8BeY{EJd-Nsvkz7yg@dLN0fQ(Z5-jawbK9?TCi(?liWW*dx}> zb9K&Zsvog#rqW*BoEv|7y#3yIK8M2#qgjC;`f@gz5vk-uJd{rSBiRJWe{zdM6hqHY zCsc=f5xNSXu?$wtm2NHX0l~~U2gW|h>Naen>uaH8eVp8@TBoT=XDIINXhh~0?-_h27@(Ub#hQIOwsjbjoL_a!Og`Y*%`cuZE zRoIjm`ePlye!RmwSJXyTtRJ?*4uc-RPdPf2(w~X}&}(0wgVUn|1{8;@2jB=^9F+NR zDtLVEVy_F@o`v|v^yv*)vSS8ICn?+UVY509>$&A3gU1qX-5bWFa;;#7lgtT7J}`Jp z3hmp8L&CR7QpNDB;+*HYbmIgkUH}a}#>GD+9g)T-%$HV-IAUdJ_anB)N5@>lPh-%G z7<@nR6I_Z+>r9;Qxt2>@H1n9cu7f!M(;cb#+(ahk%@RO4wWuVzi{VW%B5*Pi!d<9Fa^!6Lc)we9GQU6PxeXs#q7m@Sz+iD4b^0Bv7p6 zgoLurfVk@J5sNmBX&f%%RPmghu?RsvKKf8+l=cgVt%8q){^O(jao`wY&-WtDrbS-Fc+NSSkf@Sx3R}Er!+X~S3?(Vs$kEv1Yad4+!zqaL(o5Yki^a%m zkROx$P3~~TR+29#Nfyc{RP#1q`jMjp@fH-8H0`iDL6{*p&TYh!s~lIFXc%uh9Ff## zMC+j3lG989u4v6Jb*B&mz?+6msmYosW>63wNpc-MzL5lPkKUed`-Q*xuK-mZ<7N50 zl4QnOsAX!ZS=wIj{<>Nfn$`o_8Fv-$w?m0#j)EWUTe8BiA{K&bfAoDj2lq&1hH zp6Ls1Iojp$T|!7ExHL|y$(k!?(BNqcZy@;hkJNX7%53G`N*1u479-_N{qFO#p@c{% zWaXqI<3Ng))tmcVgYgizruJlJ#uuNZA56ChTjP)i;be2U+yT}YC~3WpqqF6T#-50& z(VaY@f`Xl*~a|54qAt^gZ>`J;SrvM&wHQ40iWUQrR=^D+foVATj|M@AL4me=67ni=!3<@UxD!L@`G%n+S|DJSKj^1} z9QZaQt20lx=QDg4N-ajlU|8Eqc9!$32bOp}Sdl(dj3d0pWWT9KI)EC4#`J;jph3uf z8Ht+(uRdW(K*!wVEaA~yo%35bXmCb&z3VE`=B4^gM5EtCetwx1$+6@erNzsOVu zE$|TKb9G=wv!o^)$anDZ>rQL0Xz}8_h>3`5#E9Tb3pb}Y2?j)awIhVVU|#=gIRGla zE{L0`QAiIX$RN3J)BG9s7bNQB-TrKF%fvsG51B^>Gt2<2nxzPtQAXem4Fh5b3grhL zqy8Fk%PLy}@W<52X`LYW4rSKg9m?t$;Aj#@+JlL1htGU@Q2GqMXF9b`TFbDrspjVB z%_YJ@c^}ceNak^7;JUkgK_-WGls9ztgc6)McN#PM6OqHtdCOWwG{=!rqXi13vXO?< z66)pLZTaq{{;}-VNYP^E`0_Y*eEHF@cefSBmvf&ej$Awcc4Os9WZZ!(k!%|cWS~E- z!V1i_ksL#uF{ABc@9KdZq_NG<&u=nxDQlhI)(~gnWmIYE;A z^X)E@5bCH#9@51}$16%Wkq9sL5ITi{zy{(N2$BuhQ8tij4lbh^Ga(2uEslkcfd8=y zt_}P`v*hJ-GblL%DR*DdFqGVV|@@d^j!d zXOLosPJmwoPtv=hB8phYa|nZa#mb!wZfdS#<`#p~d9T1E$sU&xg`FdI0W{@bD-@ zqjf@j@)n*XQC!5eg5{jf$e^U-l?C7C6zdDYFeIk{>54L~By`%7KVYZVN0C7{Z`QE9 zFy1+Y6JL&HRi7rMaHWxOd{h~ZNw-Gsy3w$2A5xN(sxwQvRv#L!hIUQs-y(}x<#Hr} z5}&RUD`-%8winv>6iEz+wDXHN^qskZ2X*LPdjG7<4*)_k&+9v>oxYQ)O~=cRC*nz8 zsAW&irF%VDhkFj`x?lP-dqMra>FR|yhy$=AYh@gok;o5#@L3DxBpS>%1sD<29LLKB z@dSiN-KNr0fX)hoy_u3_AH$t}WEoh~%UBf{73$4*^Er{NX|rMx1cVEPL}l3^Tme|Db}dz(A`(+z83RU@^x0bJ*3z!a~g7LCB!DglS~q( zJQ&w@@Gd(9)@A|rd-KY0A@LbGpOT`&!g6#SDCzHwq~kbUbHVPcE?Cd4cNF;-5gLGU zjVI^2qLCNyl$!`Akz+W?2I4r2YIy}9YYc$pPc2}QuX}};2T$)!)m03+zi-NTSz8T` z%y;2noC(R^C*eB-Pp)*I_H+6i3NonUKO-JM;7QLYM0i`!UYbxi8||o7SpBM_+E=)Z z90WED||~1e4ek)>AcT*U*Gfn2RoxcG-0u5+JRY67(tx>;$h@2_ekAecv(=8` ziYUsp%UM^h7I@@aH;=cb-iJ*L&2RTFYuAIMJ3$d}r2!2HdJKV)JqhKJ>+l+!;N1j( zPXmbgHNgHqq26BoRyi7aWpG- zO6adKvFHnCu|0cm@ZR(tDjH7Oh>p;7&26BbBqS{~I{UXx1DR7%KpoDiw>ZOD$1HgZ z)}kl#YoiL^60hZc&qgh|8B5TrC-f9KVoC%7!&%vgV8dL-4}@@EqrkqTVOaJBvz)cf z8l|nP7n`IUI-cYPY))AN@=k)D+XdhAx>TN>AZA&MHcA9h&;e!)u7T1Ete4GKJ`gky zoqlC8O$Tjy&l^c|P@9NY*~{z8C1>FpPd>k2Xv^{%@{?&c&54vneQ*W|B5i}m`=i>6 zl6=moj=3jIEP1IkW0ztt3~vA68BnOX6T_Z*_c7Is+Ee@B*Vyp$u*@twFsZ6oEqL^g zOco-`>+5Y*nM`7}?8YSelC=wL1yP1Y1f4MwA?@15OP4Ss8OQxa3<-SZhojP}tYS>8 zc>^|FedgERxndRl)}AD7Exjnb z2B%fOFU|B8f0&mYzjG*5%(Krmp`jVQ$qMb0JRh;{)!Kt*E|KjY&RdfVW1zKAJZK zSki_o6c!9FB~oNx+xzVUQ7d%(uXu#!kA7X)bteHPZ2Bd?U^va@50ecCv-P;uFZYj6 zdyWH3(8;bX(D?1C3cQ?cNLN|E^gttt2jtlum)ks7FDS0^%Zvg(`vkzEEaz4=lZ^a9 zgSC5hVfZ?7?V~y9yYc+z=awu&c)8Z_o^QADt`ITVZOYv)F-WGskACl=#aLc_<7$!m zaE0}V!duqkSBG*3KV@}?3OTn}JxYbhci*o*-aMGle$RanxQx5xVFoE**i}eDoJy-? zl=u28&)I=dgcmz|KbLO+7^g!+TyilQ#lmHYD0<4C81!FvY%6r=G*^;p0BUXxbv!=p zv9b3=>L}1LT~&+_+CK*JU4=Tf^XWKr~T{NkBFLFaTBuR=pLo6=Jq zL$+3%#yBfP$t%(i=hSDre)xw;p#a@m1RFOEU^1aHtF{>7p<%#mWiHMhFz#^3E$@JE zF8kGWQm_B?ma*p4F>bl{mtjSz5l#s0rx7>`ZgTeCaxlx1!b+mg_j5unI5KK&n#4(p zWPeNQ&M=Ns@bN(R=h=!4PSfqc z#2Fq`4x)*x^BR74o9qO%^9d(6=BgWCB>CI3bBlpuwt4)3=*{`x_vtNsHeyg({dj>m zcw?%g@hKqFRGoe7Rc|A#PYhJrTic*392`Qo)j%s7#49s?rF>=;1bSl;<;&41ebb#y z*|DglxoQEWF6@J3PR0V~O8-s5)=W>K#!eOOIU?%j5@i|@;pB2AemKw-=y6NQjTT*d z32Qf_W@VN7i9oY;j4JN#+4^l;PMX3_$X7M4?xe?jd+w}J{4H{gx$)g|D-P3u%^OC0 zVPaZCQxWgFG^?u&Y*uJP6ek)Dwr-skEv4BFC{t&UwYjq^1SDJJ2YTQ(L!~DY| z;RPNk^C`8Q)U@J|5xid?a3Q0HbtipR5s*8A;9VEjjErS@hQh3PO^nbt64Nbytk$vN z8I*S1DdtZVMoj3yjg!(esRtnBbNy(zbWmFjrgmd|HzEA?bECcF0H(5{>dZ{(Zu%1S zY{Sn_)ur!eb#qYf;U%@4CJt_lO5^TSUGUb^+p$xfkru>8rO$`3^5{}N#jI~_uG3ex zF;hF0+Ok};-6Rxm1QF?$JW>M&h2P*;%TlQ09&5>d9DjHryJkEz8xkOA?UkCzS+(FT zr*jPDoXJ$EY^Cf-(XJsxmR;lE%8oPOIA7%-7o6Z`e9~fmPlKzEOUyiwXT?>Bm5eGl zv~l7PTo3jA;E1(vsTwh9eTEh_p;D6}QId`ekih8{ja5r_`ow<4TtJr3*K>~~Xc#P^ z5V*TszKa#7J@m}ldcnq|b$QvB9y4B6Qc<}*M6bjUxYTc)6lPqd+?=Q|BZ1lt=G3=? z0UMqPlaxc`4x>WstArj>y)cV9gC%qC`si$H$WO?xnP@TWRHg5=IC)7cfHk)obXj`b zq1#@)L%KFODBiv4TmY;fYC) za=x%JNQ+WDUMl1B#0iu1Xq<+@GDAspVAYC5TL5k0!S zxAeLW!35|y8}&R&*PRZbqKT#b(h{0p-ckMieDG|}blkA{Sb`_3=7x@@-}2UlJXW;9IdmLbxp6Ziw};5GU{z;kHqZ1vnMcUqDhD0(WiN@x?&;c( zU>3>K$5^({#T#KE zZTysgwff`3ny>Te1ns;X=r{y7S89}4qYdU>Sg|IJfmW@Lm{oqXbBpWNtID;{xx`}e zwkTx>*Fl$R7LUiMOR&Zi*PWHR@N8$64%@9bUM%0`ib!Z}N-?BN5k5UT)1y%i47k&; z+0@T5;2;nGz0xU%GiZbHeFo1eq=VDD<#WnA)~Y6=C0Z!Sr$gd&o~w5IZJG!BE;-7+ zNXl)>s1Sb2&iq1v!?@KA1#sFJlfCt9iE!8NZUUX4N$UxJv7tCA2SU|t-N*{#b)o%u z2^JwChC}i)q(LP>%X;i&hF$)HDBNG837o-pA9blHRS%4ZQ!Yl-Mbsj>d-r6MvTx&bAD~By9q$8F$>#XbvxdUh}QVUhE^3K-M zKSbr82~yOCc`zh;T^YO%Wm=jC6#Ukf9<17msB0=`N8UmLb-E{J{Xii8Fw#qZqwQ+G zt(KhbcpmS)Zv>&^}=eMNumFAJpK`4ZF>u9p?4!VLA zvZdaF+%$KQLg=z4Ek?!t-rJb8g3r~2@=eu1WlNmL1MCo+Ho{Ay>LB`Cn`7^X_&_Qu zH0hj*^2OqQ>llgj3$5ju(nmJeDL}~~0m-F$N}FByX=)SJy22}M8|lj z1TW~DdZmikr5SkjSqZu{{BZPNPYSEh^jg`BpIU6)mBxg-3CtOdKvjGt*XKI`^TnZ< zd8&X@H~TJb0OU9cu{M+K8IKkkp~y5#d&Kxb>n_I~4Y`Obc*amF8sA*I_dZo@I8g6A zj$ClFk34Lj7Ze@kR^vdS*_@8pF5g1Z7zvxE^FE)Uto+g#K9{vBA2G+6vcm8l4~q~L zh2Mo%djk%)pF=HAxnFfU2afO3U3Fjwn_BX`ND zM^)WBscF$_fq|8lT4_|`o}6)CiuH3%DQ5!)jn|97_4K;KLKqJ6N)k88BSY*l_ohr6 z9G_PL3d=NT2Bk~TuY>~k&F-Cq7}qx1nW1;2%rb&^d_AArY7Zg&c$;^S3z~)j8%PD) zLdMu6G770?A??Yx$`JuE(C6MK4w}Gj$IvA$)F0pa`Blj{Dl+P!>V*#smPgQ5f!Bbm zCGFX>cZn-RCQ&gs>~0XHdXxDixwA}Rc2gED+5CFEh0}wvFL|5tqZPvjO0Vl#m_hb$-JY%^ zR5K~klHaT-XR!rz)3IJ~QGUil$D!W4(`Y;utS>zF z8Qzb`BH5c9zpJi!6cQ>%Ydwf?*XZ&ve+>u~jQ~GBPkHAhTudAxNN@)`+?qM(HRTE{ zC!;N=*))BIx%!a(4X5JOe5|eJ4DdhlmMe z3vmB9^~tQ4eeW;B(9xx63>O}NffI25z-iNE@|+fP%%rq^8cHm3hMCWd)cOY-8ONT2 zkG8VYP;B*p5942V4s3;_Zh>~g&hwJDNbK`e_jePJD@!m&xPSa3n^+v(O(1>U453UO zF{v_HF&yxo5-EEh)QW7vSZZIsY#k8V8Z*^5c|S@^c7lI#Z7TOg_KMGJc_)(Skc$XN z@(}k{E@Jl~#6qyzTr@gFmZkCvSgO=&rDODriR}v^-*3|rS=7I74Ikb0$9N)xTB{le zWo1Fvz93b0SU|~NR-J#duiAzRT|&gD=)6#g63a6jq9i%#EyJyLoDz`S=!R}iX68YL+h%8H*R)T%lC#uijlo~|KrXwOc=Z32vrj2;qH4j#O8}yNjnpXWX>1-IU zs&lKb&>c;`(8}|H);qP$`|6^EcN~H$h|e#+>5)SodHmD!M)3F!_32om=YUrY#m~QW zHv^-8TeY{qOaM}jl&MVA!wNKL%hW&a^T2vmC&|RrG%+GfX+5YH&5E0g;cjJCnKavs zoEp0wQJ!4l0xJ$iXg3xu(1ni~p53>#kvfCWIh6qE8c`U0H+9s7U4I)&TXSk63)lXq z)9CbEd?Ynvg4{;YvjOa9aXN%eZWZ`wW-vQXhp@8TOD;Za1``)4&2{wX(FYyw9(J|i z2PiY@3RhY%(cVKt5lX^D|IS=9CAqdB4dBxWJ_+nLjgVV8A@|A){Fi}snunl2^bU+^ z-v+l-*ANugl7s65%fzqj*yOj8sRbw>cK%@+u0AbVO#AZpuQJdX(iY7y7RN$z5aLnj zLj=MfM)w<3`unM)hC})x7jR?2wGd7ky45yLK3ANFa{7cx>GfAZ%#3d^@;61h2?bwLhT?Vx_*~y_J>y^gAH^s6Ja;EvLAWkMi&O-G_Lu{p7!r!7fWUt|U z-=ibTz+)errj#D)f>6xBt~vx!TnwQL%RW*${_H-1^hCE*ddh7E87=LS(S}$H8ydIz z#dZ&6#wJ*6N@r&3Z)`HMri%!5Iro>|z?rwCCMx*!a6XKDr?I!hkG*SnZN^xW$-UU+ z@}J#->Kxhtl@TBVB01CEc{zM|52Rb&%}TVGZ-x>`#q^iApWlflX#3u%wHM8be+BZ*;~M zIresRnZh2n)7yDf31H+YsF|d`Z7tox-ZcD;P0ddAw_S%++r1ME6^DW8E_?oR7Hg|_ z&(RQ{`bAJ35(7YpaI4%c;IE^m{USmN>7JBJyO6|N&^^fp3^y`zhpJ$InEP0-J1nr? zZu@saF%=@vKWh$cgD7Or87QN2Bd_@a4Wp{_W=X2=JFB-qS3}6V#k&xq*Ix0qw6v$` zprn0!I@g#siL_P(#94KyHkd(?t{OI6%kdv2%Ax))x8Uf?&+Wv+WuCLZosFytK@)0l z2Yt&SmkN$Oi!L{WYYZ%6eq){PJ4C!vo6oLs1J_^@?fS$>FxX1pR#xD7k(ek^$baez z96FnvT94^##E6RY*A3VG&7=jz!KCkHohsp`^`)j`wk;il@YI#@w70;{0(bx~tf!?` zntC|ebIrA!re{%=bROta+~4v5H9=&4H}>iDo#nxR!O>iLZXVg|d3y2=*cafCy)v`v z>eScMZV$-70?9myPz^?h*?xX`X<%VU<>yqd?$I_U!#oj^zD3ZqBYSuOz92#`-qi9q ztU`oO{~7(_prhT)ll*71}#b`znO%4`ysR&XzB0ovd6l{P9WM zaP)w@(IaaaZL_S@J?y^w26DcBxfUlaqxCD1Fkte99aM_MZN(Q8i>m9CnMTAJ|L~Ie z!`%KfJlG^N4{m+woU`|rCEAKn)3F1_pqtfN*zRtkbz@v=qgj5tJS1@*Mv9)8h^3LKHzX}Iy}Z20 zb?@)~ZF2tkK(~EZ;Ji8A^fLhd{1>iFf@JRj9A7wnmaPQ3#=MDv^0I_sfPMXL4ThfP zJXk^g1PpoQoG0D*6aUkY|9W)-dGaWS%KTsMho33y%By#XV3#%Ipf^lax3@MM!FgH6 zN!sa-RZRb<4gdA3^Vv$f5d6{^vv-*&=!y|ucBHUjd!+w+Yz*bJ`S{Onj@p4 zILhhw!D-2VHRq4F?7=X4ziZ!z8&7|J9!~Z19JD*%CVr$oCjQ4%XS`N|Y^*)UKI!xvrAQ(rzN=GpU{Y%sSOih1&iU-DMoSK%2 zj_zO1``^|BthH+gAay(TGU(LbAMtO$63z{_8KI{i$^QR&;;(kYNgSGhzHd)L|LY+9 z?E`+`KMwxZ&LME-9|rL2VgLL+>|U5tIOG$g{WTN+HR=C&`+r0IkKthWUoq5kN(vW+ V0_1023sS+~xieZSIj78t{{xy&3vmDd literal 148937 zcmeFZbySt%wk{4!8fgp!2|*AkB^M0>(k)$zAl(b;7U>p{4nd@q?hz5gD^P#N&Oi#O)`%x6Ax&M!noNtyuf9v&JR8iA~gq#7C;4ka2I zWE}?!e1|L2E)x8Q?xZID46US}Y6A_87EM-C{DrIGpIO`ww|>;N?AyJ0`BDsB*qFMj z;E|A`cDOsO1Nx6sed2cuu>`^L3gqY!%B@{!jOLO$?UH1%zE49~aL_}Zdx|MQIKo)p zW3)5%+>5~P61mwYXrzc3KIw1da@lAym__b72@!J5j%hTP+Wt(_Gt!D51%Gg`AvmQPij1MUN#|$NBd@IS#EGi{HV4^WXa5XlpSVF*I}x;(zsz zUn+(SZ&yfI9Lc|W67^xeCN~iOdJ|A9TV@a|6Iaqt4ga?uXP_L!{+CllO@NjO?3m-? zqRGEr8F-v_(pTi)I5hAGKW?;jG}#>{`hV;3Zmgq!V{5@9U9!F=0jV`miht{I8Qx5y zf5*st#grfnl*{cuy05hyUVQ6cU&Y6Z^_cXktYZx7-^wZ~M*JKjaGsTulglyfO&D(S z^599H6TPE zc84Qjd%F7EThun%Fw-bm{r5 zm|_Co94m9)8kaQnIN4!ynRCm#NiFni4vSoy3t{Bvi)l!Ga;Zg2lZZOrbew4G1zh^T zYj>Jl{$*$6eNM~KM{YZ{Ykh1ytuNbyNe+H+c&paA>>3tX+GmjaRa#B-91e(bl$#AG zF;L&UFYw|`-$y!|sfv*BJIH~4m5$u2$$aJFe`mQ_zU(@p*1w*V8?-&`q@s^wRE&N4 zy5rr~heIU?6&52V7pMDWW&_kiE87{aiy<=B#bQ_Z(#;W;SNK69m6B+FP++^ndFCUU z*W3x(c1WEyGMY|80l%bX5$~*($eZ@Q01U?=EZYG7Glj30t$OA`*ljjW0%Ndu3lNhH>z+-Q{=85jyCir?XXTDDa-P zsqXGe;!^x%Rci7xLK=(SE0 z%NB$p;v3-hI+yo&N;3uHtq)X(kZ>CaU;lv)Ekw+=%8`;C65@}1cIh`MI3*+bSMTan+or+R2k9Wip=M;fc5|#|sSfY|OqNTp z_NTD~U=vkQ`(K)Q1(%h)ynAjm97 zh`wUqA@T{VF<~4{+NcW$V8h}R(vK4;c7D8Vx;0*W(8E$Hb9IRxVwSb~BV;&NaAknsgD@E{3w0{B8?8 zyVa&Kjt9(8>^7S&gVS1nd}{!^C1<#O zAD>D*kD_LymyY}(P3^|e{H&%&*KgXkUhz$TD)UI5a-!;|=Q{tFAFhzhE&G^-Wo6D| z_ZBjlUKg^|8I^dxQ?JaZl==u!X@iX%s0wz~qnSR>s4mXs28P`t3Jm+AJoj3c?I(l@ zBXw_khr24OD6kNm;JdwF+2}pAN$|stV4tmt-vwZtIUc}@pEbjeufvK_hyvDo0Z#(W zx#QC*!)7c${wzcEMR{zLMK+Lt1EG|byVg4QRD8R|h@P^s_Mirf;!E5scuX>{5*XI= zz}NQ&*l}$ts)F~|*hjmDZ+DBi^gn*D`3d2bp0p>DZ

-mY4{ve38UVH{_b3@8rNx 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

#NNA^g#4o$I^QmbkE^3%urPQ+<`Lw@p81 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�vyoeBuTcI4-eVIuc@!W3a3#pXb;Rk$>K`~ zy9r4Dd0)h2Q_aQe>b@&L0oIh*?2B)fvv6^GtK72v&qJNzoXbEB6KHG}7s!a=0|lR;QH zV6JLre4nR>3+30+x|?`*uUlIX%X$mi^VnBxFNX&cl>SO{c&pEZ;j?_*zH zjb0D%@4qygBa$>Aqz_X-;c7lulKi>(L5Rg+RKW0j@3Nq;;RUVcVKC}TePfPnOtYdS z!IKG&#zCg9y`8Jc*ZtA^UT-!oiJU)~e(oK ztv3EA>M-}0s*c;J#`Or|%0e_g2!L?@-=e85yhfGr)bdksHS@v%J?Vs`BJEC|GR6DDcZS|5EQzE)O1g?3*8iVcYA`hC=EM9Xg_8mZKbj!5mhRsk3{~Qy2jevs zlMevqo1f{})u}z0b`$ZWP&s8i)C|4Ub~sBALe4Qzzyor)`KXW6CH%X3F!=uPy)k@) z$l2EznP>3gqtAeQseCWzu2PwB?~N=WB%XZtd+u(}7}O=f%dtH7_)h`YQAZQCe8BmV zTj5BPZBS&%muYW#iH>T@XJ* z4xfKjWq-+v$%7MQRGQc>sn0OPmsEddQwPE?)MJ&5yhfl2EX1*f?<+s{m)_BYq-}Bp zJQw!bE2jRU)BAhiLNrq5uKzcv@^oq?C{SX0< z>|Ev{vBbPJW`DkUKeea?aFKBgz?**U9Gff>krVH^Y2AgBM~l8f9Qfwip^O5AYz5b? z5g6J#gODD&XcT}I`2fKc0Q!{=81kMg8=W@)vY3hmI>=n&iqjxFaMOQXx39j^pZ?D! ztJgRc?}=%nHZvbeI}&G^PG;K-@v#6Q_VDrm?xt#&(8}t$jsGSMDKCk{x)8an4^ z4qNSp!$f>LnapqNP37<3?F=(P0#egLKF7avbB5prQNT`Iz{3ArT6^ug{udRc~(}<;XkVu>%9!>WqWuz9Od>OeI}FYWtB}~Rry4(z(*w! z2_1`O1#VMi#;f^)r{IA&>P*1F`DgE84M>nG#`=*wAfALB-Lx+K!VefCg7U9U#Ik@Q zSss+r`O}}6S>U5Kl28a2{LVYC01Zk27{Y=>Oh=9V4s#LPBVPK}@oK5jv(M(N-%2F2 zt--bs>AyyWl1kWk9`37?=3>P~QAiu7=bp)B+JT7STS1rLPW%7I*n5X#-TwdM_NMDX z_PC6Y3XyqbUsfTcB4x{#9U(g-WRvf*z^Lakj6S)!uHeXNd5GHCA_1}DVtJ>c2>w)CrzV|TQo?-m<{YSf1 zrMD4SRSj6)9~OdVC#wk8t(yCcX~zmAg;*{mIN7&(8m^N2J<%fXwuO(mnilXR%NDJLql$oAnYq#Iw9^7+v)wKo$_iW{VaddhXqfA$GBtp5?{7GNET`wYGDsphA4bpxTYGNkaQ+G zC@L_W8?$>R_xIH~q*viWpvzZ1iEqdV@-UbudHP=|ew09j)Qwj52k8ihFd;*3{3l!$ zz)vK!3C2|4YGrea+J}~NlTZGAEVa^Q8x;>lOxVBkd5ZGK=oce9I-uAwZCgo~%pojn zpqm}w+-1l+OVUb$dF&It0zd3hVGhH3)%-@Ym58S5O!L zlT!d^-5UgWT0cjw6EWXnq$@djsCTRO>7mlij?+YkY6|mklN%Q&``=GHzEv(z+yFrg z`52aJnEvxHnzC;Sj`5x13ccr;sj~#5nev%}!l7qW{&RrD6P!Oq9}%$qkqQN`61Juy zvNfj!01SGijBL#n(f7_^qH_1@{2z|J1fBH>gQ6uujyt4bM3PE4;8%&YVV4u1I}q^o z<71&DAVH2bcFLd!o*U1{i13BcZM%nBfZ)|U{KUtCBnsz4bkM{1K7+by-lO_wucCgl z;mgnWf-ZItz>yC~G5i1)!(NZ)1mAh4)J`ft%I+mp4BiKVdKn24LNo~NmddBO{9D0a zFkTPg*mw#dum`ET?+{;hSTc0;d6*eOf=$0!!nb-knAkH=An%bco_tjO-SdBXhs-){ z%V6M~?6Vrchwu&NP`~A2Sp_D>SmJsIEs?Kc{nI@+q!Bs2qG9FxEXnx zC@3c2{2So>u`6K%33oP;v3vj>DF0=_z?VA1LoD(Wi8P=48v>*^_7)$L(ev1&4Wr6c&?qlX~EypWd( zI2FbEQ&5@aNb`@MN&ojiMC!H8&65AZTCO873cLzZe-)R9jP7?EfAQ8!@a3G-P`fI| z6A{ogx$61ue~Y)Pv_JUZ4Ye+T)hn&?aI^CpOaz#-8pwfXeemCBtr@>_3t2xOCcXq$ z3|?y^5t0#TQga=xW6l@)_)j8$5 zDiHf_&wi5SjPMayKx!PGBj-OcEc&N(qo#l;Ms6iRQ}q^1a-Z$S!G4y9kTA$T0PE=5 zat4wThR7i8zwFS54x1=j&{?=9LvA6O-_sTJ3$OU|BV!6_!59I$v+m}aKmzb z=VT9;lum^5?1;?4=&XWNb7W@Q31~o#3QdJ#(k17h@Ih@Tjc-!TFzk*XJ<(-lIJ&HLDh1VldlDyH<|QMd4ZPDe z33qRk@8Dr&c?u16K8$oBqGi>+f>M7Rz`51R$BK;q$?frh-rgcV9dJuKa8V$_gb9rr zXhH<%hhZv1J{TRJj4U7$v`p&=p>V|3l#6=^_IUGkHrF<;Wu#Wd208>afjIn=Hi{Im z#_ZmZdp+rS$n2XhSlqcK;4nOA_2$8DWKsF@03q>#0rbGNJ2C|7>IV28-5)j}&?Ggd+-1Yziv5YF#~wF|I>TJ^Omr=S z^;;5b1^s$Hn`tr!{zgKWzJU#nX)m;*{twB_Is|4-YKPEoN#mY@Me4Ltd~bMHhk>~3 zh*?-3_z{;}^g-LP1NCRgn}B&5QKdaa)S#7_e>d-VULSipSL{D<^R_VvAK{|~ARjFt zgIkcCU=(S!Mir#DWNP=9&mqE)yILRq{`@FZF-v>58R$aYHfe{B(1}90@#Hg__`2}lwV~@V_L=OEdoIdFDbfB?{z z{shInSx%zo7v#O^fkjSw_W>8Hr8NaFH*1be=2svp@|GjdeNcb<7Xk+h)T^3C@fb6Z zte6)^P2P(jD#gXX_{8l%Ta=r2xk2c+k#&Zq@|yuI^q#M2S$wO^S2G;k_(Bg`4AXn zdDEc&2Sc%9y$o&;O{9cS2PNaW|F2g&e<&We@%3anjPquJnF>*$2+vgOVNpAYY18{( z_8%%#!=5eNldu+PLt1-6eTHF`?-#&g|^mu4t&oBdF<21GX-Z`&E;w~?*p4?xy ze&P3qM*h(GNZ!?f&-`Z?ni>8#Z-$#=W=S68eZvBRu*Ck{>56Nk(P}vBECPG{A+zi> zb{$A$%_Y-LXSi^i*RgseE(g_4D6I;0-y(t9`S;9XxfPhtm-F9!==bNos{|1Vl;2xC zD^Z(}&;AzV`gstYoe%Xt|CHQc1?(OdA<8PC2FZBk{_CjD}~Y<;U|S~ z5}0BFG(!|>Cq8zIEXJY@1xQxf@d>nv+j7rbL0_6{Lux*0_d?dE%nlSyuXg1i8BxXx z6C^g0E9SWLpA-gW(S3EJZ1=9C;_>9IBMp+SuYo{~Z!j*5oA7Zgb@A{sbG72IPlL1a2n)h#QANVK4#!JJEnn4d zK${d)$^JSDBX$nA&oi;SzA}>6x!9d864ORDxL4w$Dz6*&h$2T&nnV5CBW{)vBr+J3 z1U^e?CsKC6#y$e=^EG73;ZmzZSoM)HgvfZ55`=hbXzvHp$f%#ZuzAL39&|)wHiuw& zCzrs-=r=PF;9k_f~$xL2VmP z=n4*?LCZkIZ5c`DV{CoxaObEB@@jL$Gijuz@chiUB8oI{Pk>Lc z$ zjDkRJciM?o&mU!7mrf{h!)F1@Mx?P zu|@z@Bgx@46}bQSQu&p-=8C&;aoS#)&C4}9p5O0 zt6!4Pu)Rel17(wotfx1}gew?$wUO{;5TMCEe`gmiOBllG7Pga2kbH!%^lG^4xLf;~ zDaYbqUIGj7_~$DL{N+I_wDW#bnMN9?rsqtnPC}MN24*fo3#7X=V81M;KUvI`@K%4UW1GU~h?& zQ#%V&H(phSI>eTq<30*V^5pGT(3l|#jK#pYAM2j@Hbssgmg?`OC_xHX?BYAlcbT1o zR12Ol#ZZb+M!BV7wpB^D3IiGm(T1-y3^HjI(4^jg+9%9(Wv+<9Ty6977JI6b#336m zI#ray+^Jd`dY~%5y-hz}&*t?w-nj*WK;M+Qiu$3v9`HUar#+hu;fpF-wGZkS%L)VJ z3%_plOpj(biPTk><;zFg17j(+n;oqQ)RwaG)gNagrnsPBT(k@>>5;iw19!rhXufCy zpV|a%84V===S5$$8dbReMzrJ)o!NJ4n0#dsktL>dZh_wuFpuE|+?Teokm{}r*&7$= zN2dV3j`DL_7PC%83I@VwQskkpNnk!HAybbYobO|Gmf+dwaXOy=#J-X3e^+t{>AQtF z{L{LAdR|6oh#Mb`@0KUp5W+Te?7zv+!+LqMjVCoRXk_UWGJHBCl1tZp2=@3QS=@Zq zi&*M-PZdkf-(xX21_9e-42m1+*0XwOZ#+;nh4Wp8li4iUz$WQ}QT^bh#vtpNmbxAP zz3Mv^#TVlPkJv(j{ZGDUo#+F(prsmKIqO=)=HXCd!&zV=QC--FbzhuPiPKXhAZm_j z`>K8#zl6&PX0rVh(cl#$%K8l-fVbo#=6)Dspg`B;%a`jdF%WlS5GTxB+yGs-;6dt3 z_sm5uvd;?jr+nJibi+wNC;5%0VQIknE%Cp7#vaPe+O`+|Bqknae<96m zEcNcBzyA)1L#4{9U>pF%%NyoHyJrWj2qIFaSa_t{hoU+gO8|b~_Zk$p?zn^sy=e2| zyAgPI8q8C<+EUX*NBsRSpw+HKjZVRhe{4zBOVE;VUT(<(t*!wg56zL@@$z#XaMxaO zryO+Yk+yz^SUa$>Fa|o0z0>$b$v%{0n%O(cjMpTRWjyRUW+JX?@;xJ+;Ry2=)liiP zKmm>YXVKiDIPmNPxc=;GZh@rq=08Kov}q1J**=y6&WhqpvV{ewU2)^_IA`uNwe-#VLZL}n@o5Y3ZxA|RQLn3aP|$FPTvdUkatrtp`bxeA!bsfk^9~3P>vQGuYIi( ztBb=#ZAl6o#m2BR%WNBKNnqr~v~i=uM6V@PPAOuf35>@(5g8*L_2)~73*oF&%9vzQ z7&U7d-Kav3TKu1c%VP-34{#fe2-`i7qQ!Ie=xCF56F64*Cs4)J_fIB_*`GX&- z(cX{%pn1ffRUl+sEv3o}f<{0cZL(eFA1HXvt)XRnjCJw=HnzMqu4h)BrA=WTQl7-bR8nrAsQF!1fvN5^wc`4D27% zu#k}P7zb9Gn8Ar>&w6TA55+51g!2qhzbTouXc6)Sr*#R z=9rM2Y^67E$n!LeJE6jbY95&8u|8hsDdK5h5zEqYMv*lgQd8VE9*ILtf71%pOK&*# z%UrqJ%l));_6@dCV1BEo^s!wflPmzFDIe6eAIP4{nm30l-Efn0U9Z7Od@j+kB$Vci zyU22rs5)7G!?C=bBuCW-`p;Kt2xXFC&yn(2qhfR{zg;E6V)$-Iii<5d?#qaube5dKvjJUY$>NH;$OCS<_um zaRC>jP%5O2LFmknFeZ>=A)JeqicLyqXMNHnfgFK43@%Y`Wy&rv`QXMuP!7f1$I83t z<3-*4FD3~!j}lUe@aqtz`6SuZJ!URbnvQe|GhHQm0){$1HiJXAd+4soy-iWGmY*-+ zZKMHv?Z5rvOtg~fT|Qx-0KDlf({proQH6foDO?y&Ua`YjM{6QvPLUs;vR6BtH{_bk znApxH)^fWi>@Gm#B;6+qF*_!CeXY8X=dr`rY|=RDU+xaK<$gi2X@z2=Lr-8la&?tV zTQ_`(Eas}yOKw*=ki>S7d0q7XxlzRa!uPS;Gv}>43SeSQ;kM!^FN6j94#|e`%M5pm zI+;lhV^X*xn{XVkJ_(t2x3Sma%6~k(n&Ca4vi~Q|sZs-O%y&+p6j#{K!lnDk>zGsS0A|&o!@AO!~navvdtbI@!T|lMd3IB}M zaE0Kxq@$H94R~_1Uus)$1q}XLcvu5{S|X5CwUHo_Dl1ZaC4JWC7t*Z5iO)Z#jidbe ziJSKnS8Xgr_scxa=6{MR&}L9`;E2DtTs+`p@P}bf{Hgn^`v>%FeZa+RQ1! z!89FRmbF*;aih8`IJLezwVLWoNsejZ9I2ySODV|f9;J8 zOG3FYg?f(O9$|TCeb<~NkvXymJJWi6YR?p^!(e$`x7Pji+>lq2o6zd!fh1cgff2PE z1Kq4XqNywR0O#Nxtf|Ium=~DjG}|-^c)}x*m{xYt6WB325Ucw1bv~FsZh3t6K{|*3 zemyl8p&d84wToreXTwTOy<|F@lc24a(<3tTT%Q9FlNdiIHRY+Nxbq?wBQ3+R)Xp4R zG;a82zU;uRF2iLWu{%W6Yy$aX-$+?(_bp$^`mARpvG76R+hV}~M(whPcf5)|qWzZ);U==@=H>|rDl+R78>EapE$agUi+uZFTI+K-BS`}O zwHTO`8qIrZFd;CzCCXbTfim2|btd}z-NDK&9#^?|28T_(7__>Cf4}a1P&Pz*dw}%G zZnGW=HiuiK9*6d|em(`h@FY0PbaF+ei`A+8p|Gq zTH4HdBGiBKrKt72t5H|Q9K!J6h?_^^0CzW(-TrFOD&Py!0Hh)O=6E#obkaga$@4R` z%bt-RG~7wpzM#?(Kf=jy>$uxZ`F+WZi*bt?ca|4IHWJ@mXpI(2kU?u}MEq_D(iiDQZv?Khd@w$Yj=Wgi ztX^%!?B16cOR{5cAL2>mKTg%xeYQXh%3b)3dwsou_Gc(bB`Q>DL%U!ESPAz6bG*=K z0IOU`=oOVsu4hV6&GNQYYGr-?OX~chzb!oJ>^xvx#N(tBZ+(a}g%N!+f2@Es7=)F* zW__bJ$&uz=aZORKSq0L5{5<#?Pr)6dAhd8_=VkXB_Pt(zlEvUhG{NNUtE!u3CNm_Y zV2p1QCr}yfwDcru?Li#KASut)_#nkM=gT`RE4X7#7wAwR>D)});s*?@Od=M||D-a! z*>Dapu2&OEokuWW&4ZaOTFEHjm-@IbpR5_<+m)PY|a7sFjMZl<6}B)2xk2{ z!)6w>4g|f%G_N^R^E^O?ej5S0{W}&WU90H3~TC;pzz?zi~1zfK#QDYL5Of^ zl-Q)QYUn9>ZE*2*Q&>^p6f>Plpmlfv3|`Drn4W(v3Um#v)_RAzu9o^G^3F`Q>qR{6 z7vuR4RqAtON9{k>48qm*=zZ5h|JE}HjA!#g;F&LK9KhBIKGVV$6Rw}wGzPBPbIX6p z2}CPl;%a8+Z6GPbBI*3kUNy}Tdu|rc!16HsxYMJ?eMu_ktOh!EM^!JpxROJWRk+tM z_@88HZ!wlXQb}R|0jbRB#ITp6nq?yJ1OR%2VFq*n!s3O>fAI10DB)9<&@o7zkaq_o zGYdd&pmb8}y;b*6jKiNWfm$jLl|K8zU*d}%A3ZQSbZju?&UjI`WA@26GOEcebtGT;7Ht8 z8bjU77@$()zqVkN$Pa2hUi6}y4nVbsui6ucGUKQfG2{dht8@Q6LcW`MO3;Tqg>|HX z-ietQLiM+o_`w+Pj=k`xQgB-jv9?#qrmdi}qi-z~{L>>Q`MS5!sc>PAst7Yg#?j0O z7ai+^eBJ{&vJ4U;T7hu9!n>SKr%&JP%5y1Ge$hrCBch15r3K|-_Hnv8A>f7Q6CX9? z`ML*vHeLB-)p-*p4!ju&WGly`CC8D_;lC2tR9ch~(l$eWds6VvU;G9+%Ncd6dAXSs znL!+OaP3tVdHpt>+xg4sTW`T^m`_f}_x9a~O2OPpOFeIRwOfV0@}T;92-UdeubotOar1j+co#i1BpLH^bM zc}Whw?F%YAsAtFB53t-bcK`<#Rl{7za$5#>9boQX5wRsp-;~accMVV{jy)YN7~n?8 zji(1`XBphoBWbpola;6Al>_T{!>9rrKxmb#Awui_`(BA-`->L7T7iqKe{pEWtglPD zhMFp`gf&_JQH0=KN95=L8=ijs=F1qjCvW`s)Zu7E-f;txcE3?ePP_!s>gn6(!6|{l z(vF$&MXI3!3qSBT*{RhZ{O}3OgG;qdF|t3T@^Um@DPnG5e_9HI@c84s9$(dHHfb0m zjccJYS>{_i#I9-qc?^Za{DD)r}(x1{OAed*E(EGWOfK;qk+JArH~})&S?3SHt!)cfEQ``+fA@@OwMs5&7~-H8|p8KC>rn z-q}StWBp5Vb^FBU`eRImd$Avj#z0|dapg_rNBs4JbycM$g-I_2Rb=L*DjJ_5!X zH9Sp!Iy)c`ZEVz3_CtM$aA^#k>zeJ{!}3p&)ZUWjYkY~oY|W$y%0IW85vq+I|Kpna zk63DCuB|vsB;5hZU|GjKH3KW;Jr#8k9=7~5SH7F(UR6-bI)jlIH3g>&;xGkIkX0o0F|>jOz%JAo?2VTx zeh|RmfSpQImRPys=B4S?fiPx}mLu!fk3WO*fx64$QIAh*S;PM5W^!AMfQoy1pL4xxRKBo-8`r0?%>L`xO@MAasOtN~6_|B%g_2_~w zMw9EW1GX3((SR=BU+yIjrnq<}4z&}?s!p6|mmW%}R$GPS*}ym;VfiYp4$nZm6-uYH zHE`B0!(VP2s-+wojmH(=C&C(HoGH$#Sc5F+nGVOb{!c)-Y4zG+tADaguoo|uod@v*%BPx{fYyjro#VQX($@Jq= zKC${&0jB2ZYCgiV4L#7H{Q|gEY`?o+7#TpOJ$xi2=UFfH?EMt8ce8GMkCpz8tF2mL z67*u%kr1fNYa?O$f5fN!fWBTa>Tqf!QJfey{dn(`sBF#uXaRugM6CW`u#(+@mcXv+ z77Y5RnRJ3r^vY-=C5F8=*JXqo7H*B;Eg|WR%L6jmhsTJ^6tSE-hD9vv!whHv$>Ac$ zrLj0KA9D26b0eo4FdFq{)oSq*7O{E!f8No}xpQxy2vw>>6lu>zt;jS#>PTfw&B_NZ z#Kt$U%iUMY$838x+>>{mB;%4nsAElBK_~1P?YVsqIrC*#x1Q8I_Wy|dEoZdkgjoU$ zcR!AxA>e0%fk;%P-X0szXc-Ls;aOL~6Ih|-8?7$EGvUF;Mo2n>A6{c1pNZTaUz+3z zs~Z6@<5^$L2*SoeX>$v)$3XIr)iTguVCC^EIEE2#AtE;l)Hxc>m!a3W$@V!et=78N z7_p;&YYrx+oL}D-GP^95s5Fsd?;L54=N+IW+W)fQEFvu`diXzF>Bz}i8UfA(W3Ehp zrr=55%U$BFDY%CWg}q*ir76576(^1N<*S-TWHAp`qR@yw&9YgJ%&oBj=Dykp>{h== zt{I-QKw|#utk)lHd`Uu_N@K<;(It**Hw*p>s)~3mz6vXC>R~W?0lknaB3|M*AEuv4+PG8hJ4C#=_rwC z^uD)B=EmtdU+ulV7_$ek{NT`g;=CLOZ_p&^gGJ9>|E%(5(G_$HvlUaTVHStnYGKnL z!paWcEK6Qz*^&%*opsaZm5K-0AK#;mE9@b-qCo=>>Px6-sIH~e`G@V1JK3mH9m|vx`p=zvD0j&e$+%DD!6S+ zdtJBj$JcW~bh=>}Q?A&De@3leyrgQ^SvDJ=238P3S$Wnx3l{F%`Y7Y+h{qYMb@yP*KW(Z>@kRa@jL`~A2e($$-H*gYO^5>>xTeC` zBQB5DI}~d21I24VM-gsU2wJkWoM~0!AiD72Q{x-y>z2V;{ih~eJ1^Kl0LlJK`>A@x zeLjljC~zNWts%%_H>J~yXBUjL)s&_J*h%k#H4j1q{^)}%V7S~Jk)dRyA6)J=!%9A< zQ@K=lu4L;}YPWRQ-XX$f=7X~`6$RbAhX+(T#9XsPD8F1kYD=^G{mq@W=lH)XGev~n zRJYW3Zen`xfJ(E7`}!GnVEOnF<;37tfH(aam$a7VZa18wNDk3~#LFY%B^<4*g_=Wg z19;4wXt1yvX zAAaJSB)9zjB2Pv~@Zw$7&wh4l*CY5@e^0UZJ2g;%N4mV zIi4OpQ51NqZlU+Sv_Z)BPV}m#N*hTEx4~aO;dYXOT@b3@OlvP_jCyRsU)DQQ<4Xs1I#CQ!n(YwNg zCL4Ij!(5v(68E?GXkvK8kv zs4n8S@9tMy?To1Yf9;9TRQEFkKjg<7p@)~ zGvZwb2TjF*)`apYS$%9Z5~u_O1I|w%CIJWr3-+y>X=&nYStYg-a37Rk+@^GJ<_i3p zso89c*7VkV38?tu_@&Y{2-wzz8r&b|0y@_t7JV}(CAd_43MLHy7wSt@$Hpo zyPBUlGFvGL&!g*JVSBi&<Img;a^ zc~*RKh$pXrF{6!yn5H=>twS}6oqV~LWSsET3t3KAkPxnTT=gZA);m{dBH25-+u3d4 z`cSovWQlv>kVDssDlb|%)c<-J;B`+n zN~tF>PgTl~hl;DYw!Ll*Co#2$w7Zk4Zs$VCc+Y2EPgayjau{i^p+4Ifot%Cr#No&N zI=@c|lr>CI^Y?1W7pgmjhztH&24%-I5zsYOy>0Syr48oarslR*k9r@E?8&8^)Al5i{YkErxA;6VL4-NGhZ3uo z_)FWC-b?OBOrgSKgV1heDG~lFkry8Sc5Wo+UtE+%(G^NQW{VYAEifv^_t?k?l2t*l zWj~ACypm)J=00%eF5OC++|r0*4^$W^vLMxR<()&zx1?xqWBLi)S3H9)!e=cf)y#@{ z;Ll+?Wzj14)~$dCMKC3qx|1qv?EFg}l0Q=MG*WYG{s`us-OroFmp&rc;vL(=hRLzqLwvj<`Y@` zF=AfMv%z=$OjAE@?dooNT5nBRiM9vdco=_w!aCN0{Q$%7a#h0APm_Ww%RA-MeP3_0 zLZ012_KW70tHp#C_s7c`ZJ%lGw?jn-)f9)0@1NXr8=lRHO}B&nv=UPMp+Cw6k+p(3c#Ssr3mR9$von5lPu_4u#MDG;*IaPU;CQwj0E_(oTW0Lxt(? z=Ix(d+<%02&>jKN-bec|c`*}I3ai>Eg&+meG0QfF+;rDSnLiV5pE$0Z9th);nsoF7 zF`1CZ%KP$s#2E$E-ad=ai!SmHczl0EfOEA86hIz^II2P`xN6DV5s~C^9z(NaDD%0V z-aN#vcMZH!@{TzH7v`pc*Ou221U0{w{Q1wDkEql*eCXL6oWC}3$>z{(atHBneI>sJ z*1A*6%Ff5C9G*^Qv@Hp8s*4M72H!z<49F$ExlGd>2iEU(+`Wb(f;Y4G_}6WGf6U%t zRWLk%FhHnX{%tjtHjaiVENb+>BDcTUckbkElpE6CNOj7=>k!jc;>Vf~Tl>fTJntIW zrkBY6>U`RnC+yQxtCRlaixpblBN&?3dsWx96_q9GV-)zniF~C|fE*tDTZlvl-@XEn zU7!3!JzhQ4h;CR%!VG2$V0gQ}Bx@@tO}&>!s6%}fYNk+ZV^Y|VSwT;O3V&Y6ExDqT zjV2pG-sc8g?2;VSoM{vX-Qt6a?%w_mBc<{o>@svntG)JVqoo?vC>|DAwjqy^P}cja zABL02?D>ZDNBFaJ`sA8stz|pLT;OJ#nhuh3ts@$H9p#r71lIx^GIw-gvXpswu;Iy6 zA*fbxI5S79{^G(n5!ZsB(Tw7?GOpl;nS~fh-&g$KqCU&nTA$mSM7W=dBcv z6t0;uOG3R4BslhCSU1b_i)4Ah<1e+9_C_2^dMu(_Q~xZ#`#7}M=x8Q;1nH&rM%Rcg zEvLfVS>vm;hC!uFQPyN6i+k`hcGZ2S3790fGQH|Ao4!ymoEVV^;*c^q+ne*7{0X~; zg#Y(a9?oGNHFl50vZS2nZm!#>3mAPCiz4X83%>l^@GR!pSV2qrO)ZE?MNm(Z70Z-e zl*>}|JsP3RqBmCt!n7#iVcu%?6K>XjF=;Rq;NjEzS!Z>aR|Iz}aPwxu*3BctO5erS zi>SN&RtelV#}!f)g+!YvE&X;AR#);Uv5L|K)P`!9oKbb$z2eTTfu2qFCYEOx!OV7a z?K|kHWr7$8jFIeGIa1T~8sJ|a%6C6tDogZ`IPlI44AGG1+U9X=Zedm;kw~QEn#YD) zV^Uq5l578I5zO&k;&rMZ;B>>C5B8l%$+^-loUdj9yoX9{ncCok(!TEWSa z{mYB{R{av*!$t@!8?+b=iZgVIZBT#@QKN$z_f?50Onu06@C@m$dWIZ0UGM^Zft2qj zx2dr?=eKwjfvh*eE3_UDWGdfu!?VK%ua2{8%ed4N;0LeZ2>KwP-FPvjG8sF2 zI4Au>Jny#igf%URiM+oESg5 zy^LTF{7p1kACaiAhpKU=$4Dr4e_##cBK<%nY1Z&<*5pebsEevI+_gd-6khynYpk_O z$octYBT5ZUHnP3M27x(2~vhBP!_t;suem8_;i z@uFlp%IQUlk-q2RZMi0Ncc3*~pg)O?$5bj9&OaaCoO`iiqyq!XZ+>}_6#jiai;9dW z>F!Nun@EnNyF+)>5R>E&1x>}(uAod5^IExcQ1u3d5Fo0dAFQ@V{@I)j9ILYD}Vjxl$mA)dd7VwHX`k% zlu{wrQb&wFnbB#p9fzWUMATZ7Z_j&w?5y-U69c<(B(;Tn!QwZxI=)m+c&9Hsh9KfMHir#j1p!WZ>b%hR7}}^Qk}+uchUWR~*v z)+a^&If~}z`6-jwPadJx{#_ygbs5b6X~UO~-tFQO*kHDo)?4^}^rVE6)`~mO^vWeZ zi!`SDPR*Xbi+X0BJ6#?P_+$x@Av@HMLd0Kc4A-ArKf{XB=q%vM)lA8{zot}t`?K?_ z52RJ38Uf=4WD@;3@66w~zW3)2p4Mz?Ni9`EDRHrnSQ7rQ)@pN;T5#53wl+R=v`NEs zjHPXm%op@w$WnGhl2hoVTk>oQIY4;*;prghh{t&Q$cMXF_Q0rcT2s@ZIn;0$wRII| zL&iFDdMh#&)JZago`^I}V{h)w3%4W9pF5WOp3P2lD~AMUF*^HP*yWK5cZ+dPUp>@L zcUh?KtgQL25;6K#=_)cQDgDG8cVI1nql98UEtJ@^*wpq8@tSIjfAydlz z-L-ca3%D~PA!e%=thl8XsRILQxSDUS9)sXUyl;e0s{%x6j@FLVUpdFA(3DkIe58{E zA|2|nmnj)a=~!FWCuYkXCjgOtU&q@0fX32~`(luXBCfQ5|0l5oFn@vhs?kl`$B}aL zT_NK25Du}!tYok&-eUY*=&tGvg;|zj@bycXs3+AE!wQF`e}bmkAL4K*vU3D^QiN*~ z9ee829M^!5htmH2xO>Qprt;BfBUk@WI1*kEFXJ$Z{^{~mVZD;{2` z(SQ6_OJeG4ayX&tDxnb#MbrRTloS-E3crPz%*ImjOT;)CoUkdm&J0QQ|78OuLU~`B z^Ao??--ZVqQYjUyMeWMkzX>vsOg{XdL#e zUWR@J=lUoGj49pykS3jemOJi!Di2FcoD0kDa`w0;2TX|szkWL9AQI;4@Y3a%OH
hiJDCC(b#dDJl5Z5M&7a_&4Rk*}Q^{s)E3=0Opv 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
)-5qU3qKp71 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 From b70a61f06d85f8d4dd06696ec3d7bef1ebff1d0f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 8 Oct 2021 20:16:31 +0800 Subject: [PATCH 090/479] feat: init cli command --- cmd/main/main.go | 7 + cmd/root.go | 32 +++ go.mod | 9 +- go.sum | 568 ++++++++++++++++++++++++++++++++++++++++++++++- version.go | 3 + 5 files changed, 613 insertions(+), 6 deletions(-) create mode 100644 cmd/main/main.go create mode 100644 cmd/root.go create mode 100644 version.go diff --git a/cmd/main/main.go b/cmd/main/main.go new file mode 100644 index 00000000..12077975 --- /dev/null +++ b/cmd/main/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/httprunner/httpboomer/cmd" + +func main() { + cmd.Execute() +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 00000000..7d452891 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,32 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/httprunner/httpboomer" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "httpboomer", + Short: "httpboomer = httprunner + boomer", + Long: `HttpBoomer is a golang implementation of HttpRunner. +Ideally, HttpBoomer will be fully compatible with HttpRunner, including testcase format and usage. +What's more, HttpBoomer will integrate Boomer natively to be a better load generator for locust.`, + Version: httpboomer.VERSION, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) {}, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index a0392e52..bb99ed69 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,14 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect github.com/google/uuid v1.3.0 // indirect - github.com/imroc/req v0.3.0 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/maja42/goval v1.2.1 // indirect + github.com/imroc/req v0.3.0 + github.com/jmespath/go-jmespath v0.4.0 + github.com/maja42/goval v1.2.1 github.com/myzhan/boomer v1.6.0 github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/shirou/gopsutil v3.21.8+incompatible // indirect - github.com/stretchr/testify v1.7.0 // indirect + github.com/spf13/cobra v1.2.1 + github.com/stretchr/testify v1.7.0 github.com/tklauser/go-sysconf v0.3.9 // indirect github.com/ugorji/go v1.2.6 // indirect github.com/zeromq/goczmq v4.1.0+incompatible // indirect diff --git a/go.sum b/go.sum index 5ebb7ba7..3fd50847 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,250 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM= github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U= github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/maja42/goval v1.2.1 h1:fyEgzddqPgCZsKcFLk4C6SdCHyEaAHYvtZG4mGzQOHU= github.com/maja42/goval v1.2.1/go.mod h1:42LU+BQXL/veE9jnTTUOSj38GRmOTSThYSXRVodI5J4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/myzhan/boomer v1.6.0 h1:xjgvmhDjgU9IEKnB7nU1HyoVEfj8SuuU3u6oY3Nugj0= github.com/myzhan/boomer v1.6.0/go.mod h1:Ma68Td5C5EAc1M9XA7yjC/tXg9u5qviNujytnX099ZQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v3.21.8+incompatible h1:sh0foI8tMRlCidUJR+KzqWYWxrkuuPIGiO6Vp+KXdCU= github.com/shirou/gopsutil v3.21.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= @@ -36,17 +253,364 @@ github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zeromq/goczmq v4.1.0+incompatible h1:cGVQaU6kIwwrGso0Pgbl84tzAz/h7FJ3wYQjSonjFFc= github.com/zeromq/goczmq v4.1.0+incompatible/go.mod h1:1uZybAJoSRCvZMH2rZxEwWBSmC4T7CB/xQOfChwPEzg= github.com/zeromq/gomq v0.0.0-20201031135124-cef4e507bb8e h1:vGjfCnWv/zWeO1ivv4+OUPgTzG/WV1iGfZwVdtUpLkM= github.com/zeromq/gomq v0.0.0-20201031135124-cef4e507bb8e/go.mod h1:SkCxcSQ7BQEA9FvDzbj+3hV6EMhSywyxWnHwUXVIyLY= github.com/zeromq/gomq/zmtp v0.0.0-20201031135124-cef4e507bb8e h1:pjp04/sSr2TYuaPdt+u6Cc1M38Aocp+3er0akr3auFg= github.com/zeromq/gomq/zmtp v0.0.0-20201031135124-cef4e507bb8e/go.mod h1:LBjWEodY/ESvKRwLw3bc7mhn49oiI8qlXUqeqLn0pcU= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/version.go b/version.go new file mode 100644 index 00000000..9413b4b9 --- /dev/null +++ b/version.go @@ -0,0 +1,3 @@ +package httpboomer + +const VERSION = "v0.1.0" From 29e8942f184ef7234c187c66104c05a659ce7694 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 9 Oct 2021 14:50:52 +0800 Subject: [PATCH 091/479] feat: add TCase struct for json loading and dumping --- models.go | 63 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/models.go b/models.go index 9cc46949..c3716b5b 100644 --- a/models.go +++ b/models.go @@ -14,44 +14,50 @@ const ( type TConfig struct { Name string `json:"name"` - Verify bool `json:"verify"` - BaseURL string `json:"base_url"` - Variables map[string]interface{} `json:"variables"` - Parameters map[string]interface{} `json:"parameters"` - Export []string `json:"export"` - Weight int `json:"weight"` + Verify bool `json:"verify,omitempty"` + BaseURL string `json:"base_url,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` + Export []string `json:"export,omitempty"` + Weight int `json:"weight,omitempty"` } type TRequest struct { Method enumHTTPMethod `json:"method"` URL string `json:"url"` - Params map[string]interface{} `json:"params"` - Headers map[string]string `json:"headers"` - Cookies map[string]string `json:"cookies"` - Data interface{} `json:"data"` - JSON interface{} `json:"json"` - Timeout float32 `json:"timeout"` - AllowRedirects bool `json:"allow_redirects"` - Verify bool `json:"verify"` + Params map[string]interface{} `json:"params,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Cookies map[string]string `json:"cookies,omitempty"` + Data interface{} `json:"data,omitempty"` + JSON interface{} `json:"json,omitempty"` + Timeout float32 `json:"timeout,omitempty"` + AllowRedirects bool `json:"allow_redirects,omitempty"` + Verify bool `json:"verify,omitempty"` } type TValidator struct { - Check string // get value with jmespath - Assert string - Expect interface{} - Message string + Check string `json:"check,omitempty"` // get value with jmespath + Assert string `json:"assert,omitempty"` + Expect interface{} `json:"expect,omitempty"` + Message string `json:"msg,omitempty"` } type TStep struct { Name string `json:"name"` - Request *TRequest `json:"request"` - TestCase *TestCase `json:"testcase"` - Variables map[string]interface{} `json:"variables"` - SetupHooks []string `json:"setup_hooks"` - TeardownHooks []string `json:"teardown_hooks"` - Extract map[string]string `json:"extract"` - Validators []TValidator `json:"validators"` - Export []string `json:"export"` + Request *TRequest `json:"request,omitempty"` + TestCase *TestCase `json:"testcase,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty"` + SetupHooks []string `json:"setup_hooks,omitempty"` + TeardownHooks []string `json:"teardown_hooks,omitempty"` + Extract map[string]string `json:"extract,omitempty"` + Validators []TValidator `json:"validate,omitempty"` + Export []string `json:"export,omitempty"` +} + +// used for testcase json loading and dumping +type TCase struct { + Config TConfig `json:"config"` + TestSteps []*TStep `json:"teststeps"` } // interface for all types of steps @@ -61,9 +67,10 @@ type IStep interface { ToStruct() *TStep } +// used for testcase runner type TestCase struct { - Config TConfig `json:"config"` - TestSteps []IStep `json:"teststeps"` + Config TConfig + TestSteps []IStep } type TestCaseSummary struct{} From 44f9756646fc2248b5b174d665675f470f3c9d0d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 9 Oct 2021 15:35:11 +0800 Subject: [PATCH 092/479] feat: dump2JSON --- convert.go | 35 +++++++++++++++++++++++++++++++ convert_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 convert.go create mode 100644 convert_test.go diff --git a/convert.go b/convert.go new file mode 100644 index 00000000..94c06577 --- /dev/null +++ b/convert.go @@ -0,0 +1,35 @@ +package httpboomer + +import ( + "encoding/json" + "io/ioutil" + "log" + "path/filepath" +) + +func (tc *TestCase) toStruct() *TCase { + tcStruct := TCase{ + Config: tc.Config, + } + for _, step := range tc.TestSteps { + tcStruct.TestSteps = append(tcStruct.TestSteps, step.ToStruct()) + } + return &tcStruct +} + +func (tc *TestCase) dump2JSON(path string) error { + path, err := filepath.Abs(path) + if err != nil { + log.Printf("convert absolute path error: %v, path: %v", err, path) + return err + } + log.Printf("dump testcase to json path: %s", path) + tcStruct := tc.toStruct() + file, _ := json.MarshalIndent(tcStruct, "", " ") + err = ioutil.WriteFile(path, file, 0644) + if err != nil { + log.Printf("dump json path error: %v", err) + return err + } + return nil +} diff --git a/convert_test.go b/convert_test.go new file mode 100644 index 00000000..c21a3384 --- /dev/null +++ b/convert_test.go @@ -0,0 +1,56 @@ +package httpboomer + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDump2JSON(t *testing.T) { + testcase := &TestCase{ + Config: TConfig{ + Name: "demo with complex mechanisms", + BaseURL: "https://postman-echo.com", + Variables: map[string]interface{}{ // global level variables + "n": 5, + "a": 12.3, + "b": 3.45, + "varFoo1": "${gen_random_string($n)}", + "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function + }, + }, + TestSteps: []IStep{ + Step("get with params"). + WithVariables(map[string]interface{}{ // step level variables + "n": 3, // inherit config level variables if not set in step level, a/varFoo1 + "b": 34.5, // override config level variable if existed, n/b/varFoo2 + "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). // request with headers + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). // extract variable with jmespath + Validate(). + AssertEqual("status_code", 200, "check response status code"). // validate response status code + AssertStartsWith("headers.\"Content-Type\"", "application/json", ""). // validate response header + AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath + AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step + AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string + Step("post json data"). + POST("/post"). + WithJSON(map[string]interface{}{ + "foo1": "$varFoo1", // reference former extracted variable + "foo2": "${max($a, $b)}", // 12.3; step level variables are independent, variable b is 3.45 here + }). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.json.foo1", 5, "check args foo1"). + AssertEqual("body.json.foo2", 12.3, "check args foo2"), + }, + } + err := testcase.dump2JSON("test.json") + if !assert.NoError(t, err) { + t.Fail() + } +} From 3cbcab6361ef009079cb0c126c5a9f48743e179d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 9 Oct 2021 16:39:25 +0800 Subject: [PATCH 093/479] feat: dump2YAML --- convert.go | 31 ++++++++++++++++ convert_test.go | 94 +++++++++++++++++++++++++++---------------------- go.mod | 1 + models.go | 64 ++++++++++++++++----------------- 4 files changed, 115 insertions(+), 75 deletions(-) diff --git a/convert.go b/convert.go index 94c06577..654e3976 100644 --- a/convert.go +++ b/convert.go @@ -1,10 +1,13 @@ package httpboomer import ( + "bytes" "encoding/json" "io/ioutil" "log" "path/filepath" + + "gopkg.in/yaml.v3" ) func (tc *TestCase) toStruct() *TCase { @@ -33,3 +36,31 @@ func (tc *TestCase) dump2JSON(path string) error { } return nil } + +func (tc *TestCase) dump2YAML(path string) error { + path, err := filepath.Abs(path) + if err != nil { + log.Printf("convert absolute path error: %v, path: %v", err, path) + return err + } + log.Printf("dump testcase to yaml path: %s", path) + + // init yaml encoder + buffer := new(bytes.Buffer) + encoder := yaml.NewEncoder(buffer) + encoder.SetIndent(4) + + // encode + tcStruct := tc.toStruct() + err = encoder.Encode(tcStruct) + if err != nil { + return err + } + + err = ioutil.WriteFile(path, buffer.Bytes(), 0644) + if err != nil { + log.Printf("dump yaml path error: %v", err) + return err + } + return nil +} diff --git a/convert_test.go b/convert_test.go index c21a3384..c3f8e45b 100644 --- a/convert_test.go +++ b/convert_test.go @@ -6,50 +6,58 @@ import ( "github.com/stretchr/testify/assert" ) +var demoTestCase = &TestCase{ + Config: TConfig{ + Name: "demo with complex mechanisms", + BaseURL: "https://postman-echo.com", + Variables: map[string]interface{}{ // global level variables + "n": 5, + "a": 12.3, + "b": 3.45, + "varFoo1": "${gen_random_string($n)}", + "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function + }, + }, + TestSteps: []IStep{ + Step("get with params"). + WithVariables(map[string]interface{}{ // step level variables + "n": 3, // inherit config level variables if not set in step level, a/varFoo1 + "b": 34.5, // override config level variable if existed, n/b/varFoo2 + "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again + }). + GET("/get"). + WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params + WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). // request with headers + Extract(). + WithJmesPath("body.args.foo1", "varFoo1"). // extract variable with jmespath + Validate(). + AssertEqual("status_code", 200, "check response status code"). // validate response status code + AssertStartsWith("headers.\"Content-Type\"", "application/json", ""). // validate response header + AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath + AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step + AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string + Step("post json data"). + POST("/post"). + WithJSON(map[string]interface{}{ + "foo1": "$varFoo1", // reference former extracted variable + "foo2": "${max($a, $b)}", // 12.3; step level variables are independent, variable b is 3.45 here + }). + Validate(). + AssertEqual("status_code", 200, "check status code"). + AssertLengthEqual("body.json.foo1", 5, "check args foo1"). + AssertEqual("body.json.foo2", 12.3, "check args foo2"), + }, +} + func TestDump2JSON(t *testing.T) { - testcase := &TestCase{ - Config: TConfig{ - Name: "demo with complex mechanisms", - BaseURL: "https://postman-echo.com", - Variables: map[string]interface{}{ // global level variables - "n": 5, - "a": 12.3, - "b": 3.45, - "varFoo1": "${gen_random_string($n)}", - "varFoo2": "${max($a, $b)}", // 12.3; eval with built-in function - }, - }, - TestSteps: []IStep{ - Step("get with params"). - WithVariables(map[string]interface{}{ // step level variables - "n": 3, // inherit config level variables if not set in step level, a/varFoo1 - "b": 34.5, // override config level variable if existed, n/b/varFoo2 - "varFoo2": "${max($a, $b)}", // 34.5; override variable b and eval again - }). - GET("/get"). - WithParams(map[string]interface{}{"foo1": "$varFoo1", "foo2": "$varFoo2"}). // request with params - WithHeaders(map[string]string{"User-Agent": "HttpBoomer"}). // request with headers - Extract(). - WithJmesPath("body.args.foo1", "varFoo1"). // extract variable with jmespath - Validate(). - AssertEqual("status_code", 200, "check response status code"). // validate response status code - AssertStartsWith("headers.\"Content-Type\"", "application/json", ""). // validate response header - AssertLengthEqual("body.args.foo1", 5, "check args foo1"). // validate response body with jmespath - AssertLengthEqual("$varFoo1", 5, "check args foo1"). // assert with extracted variable from current step - AssertEqual("body.args.foo2", "34.5", "check args foo2"), // notice: request params value will be converted to string - Step("post json data"). - POST("/post"). - WithJSON(map[string]interface{}{ - "foo1": "$varFoo1", // reference former extracted variable - "foo2": "${max($a, $b)}", // 12.3; step level variables are independent, variable b is 3.45 here - }). - Validate(). - AssertEqual("status_code", 200, "check status code"). - AssertLengthEqual("body.json.foo1", 5, "check args foo1"). - AssertEqual("body.json.foo2", 12.3, "check args foo2"), - }, - } - err := testcase.dump2JSON("test.json") + err := demoTestCase.dump2JSON("demo.json") + if !assert.NoError(t, err) { + t.Fail() + } +} + +func TestDump2YAML(t *testing.T) { + err := demoTestCase.dump2YAML("demo.yml") if !assert.NoError(t, err) { t.Fail() } diff --git a/go.mod b/go.mod index bb99ed69..c7cfc1b5 100644 --- a/go.mod +++ b/go.mod @@ -20,4 +20,5 @@ require ( github.com/zeromq/gomq v0.0.0-20201031135124-cef4e507bb8e // indirect github.com/zeromq/gomq/zmtp v0.0.0-20201031135124-cef4e507bb8e // indirect golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/models.go b/models.go index c3716b5b..8b0e8e75 100644 --- a/models.go +++ b/models.go @@ -13,51 +13,51 @@ const ( ) type TConfig struct { - Name string `json:"name"` - Verify bool `json:"verify,omitempty"` - BaseURL string `json:"base_url,omitempty"` - Variables map[string]interface{} `json:"variables,omitempty"` - Parameters map[string]interface{} `json:"parameters,omitempty"` - Export []string `json:"export,omitempty"` - Weight int `json:"weight,omitempty"` + Name string `json:"name" yaml:"name"` + Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` + BaseURL string `json:"base_url,omitempty" yaml:"base_url,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Export []string `json:"export,omitempty" yaml:"export,omitempty"` + Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` } type TRequest struct { - Method enumHTTPMethod `json:"method"` - URL string `json:"url"` - Params map[string]interface{} `json:"params,omitempty"` - Headers map[string]string `json:"headers,omitempty"` - Cookies map[string]string `json:"cookies,omitempty"` - Data interface{} `json:"data,omitempty"` - JSON interface{} `json:"json,omitempty"` - Timeout float32 `json:"timeout,omitempty"` - AllowRedirects bool `json:"allow_redirects,omitempty"` - Verify bool `json:"verify,omitempty"` + Method enumHTTPMethod `json:"method" yaml:"method"` + URL string `json:"url" yaml:"url"` + Params map[string]interface{} `json:"params,omitempty" yaml:"params,omitempty"` + Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"` + Cookies map[string]string `json:"cookies,omitempty" yaml:"cookies,omitempty"` + Data interface{} `json:"data,omitempty" yaml:"data,omitempty"` + JSON interface{} `json:"json,omitempty" yaml:"json,omitempty"` + Timeout float32 `json:"timeout,omitempty" yaml:"timeout,omitempty"` + AllowRedirects bool `json:"allow_redirects,omitempty" yaml:"allow_redirects,omitempty"` + Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` } type TValidator struct { - Check string `json:"check,omitempty"` // get value with jmespath - Assert string `json:"assert,omitempty"` - Expect interface{} `json:"expect,omitempty"` - Message string `json:"msg,omitempty"` + Check string `json:"check,omitempty" yaml:"check,omitempty"` // get value with jmespath + Assert string `json:"assert,omitempty" yaml:"assert,omitempty"` + Expect interface{} `json:"expect,omitempty" yaml:"expect,omitempty"` + Message string `json:"msg,omitempty" yaml:"msg,omitempty"` } type TStep struct { - Name string `json:"name"` - Request *TRequest `json:"request,omitempty"` - TestCase *TestCase `json:"testcase,omitempty"` - Variables map[string]interface{} `json:"variables,omitempty"` - SetupHooks []string `json:"setup_hooks,omitempty"` - TeardownHooks []string `json:"teardown_hooks,omitempty"` - Extract map[string]string `json:"extract,omitempty"` - Validators []TValidator `json:"validate,omitempty"` - Export []string `json:"export,omitempty"` + Name string `json:"name" yaml:"name"` + Request *TRequest `json:"request,omitempty" yaml:"request,omitempty"` + TestCase *TestCase `json:"testcase,omitempty" yaml:"testcase,omitempty"` + Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` + SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` + TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` + Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"` + Validators []TValidator `json:"validate,omitempty" yaml:"validate,omitempty"` + Export []string `json:"export,omitempty" yaml:"export,omitempty"` } // used for testcase json loading and dumping type TCase struct { - Config TConfig `json:"config"` - TestSteps []*TStep `json:"teststeps"` + Config TConfig `json:"config" yaml:"config"` + TestSteps []*TStep `json:"teststeps" yaml:"teststeps"` } // interface for all types of steps From 89ca7776406ebbfaae58414879ce6cd135b22b13 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 9 Oct 2021 20:11:02 +0800 Subject: [PATCH 094/479] feat: loadFromJSON --- convert.go | 19 +++++++++++++++++++ convert_test.go | 21 +++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/convert.go b/convert.go index 654e3976..5d579043 100644 --- a/convert.go +++ b/convert.go @@ -64,3 +64,22 @@ func (tc *TestCase) dump2YAML(path string) error { } return nil } + +func loadFromJSON(path string) (*TCase, error) { + path, err := filepath.Abs(path) + if err != nil { + log.Printf("convert absolute path error: %v, path: %v", err, path) + return nil, err + } + log.Printf("load testcase from json path: %s", path) + + file, err := ioutil.ReadFile(path) + if err != nil { + log.Printf("dump json path error: %v", err) + return nil, err + } + + tc := &TCase{} + err = json.Unmarshal(file, tc) + return tc, err +} diff --git a/convert_test.go b/convert_test.go index c3f8e45b..a538c1cb 100644 --- a/convert_test.go +++ b/convert_test.go @@ -49,11 +49,28 @@ var demoTestCase = &TestCase{ }, } -func TestDump2JSON(t *testing.T) { - err := demoTestCase.dump2JSON("demo.json") +func TestDumpAndLoadJSON(t *testing.T) { + jsonPath := "demo.json" + err := demoTestCase.dump2JSON(jsonPath) if !assert.NoError(t, err) { t.Fail() } + tc, err := loadFromJSON(jsonPath) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, tc.Config.Name, demoTestCase.Config.Name) { + t.Fail() + } + if !assert.Equal(t, tc.Config.BaseURL, demoTestCase.Config.BaseURL) { + t.Fail() + } + if !assert.Equal(t, tc.TestSteps[1].Name, demoTestCase.TestSteps[1].Name()) { + t.Fail() + } + if !assert.Equal(t, tc.TestSteps[1].Request, demoTestCase.TestSteps[1].ToStruct().Request) { + t.Fail() + } } func TestDump2YAML(t *testing.T) { From 578d56daadca690ad2a3ee4420eb53c4f1f53f3e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 9 Oct 2021 20:12:50 +0800 Subject: [PATCH 095/479] feat: loadFromYAML --- convert.go | 19 +++++++++++++++++++ convert_test.go | 21 +++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/convert.go b/convert.go index 5d579043..add6e415 100644 --- a/convert.go +++ b/convert.go @@ -83,3 +83,22 @@ func loadFromJSON(path string) (*TCase, error) { err = json.Unmarshal(file, tc) return tc, err } + +func loadFromYAML(path string) (*TCase, error) { + path, err := filepath.Abs(path) + if err != nil { + log.Printf("convert absolute path error: %v, path: %v", err, path) + return nil, err + } + log.Printf("load testcase from yaml path: %s", path) + + file, err := ioutil.ReadFile(path) + if err != nil { + log.Printf("dump yaml path error: %v", err) + return nil, err + } + + tc := &TCase{} + err = yaml.Unmarshal(file, tc) + return tc, err +} diff --git a/convert_test.go b/convert_test.go index a538c1cb..88622cbe 100644 --- a/convert_test.go +++ b/convert_test.go @@ -73,9 +73,26 @@ func TestDumpAndLoadJSON(t *testing.T) { } } -func TestDump2YAML(t *testing.T) { - err := demoTestCase.dump2YAML("demo.yml") +func TestDumpAndLoadYAML(t *testing.T) { + yamlPath := "demo.yaml" + err := demoTestCase.dump2YAML(yamlPath) if !assert.NoError(t, err) { t.Fail() } + tc, err := loadFromYAML(yamlPath) + if !assert.NoError(t, err) { + t.Fail() + } + if !assert.Equal(t, tc.Config.Name, demoTestCase.Config.Name) { + t.Fail() + } + if !assert.Equal(t, tc.Config.BaseURL, demoTestCase.Config.BaseURL) { + t.Fail() + } + if !assert.Equal(t, tc.TestSteps[1].Name, demoTestCase.TestSteps[1].Name()) { + t.Fail() + } + if !assert.Equal(t, tc.TestSteps[1].Request, demoTestCase.TestSteps[1].ToStruct().Request) { + t.Fail() + } } From fbc557ecd9db254472ad092364a730d0c93c5010 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 9 Oct 2021 20:41:00 +0800 Subject: [PATCH 096/479] feat: convertTestCase --- convert.go | 20 ++++++++++++++++++++ convert_test.go | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/convert.go b/convert.go index add6e415..66cfd343 100644 --- a/convert.go +++ b/convert.go @@ -102,3 +102,23 @@ func loadFromYAML(path string) (*TCase, error) { err = yaml.Unmarshal(file, tc) return tc, err } + +func convertTestCase(tc *TCase) (*TestCase, error) { + testCase := &TestCase{ + Config: tc.Config, + } + for _, step := range tc.TestSteps { + if step.Request != nil { + testCase.TestSteps = append(testCase.TestSteps, &requestWithOptionalArgs{ + step: step, + }) + } else if step.TestCase != nil { + testCase.TestSteps = append(testCase.TestSteps, &testcaseWithOptionalArgs{ + step: step, + }) + } else { + log.Printf("[convertTestCase] unexpected step: %+v", step) + } + } + return testCase, nil +} diff --git a/convert_test.go b/convert_test.go index 88622cbe..eaad39d2 100644 --- a/convert_test.go +++ b/convert_test.go @@ -96,3 +96,19 @@ func TestDumpAndLoadYAML(t *testing.T) { t.Fail() } } + +func TestLoadJSONAndRun(t *testing.T) { + jsonPath := "demo.json" + tc, err := loadFromJSON(jsonPath) + if !assert.NoError(t, err) { + t.Fail() + } + testcase, err := convertTestCase(tc) + if !assert.NoError(t, err) { + t.Fail() + } + err = Test(t, testcase) + if err != nil { + t.Fatalf("run testcase error: %v", err) + } +} From bfc1742e3b929e892724da2c194b4b4271699c92 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 9 Oct 2021 23:17:36 +0800 Subject: [PATCH 097/479] fix: json unmarshal int --- builtin/assertion.go | 35 ++++++++++++++++++++++++++++++++--- convert.go | 4 +++- convert_test.go | 5 +---- parser.go | 35 ++++++++++++++++++++++++++++++----- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/builtin/assertion.go b/builtin/assertion.go index 30ecfdd5..31110221 100644 --- a/builtin/assertion.go +++ b/builtin/assertion.go @@ -49,8 +49,37 @@ func EndsWith(t assert.TestingT, expected, actual interface{}, msgAndArgs ...int } func EqualLength(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if !assert.IsType(t, 129, expected, fmt.Sprintf("expected type is not int, got %#v", expected)) { - return false + length, err := convertInt(expected) + if err != nil { + return assert.Fail(t, fmt.Sprintf("expected type is not int, got %#v", expected), msgAndArgs...) + } + + return assert.Len(t, actual, length, msgAndArgs...) +} + +func convertInt(value interface{}) (int, error) { + switch v := value.(type) { + case int: + return v, nil + case int8: + return int(v), nil + case int16: + return int(v), nil + case int32: + return int(v), nil + case int64: + return int(v), nil + case uint: + return int(v), nil + case uint8: + return int(v), nil + case uint16: + return int(v), nil + case uint32: + return int(v), nil + case uint64: + return int(v), nil + default: + return 0, fmt.Errorf("unsupported int convertion for %v(%T)", v, v) } - return assert.Len(t, actual, expected.(int), msgAndArgs...) } diff --git a/convert.go b/convert.go index 66cfd343..80280226 100644 --- a/convert.go +++ b/convert.go @@ -80,7 +80,9 @@ func loadFromJSON(path string) (*TCase, error) { } tc := &TCase{} - err = json.Unmarshal(file, tc) + decoder := json.NewDecoder(bytes.NewReader(file)) + decoder.UseNumber() + err = decoder.Decode(tc) return tc, err } diff --git a/convert_test.go b/convert_test.go index eaad39d2..8b9dbb9e 100644 --- a/convert_test.go +++ b/convert_test.go @@ -83,10 +83,7 @@ func TestDumpAndLoadYAML(t *testing.T) { if !assert.NoError(t, err) { t.Fail() } - if !assert.Equal(t, tc.Config.Name, demoTestCase.Config.Name) { - t.Fail() - } - if !assert.Equal(t, tc.Config.BaseURL, demoTestCase.Config.BaseURL) { + if !assert.Equal(t, tc.Config, demoTestCase.Config) { t.Fail() } if !assert.Equal(t, tc.TestSteps[1].Name, demoTestCase.TestSteps[1].Name()) { diff --git a/parser.go b/parser.go index f5179cb3..df6a356a 100644 --- a/parser.go +++ b/parser.go @@ -1,6 +1,7 @@ package httpboomer import ( + "encoding/json" "fmt" "log" "net/url" @@ -61,6 +62,11 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) (interf rawValue := reflect.ValueOf(raw) switch rawValue.Kind() { case reflect.String: + // json.Number + if rawValue, ok := raw.(json.Number); ok { + return parseJSONNumber(rawValue) + } + // other string value := rawValue.String() value = strings.TrimSpace(value) return parseString(value, variablesMapping) @@ -97,6 +103,16 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) (interf } } +func parseJSONNumber(raw json.Number) (interface{}, error) { + if strings.Contains(raw.String(), ".") { + // float64 + return raw.Float64() + } else { + // int64 + return raw.Int64() + } +} + const ( regexVariable = `[a-zA-Z_]\w*` // variable name should start with a letter or underscore regexFunctionName = `[a-zA-Z_]\w*` // function name should start with a letter or underscore @@ -252,17 +268,26 @@ func callFunc(funcName string, arguments ...interface{}) (interface{}, error) { argumentsValue := make([]reflect.Value, len(arguments)) for index, argument := range arguments { - // ensure each argument type match + argumentValue := reflect.ValueOf(argument) expectArgumentType := funcValue.Type().In(index) actualArgumentType := reflect.TypeOf(argument) - if expectArgumentType != actualArgumentType { - // function argument type not match - err := fmt.Errorf("function %s argument %d type not match, expect %v, actual %v", + + // type match + if expectArgumentType == actualArgumentType { + argumentsValue[index] = argumentValue + continue + } + + // type not match, check if convertible + if !actualArgumentType.ConvertibleTo(expectArgumentType) { + // function argument type not match and not convertible + err := fmt.Errorf("function %s argument %d type is neither match nor convertible, expect %v, actual %v", funcName, index, expectArgumentType, actualArgumentType) log.Printf("[callFunction] error: %s", err.Error()) return nil, err } - argumentsValue[index] = reflect.ValueOf(argument) + // convert argument to expect type + argumentsValue[index] = argumentValue.Convert(expectArgumentType) } log.Printf("[callFunction] func: %v, input arguments: %v", funcName, arguments) From 7579172e25620bda9bc4537f79f2490a0b325fa3 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 00:22:26 +0800 Subject: [PATCH 098/479] feat: run tsetcase by path --- README.md | 2 +- convert.go | 27 +++++++++++++++++++++++++++ convert_test.go | 12 +++++------- models.go | 20 ++++++++++++++++++++ runner.go | 12 +++++++++--- runner_test.go | 23 ++++++++++++++++++++++- 6 files changed, 84 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 851d0efa..452722d2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will ![flow chart](docs/flow.jpg) - [x] Full support for HTTP(S) requests, more protocols are also in the plan. -- [ ] Testcases can be described in multiple formats, `YAML`/`JSON`/`Golang`, and they are interchangeable. +- [x] Testcases can be described in multiple formats, `YAML`/`JSON`/`Golang`, and they are interchangeable. - [ ] With [`HAR`][HAR] support, you can use Charles/Fiddler/Chrome/etc as a script recording generator. - [x] Supports `variables`/`extract`/`validate`/`hooks` mechanisms to create extremely complex test scenarios. - [ ] Built-in integration of rich functions, and you can also use [`go plugin`][plugin] to create and call custom functions. diff --git a/convert.go b/convert.go index 80280226..c99d2bb7 100644 --- a/convert.go +++ b/convert.go @@ -3,6 +3,7 @@ package httpboomer import ( "bytes" "encoding/json" + "fmt" "io/ioutil" "log" "path/filepath" @@ -124,3 +125,29 @@ func convertTestCase(tc *TCase) (*TestCase, error) { } return testCase, nil } + +var ErrUnsupportedFileExt = fmt.Errorf("unsupported testcase file extension") + +func loadTestFile(path *TestCasePath) (*TestCase, error) { + var tc *TCase + var err error + + casePath := path.string + ext := filepath.Ext(casePath) + switch ext { + case ".json": + tc, err = loadFromJSON(casePath) + case ".yaml", ".yml": + tc, err = loadFromYAML(casePath) + default: + err = ErrUnsupportedFileExt + } + if err != nil { + return nil, err + } + testcase, err := convertTestCase(tc) + if err != nil { + return nil, err + } + return testcase, nil +} diff --git a/convert_test.go b/convert_test.go index 8b9dbb9e..9abd788c 100644 --- a/convert_test.go +++ b/convert_test.go @@ -50,7 +50,7 @@ var demoTestCase = &TestCase{ } func TestDumpAndLoadJSON(t *testing.T) { - jsonPath := "demo.json" + jsonPath := demoTestCaseJSONPath err := demoTestCase.dump2JSON(jsonPath) if !assert.NoError(t, err) { t.Fail() @@ -94,13 +94,11 @@ func TestDumpAndLoadYAML(t *testing.T) { } } +var demoTestCaseJSONPath = "demo.json" + func TestLoadJSONAndRun(t *testing.T) { - jsonPath := "demo.json" - tc, err := loadFromJSON(jsonPath) - if !assert.NoError(t, err) { - t.Fail() - } - testcase, err := convertTestCase(tc) + jsonPath := &TestCasePath{demoTestCaseJSONPath} + testcase, err := loadTestFile(jsonPath) if !assert.NoError(t, err) { t.Fail() } diff --git a/models.go b/models.go index 8b0e8e75..87e67f29 100644 --- a/models.go +++ b/models.go @@ -67,12 +67,32 @@ type IStep interface { ToStruct() *TStep } +type ITestCase interface { + ToStruct() (*TestCase, error) +} + // used for testcase runner type TestCase struct { Config TConfig TestSteps []IStep } +func (tc *TestCase) ToStruct() (*TestCase, error) { + return tc, nil +} + +type TestCasePath struct { + string +} + +func (path *TestCasePath) ToStruct() (*TestCase, error) { + testcase, err := loadTestFile(path) + if err != nil { + return nil, err + } + return testcase, nil +} + type TestCaseSummary struct{} type StepData struct { diff --git a/runner.go b/runner.go index 6565f120..43a99005 100644 --- a/runner.go +++ b/runner.go @@ -8,7 +8,7 @@ import ( "github.com/imroc/req" ) -func Test(t *testing.T, testcases ...*TestCase) error { +func Test(t *testing.T, testcases ...ITestCase) error { return NewRunner().WithTestingT(t).SetDebug(true).Run(testcases...) } @@ -36,9 +36,15 @@ func (r *Runner) SetDebug(debug bool) *Runner { return r } -func (r *Runner) Run(testcases ...*TestCase) error { +func (r *Runner) Run(testcases ...ITestCase) error { for _, testcase := range testcases { - if err := r.runCase(testcase); err != nil { + tcStruct, err := testcase.ToStruct() + if err != nil { + log.Printf("[Run] testcase.ToStruct() error: %v", err) + return err + } + if err := r.runCase(tcStruct); err != nil { + log.Printf("[Run] runCase error: %v", err) return err } } diff --git a/runner_test.go b/runner_test.go index 8a490e23..fbd180e8 100644 --- a/runner_test.go +++ b/runner_test.go @@ -1,6 +1,7 @@ package httpboomer import ( + "os" "testing" ) @@ -30,9 +31,29 @@ func TestHttpRunner(t *testing.T) { Weight: 3, }, } + testcase3 := &TestCasePath{demoTestCaseJSONPath} - err := Test(t, testcase1, testcase2) + err := Test(t, testcase1, testcase2, testcase3) if err != nil { t.Fatalf("run testcase error: %v", err) } } + +func TestMain(m *testing.M) { + // setup, prepare demo json testcase file path + jsonPath := demoTestCaseJSONPath + err := demoTestCase.dump2JSON(jsonPath) + if err != nil { + os.Exit(1) + } + + // run all tests + code := m.Run() + defer os.Exit(code) + + // teardown + err = os.Remove(jsonPath) + if err != nil { + os.Exit(1) + } +} From 47df01628068bf7dee5c9c85294d5c94056160ef Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 00:38:17 +0800 Subject: [PATCH 099/479] change: relocate tests --- convert_test.go | 50 ++++++++++++++++++++++++++++++++++--------------- runner_test.go | 20 -------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/convert_test.go b/convert_test.go index 9abd788c..381029f0 100644 --- a/convert_test.go +++ b/convert_test.go @@ -1,6 +1,7 @@ package httpboomer import ( + "os" "testing" "github.com/stretchr/testify/assert" @@ -49,13 +50,39 @@ var demoTestCase = &TestCase{ }, } -func TestDumpAndLoadJSON(t *testing.T) { - jsonPath := demoTestCaseJSONPath - err := demoTestCase.dump2JSON(jsonPath) - if !assert.NoError(t, err) { - t.Fail() +var ( + demoTestCaseJSONPath = "demo.json" + demoTestCaseYAMLPath = "demo.yaml" +) + +func TestMain(m *testing.M) { + // setup, prepare demo json/yaml testcase file path + err := demoTestCase.dump2JSON(demoTestCaseJSONPath) + if err != nil { + os.Exit(1) } - tc, err := loadFromJSON(jsonPath) + err = demoTestCase.dump2YAML(demoTestCaseYAMLPath) + if err != nil { + os.Exit(1) + } + + // run all tests + code := m.Run() + defer os.Exit(code) + + // teardown + err = os.Remove(demoTestCaseJSONPath) + if err != nil { + os.Exit(1) + } + err = os.Remove(demoTestCaseYAMLPath) + if err != nil { + os.Exit(1) + } +} + +func TestLoadJSONCase(t *testing.T) { + tc, err := loadFromJSON(demoTestCaseJSONPath) if !assert.NoError(t, err) { t.Fail() } @@ -73,13 +100,8 @@ func TestDumpAndLoadJSON(t *testing.T) { } } -func TestDumpAndLoadYAML(t *testing.T) { - yamlPath := "demo.yaml" - err := demoTestCase.dump2YAML(yamlPath) - if !assert.NoError(t, err) { - t.Fail() - } - tc, err := loadFromYAML(yamlPath) +func TestLoadYAMLCase(t *testing.T) { + tc, err := loadFromYAML(demoTestCaseYAMLPath) if !assert.NoError(t, err) { t.Fail() } @@ -94,8 +116,6 @@ func TestDumpAndLoadYAML(t *testing.T) { } } -var demoTestCaseJSONPath = "demo.json" - func TestLoadJSONAndRun(t *testing.T) { jsonPath := &TestCasePath{demoTestCaseJSONPath} testcase, err := loadTestFile(jsonPath) diff --git a/runner_test.go b/runner_test.go index fbd180e8..7b366c43 100644 --- a/runner_test.go +++ b/runner_test.go @@ -1,7 +1,6 @@ package httpboomer import ( - "os" "testing" ) @@ -38,22 +37,3 @@ func TestHttpRunner(t *testing.T) { t.Fatalf("run testcase error: %v", err) } } - -func TestMain(m *testing.M) { - // setup, prepare demo json testcase file path - jsonPath := demoTestCaseJSONPath - err := demoTestCase.dump2JSON(jsonPath) - if err != nil { - os.Exit(1) - } - - // run all tests - code := m.Run() - defer os.Exit(code) - - // teardown - err = os.Remove(jsonPath) - if err != nil { - os.Exit(1) - } -} From b69b7dd00c0a02f4d71a3f6bdde0c1f5593d79c9 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 00:49:19 +0800 Subject: [PATCH 100/479] feat: run tsetcase by path --- boomer.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/boomer.go b/boomer.go index c9863441..5c054602 100644 --- a/boomer.go +++ b/boomer.go @@ -6,7 +6,7 @@ import ( "github.com/myzhan/boomer" ) -func Run(testcases ...*TestCase) { +func Run(testcases ...ITestCase) { NewBoomer().Run(testcases...) } @@ -25,10 +25,14 @@ func (b *Boomer) SetDebug(debug bool) *Boomer { return b } -func (b *Boomer) Run(testcases ...*TestCase) { +func (b *Boomer) Run(testcases ...ITestCase) { var taskSlice []*boomer.Task for _, testcase := range testcases { - task := b.convertBoomerTask(testcase) + tcStruct, err := testcase.ToStruct() + if err != nil { + panic(err) + } + task := b.convertBoomerTask(tcStruct) taskSlice = append(taskSlice, task) } boomer.Run(taskSlice...) From e1411c36da1360b97c18bcbffe36283530a11701 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 01:09:36 +0800 Subject: [PATCH 101/479] change: TestCasePath Path field --- convert.go | 2 +- models.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/convert.go b/convert.go index c99d2bb7..67c8b174 100644 --- a/convert.go +++ b/convert.go @@ -132,7 +132,7 @@ func loadTestFile(path *TestCasePath) (*TestCase, error) { var tc *TCase var err error - casePath := path.string + casePath := path.Path ext := filepath.Ext(casePath) switch ext { case ".json": diff --git a/models.go b/models.go index 87e67f29..a8fc8a54 100644 --- a/models.go +++ b/models.go @@ -82,7 +82,7 @@ func (tc *TestCase) ToStruct() (*TestCase, error) { } type TestCasePath struct { - string + Path string } func (path *TestCasePath) ToStruct() (*TestCase, error) { From ce06ee09db779221db8fc38b3fe9fc5d0587f302 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 12:21:24 +0800 Subject: [PATCH 102/479] refactor: rename API --- README.md | 5 ++--- boomer.go | 2 +- convert_test.go | 2 +- examples/demo_test.go | 2 +- examples/extract_test.go | 4 ++-- examples/function_test.go | 2 +- examples/request_test.go | 2 +- examples/validate_test.go | 2 +- examples/variables_test.go | 8 ++++---- runner.go | 2 +- runner_test.go | 2 +- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 452722d2..084f4dfe 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ HttpBoomer is a golang implementation of [HttpRunner]. Ideally, HttpBoomer will - [x] Supports `variables`/`extract`/`validate`/`hooks` mechanisms to create extremely complex test scenarios. - [ ] Built-in integration of rich functions, and you can also use [`go plugin`][plugin] to create and call custom functions. - [x] Inherit all powerful features of [`Boomer`][Boomer] and [`locust`][locust], you can run `load test` without extra work. -- [ ] Use it as a `CLI tool` or as a `library` are both supported. +- [x] Use it as a `CLI tool` or as a `library` are both supported. ## Quick Start @@ -35,7 +35,6 @@ $ go get -u github.com/httprunner/httpboomer This is an example of HttpBoomer testcase. You can find more in the [`examples`][examples] directory. ```go - import ( "testing" @@ -86,7 +85,7 @@ func TestCaseDemo(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/boomer.go b/boomer.go index 5c054602..5680137c 100644 --- a/boomer.go +++ b/boomer.go @@ -6,7 +6,7 @@ import ( "github.com/myzhan/boomer" ) -func Run(testcases ...ITestCase) { +func Boom(testcases ...ITestCase) { NewBoomer().Run(testcases...) } diff --git a/convert_test.go b/convert_test.go index 381029f0..b4737f91 100644 --- a/convert_test.go +++ b/convert_test.go @@ -122,7 +122,7 @@ func TestLoadJSONAndRun(t *testing.T) { if !assert.NoError(t, err) { t.Fail() } - err = Test(t, testcase) + err = Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/examples/demo_test.go b/examples/demo_test.go index f421b3bd..8817a24d 100644 --- a/examples/demo_test.go +++ b/examples/demo_test.go @@ -50,7 +50,7 @@ func TestCaseDemo(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/examples/extract_test.go b/examples/extract_test.go index 5a7c05e0..f85368a2 100644 --- a/examples/extract_test.go +++ b/examples/extract_test.go @@ -37,7 +37,7 @@ func TestCaseExtractStepSingle(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } @@ -82,7 +82,7 @@ func TestCaseExtractStepAssociation(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/examples/function_test.go b/examples/function_test.go index e9398240..098847fd 100644 --- a/examples/function_test.go +++ b/examples/function_test.go @@ -40,7 +40,7 @@ func TestCaseCallFunction(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/examples/request_test.go b/examples/request_test.go index 3180244b..4a99f98f 100644 --- a/examples/request_test.go +++ b/examples/request_test.go @@ -57,7 +57,7 @@ func TestCaseBasicRequest(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/examples/validate_test.go b/examples/validate_test.go index ab6bbf96..daecd4fb 100644 --- a/examples/validate_test.go +++ b/examples/validate_test.go @@ -51,7 +51,7 @@ func TestCaseValidateStep(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/examples/variables_test.go b/examples/variables_test.go index 5ce4262c..c297daa2 100644 --- a/examples/variables_test.go +++ b/examples/variables_test.go @@ -33,7 +33,7 @@ func TestCaseConfigVariables(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } @@ -66,7 +66,7 @@ func TestCaseStepVariables(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } @@ -104,7 +104,7 @@ func TestCaseOverrideConfigVariables(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } @@ -151,7 +151,7 @@ func TestCaseParseVariables(t *testing.T) { }, } - err := httpboomer.Test(t, testcase) + err := httpboomer.Run(t, testcase) if err != nil { t.Fatalf("run testcase error: %v", err) } diff --git a/runner.go b/runner.go index 43a99005..d5151502 100644 --- a/runner.go +++ b/runner.go @@ -8,7 +8,7 @@ import ( "github.com/imroc/req" ) -func Test(t *testing.T, testcases ...ITestCase) error { +func Run(t *testing.T, testcases ...ITestCase) error { return NewRunner().WithTestingT(t).SetDebug(true).Run(testcases...) } diff --git a/runner_test.go b/runner_test.go index 7b366c43..b770a964 100644 --- a/runner_test.go +++ b/runner_test.go @@ -32,7 +32,7 @@ func TestHttpRunner(t *testing.T) { } testcase3 := &TestCasePath{demoTestCaseJSONPath} - err := Test(t, testcase1, testcase2, testcase3) + err := Run(t, testcase1, testcase2, testcase3) if err != nil { t.Fatalf("run testcase error: %v", err) } From d490842f25d21af5b49352e966385d806c3efe0d Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 14:49:38 +0800 Subject: [PATCH 103/479] feat: add run subcommand --- cmd/root.go | 13 ++++++------- cmd/run.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 cmd/run.go diff --git a/cmd/root.go b/cmd/root.go index 7d452891..f83813f7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,14 +12,13 @@ import ( // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "httpboomer", - Short: "httpboomer = httprunner + boomer", - Long: `HttpBoomer is a golang implementation of HttpRunner. -Ideally, HttpBoomer will be fully compatible with HttpRunner, including testcase format and usage. -What's more, HttpBoomer will integrate Boomer natively to be a better load generator for locust.`, + Short: "One-stop solution for HTTP(S) testing.", + Long: `HttpBoomer is the next generation for HttpRunner. Enjoy! ✨ 🚀 ✨ + +License: Apache-2.0 +Github: https://github.com/httprunner/httpboomer +Copyright 2021 debugtalk`, Version: httpboomer.VERSION, - // Uncomment the following line if your bare application - // has an action associated with it: - // Run: func(cmd *cobra.Command, args []string) {}, } // Execute adds all child commands to the root command and sets flags appropriately. diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 00000000..c20e4f3d --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "testing" + + "github.com/spf13/cobra" + + "github.com/httprunner/httpboomer" +) + +// runCmd represents the run command +var runCmd = &cobra.Command{ + Use: "run path...", + Short: "run API test", + Long: `run yaml/json testcase files`, + Example: ` $ httpboomer run demo.json # run specified json testcase file + $ httpboomer run demo.yaml # run specified yaml testcase file + $ httpboomer run examples/ # run testcases in specified folder`, + RunE: func(cmd *cobra.Command, args []string) error { + + // f, _ := cmd.Flags().GetBool("gen-html-report") + // fmt.Println(f) + + var paths []httpboomer.ITestCase + for _, arg := range args { + paths = append(paths, &httpboomer.TestCasePath{Path: arg}) + } + return httpboomer.Run(&testing.T{}, paths...) + }, +} + +func init() { + rootCmd.AddCommand(runCmd) + runCmd.Flags().BoolP("gen-html-report", "r", false, "Generate HTML report") +} From e946799b7ca2980cabc62e67f5d0abc2cbe702c2 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 15:02:00 +0800 Subject: [PATCH 104/479] change: Runner client --- runner.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runner.go b/runner.go index d5151502..8ffdbe35 100644 --- a/runner.go +++ b/runner.go @@ -16,14 +16,14 @@ func NewRunner() *Runner { return &Runner{ t: &testing.T{}, debug: false, // default to turn off debug - Client: req.New(), + client: req.New(), } } type Runner struct { t *testing.T debug bool - Client *req.Req + client *req.Req } func (r *Runner) WithTestingT(t *testing.T) *Runner { @@ -157,7 +157,7 @@ func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { // do request action req.Debug = r.debug - resp, err := r.Client.Do(string(step.Request.Method), step.Request.URL, v...) + resp, err := r.client.Do(string(step.Request.Method), step.Request.URL, v...) if err != nil { return } From 18f3dd9f5c8edeb35759e9ae14935d9e4462b1d2 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 10 Oct 2021 16:35:25 +0800 Subject: [PATCH 105/479] change: update flow chart --- docs/flow.jpg | Bin 151998 -> 227535 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/flow.jpg b/docs/flow.jpg index 846f1b9404951be0ef0bdcc803968a5173101805..72f38f14b05bfa5d5caf4f975ca147f1d7a640bf 100644 GIT binary patch literal 227535 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