tes-request 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.travis.yml +22 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +79 -0
- data/Rakefile +6 -0
- data/bin/tes-ci-slicer +26 -0
- data/lib/hash.rb +24 -0
- data/lib/tes/request.rb +3 -0
- data/lib/tes/request/ask.rb +126 -0
- data/lib/tes/request/client.rb +72 -0
- data/lib/tes/request/expression.rb +116 -0
- data/lib/tes/request/profile.rb +282 -0
- data/lib/tes/request/rake_task.rb +90 -0
- data/lib/tes/request/rspec/ci_slicer.rb +82 -0
- data/lib/tes/request/rspec/distribute.rb +214 -0
- data/lib/tes/request/rspec/function.rb +48 -0
- data/lib/tes/request/rspec/profile_parser.rb +132 -0
- data/lib/tes/request/version.rb +5 -0
- data/tes-request.gemspec +40 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 142a8a4477f2f7b6eae5d87bcd700921033422dd
|
4
|
+
data.tar.gz: 04c665604ac024a00842ac3402db59913762bdd8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2f17b79139684259ebe49a1addce1b52468ae661254e927a983fe254204609a7fbae6dd74b32a20661d361c1e55728f782fd0563b7edfed6bfbb9774efec095b
|
7
|
+
data.tar.gz: a4475a7f6700cdd5c5fb39a1f7ba25944effbf855f45d284e40d03edf364bb3cc818e2d46340d1e7e3e15fdccef354af869239be799d59811652918b76996cf1
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.10
|
6
|
+
- 2.2.7
|
7
|
+
- 2.3.3
|
8
|
+
- 2.3.4
|
9
|
+
- 2.4.0
|
10
|
+
- 2.4.1
|
11
|
+
before_install: gem install bundler
|
12
|
+
install: bundle
|
13
|
+
script:
|
14
|
+
- bundle exec rspec
|
15
|
+
before_deploy: bundle exec rake build
|
16
|
+
deploy:
|
17
|
+
provider: rubygems
|
18
|
+
api-key:
|
19
|
+
secure: VILjurTJb9jwpQE+RVQ93DZjqR2nVrjkuh8TAIEiaKjGvLdFqWN5QBQ8BRRU+7nOLowyp0YB+/TYRd0oNslz+BZDEgisLWmzjvDXdJQtZuk/2TnslumTctF3uz9ankwESaBrembgC/BPye/Xpio8oaXx9huOGYqIKD8cKX1U8/lFZKCfRIHCYo+7iETBdifBp6e3jj3ABGrCeurcvC0GUPlhcnYrYTjvXsmLVCxQCWyQZ7KfW/9dnqI2ssfeTLgg6VGO5uCR+dvKaiZis9YKICfrmaKSsZBQrHXr49b7jnizTkAVbg/JG9LG/Y+Xs8LeF/8Rx270EEP0TKnwpzWzzOn2yV6D7TeFnzHq0xn+0xoehtcL0jNv7rAGgCzkS4CrR0fWdAITZylhHYBVx4aTLhJ3SEVMxexuyych6JhSWVDvq7ouYIyBKhDtHKTpTQjznsqAGVAxWXjRlbd/GOzwvwqB32pgnqfHQX7zMMOGQk0t4nHMVIieSfVetOXXUAERjiMAajWJTwI0/exeodzD0vVF9kfpOB1RLtQgSYxOyD2nu8TSloxiF5ZLzypQw1wRg0ctzpOUuz/tgIHtyK9gJtY1XYV4mgXlEdtteM6GdYoLj8AKjaOpi9evEM+QnGYsI4xoLq4P6XgDjyBpzFoUkBO8E7u0nPQRsB86yqLINLI=
|
20
|
+
on:
|
21
|
+
tags: true
|
22
|
+
rvm: 2.4.1
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at 51085@sangfor.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 wuhuizuo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Tes::Request
|
2
|
+
|
3
|
+
TES(Test Env Service) request profile struct and manager lib
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/wuhuizuo/tes-request.svg)](https://travis-ci.org/wuhuizuo/tes-request)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
source "https://rubygems.org"
|
12
|
+
gem 'tes-request'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install tes-request
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
Given you project root path: `/project/root/path`, and there has file `.ci.yml`, file contents as follow:
|
25
|
+
|
26
|
+
```yml
|
27
|
+
# file .ci.yml
|
28
|
+
---
|
29
|
+
with_one_exclude_pattern:
|
30
|
+
spec:
|
31
|
+
pattern:
|
32
|
+
- spec/a/**/*_spec.rb
|
33
|
+
- spec/b/**/*_spec.rb
|
34
|
+
exclude_pattern: spec/**/exclude_*_spec.rb
|
35
|
+
```
|
36
|
+
|
37
|
+
|
38
|
+
And `spec/**/*_spec.rb` has content:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# file spec/**/*_spec.rb all have the content part in the head:
|
42
|
+
# @env begin
|
43
|
+
# *1:type=cluster,cfg.member.size>=2,cfg.vs_enable=1
|
44
|
+
# &1.cfg.member
|
45
|
+
# type=storage_iscsi
|
46
|
+
# type=storage_iscsi
|
47
|
+
# @distribution -standalone
|
48
|
+
# @end
|
49
|
+
|
50
|
+
describe 'abc' do
|
51
|
+
# more ...
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
Then you need to slice all spec script to several paralleled jobs.
|
56
|
+
|
57
|
+
**Step:**
|
58
|
+
|
59
|
+
1. install this gem
|
60
|
+
2. run cmds like:
|
61
|
+
|
62
|
+
```bash
|
63
|
+
tes-ci-slicer properties apps/test_project func_test 16 apps/test_project/res_attr_add_map.json
|
64
|
+
```
|
65
|
+
|
66
|
+
## Development
|
67
|
+
|
68
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
69
|
+
|
70
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
71
|
+
|
72
|
+
## Contributing
|
73
|
+
|
74
|
+
Bug reports and pull requests are welcome on GitHub at http://200.200.0.36/vtt/tes-request. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
75
|
+
|
76
|
+
## License
|
77
|
+
|
78
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
79
|
+
|
data/Rakefile
ADDED
data/bin/tes-ci-slicer
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'tes/request/rspec/ci_slicer'
|
4
|
+
|
5
|
+
usage = <<EOF
|
6
|
+
Usage: % #{$0} {#{Tes::Request::RSpec::CiSlicer::SUPPORT_FILE_TYPES.join('|')}} {project_dir} {job_type} {job_min_pieces} [res_addition_attr_map_json_file_path]
|
7
|
+
|
8
|
+
Example:
|
9
|
+
#{$0} json apps/test_project ci_merge_test 4
|
10
|
+
#{$0} properties apps/test_project func_test 16 apps/test_project/res_attr_add_map.json
|
11
|
+
EOF
|
12
|
+
|
13
|
+
if ARGV.size < 4
|
14
|
+
warn usage
|
15
|
+
exit 2
|
16
|
+
end
|
17
|
+
|
18
|
+
cfg_type = ARGV.shift.to_sym
|
19
|
+
project_dir = ARGV.shift
|
20
|
+
ci_job_type = ARGV.shift
|
21
|
+
job_piece_count = ARGV.shift
|
22
|
+
res_addition_attr_map_json_file = ARGV.shift
|
23
|
+
Tes::Request::RSpec::CiSlicer.
|
24
|
+
new(cfg_type, project_dir, res_addition_attr_map_json_file).
|
25
|
+
run(ci_job_type, job_piece_count.to_i)
|
26
|
+
|
data/lib/hash.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class Hash
|
2
|
+
# Magic predicates. For instance:
|
3
|
+
#
|
4
|
+
# options.force? # => !!options['force']
|
5
|
+
# options.shebang # => "/usr/lib/local/ruby"
|
6
|
+
# options.test_framework?(:rspec) # => options[:test_framework] == :rspec
|
7
|
+
#
|
8
|
+
def method_missing(method, *args, &block)
|
9
|
+
judge_mt = method.to_s.match(/^(\w+)\?$/)
|
10
|
+
|
11
|
+
return (self[method] || self[method.to_s]) unless judge_mt
|
12
|
+
value = self[judge_mt[1]] || self[judge_mt[1].to_sym]
|
13
|
+
args.empty? ? !!value : (value == args.first)
|
14
|
+
end
|
15
|
+
|
16
|
+
# 串联获取内部嵌套hash值
|
17
|
+
# @param [String] chain_str 嵌套取值表达式
|
18
|
+
# @return [Object]
|
19
|
+
def get_by_chain(chain_str)
|
20
|
+
chains = chain_str.split('.')
|
21
|
+
chains.keep_if { |k| k =~ /.+/ }
|
22
|
+
chains.inject(self) { |t, p| t && t.send(p) }
|
23
|
+
end
|
24
|
+
end
|
data/lib/tes/request.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'set'
|
2
|
+
require_relative 'expression'
|
3
|
+
|
4
|
+
module Tes
|
5
|
+
module Request
|
6
|
+
class Ask
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
# @param [String] ask_str 单个资源的请求要求字符串
|
10
|
+
def initialize(ask_str)
|
11
|
+
snippets = ask_str.strip.sub(/^\*\d+:/, '').sub(/^&\d+\./, '').split(/\s*,\s*/)
|
12
|
+
expressions = snippets.map { |s| Expression.new(s) }
|
13
|
+
@data = expressions.inject({}) { |t, e| t.merge(e.data[:left_exp] => e) }
|
14
|
+
# @reference 是表达式列表
|
15
|
+
@reference = []
|
16
|
+
@greedy = false
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :data, :reference
|
20
|
+
attr_accessor :greedy
|
21
|
+
|
22
|
+
# 计算数据是否满足要求
|
23
|
+
# @param [Object] data
|
24
|
+
def match?(data)
|
25
|
+
@data.all? { |_n, exp| exp.match?(data) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def <=>(other)
|
29
|
+
exp_compare_lab = -> (a, b) do
|
30
|
+
if a.nil? and b.nil?
|
31
|
+
0
|
32
|
+
elsif a.nil?
|
33
|
+
-1
|
34
|
+
elsif b.nil?
|
35
|
+
1
|
36
|
+
else
|
37
|
+
a <=> b
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
ret = if @data == other.data
|
42
|
+
0
|
43
|
+
elsif self.data > other.data
|
44
|
+
1
|
45
|
+
elsif self.data < other.data
|
46
|
+
-1
|
47
|
+
else
|
48
|
+
a_to_b_results = @data.map { |n, e| exp_compare_lab.call(e, other.data[n]) }
|
49
|
+
if a_to_b_results.all? { |ret| ret and ret <= 0 }
|
50
|
+
-1
|
51
|
+
elsif a_to_b_results.all? { |ret| ret and ret >= 0 }
|
52
|
+
# 这时必须要求other的表达式全部能被self覆盖.
|
53
|
+
(other.data.keys - @data.keys).size > 0 ? nil : 1
|
54
|
+
else
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
case [greedy, other.greedy]
|
59
|
+
when [true, false]
|
60
|
+
return ret unless ret
|
61
|
+
ret >=0 ? 1 : nil
|
62
|
+
when [false, true]
|
63
|
+
return ret unless ret
|
64
|
+
ret <=0 ? -1 : nil
|
65
|
+
else
|
66
|
+
ret
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# 相对other要求多的和严格的
|
71
|
+
# @return [Ask]
|
72
|
+
def -(other)
|
73
|
+
diff_data = @data.reject do |n, e|
|
74
|
+
o_e = other.data[n]
|
75
|
+
o_e && e <= o_e
|
76
|
+
end
|
77
|
+
|
78
|
+
diff_ask = self.new('')
|
79
|
+
diff_ask.data.merge!(diff_data)
|
80
|
+
diff_ask
|
81
|
+
end
|
82
|
+
|
83
|
+
# 合并叠加对资源的请求
|
84
|
+
# @return [Ask]
|
85
|
+
def +(other)
|
86
|
+
if self == other
|
87
|
+
self
|
88
|
+
elsif self >=other
|
89
|
+
self
|
90
|
+
elsif self <other
|
91
|
+
other
|
92
|
+
else
|
93
|
+
raise('不能合并叠加') unless merge_able?(other)
|
94
|
+
|
95
|
+
self_gt_other = self.-(other)
|
96
|
+
other_gt_self = other.-(self)
|
97
|
+
|
98
|
+
ask1 = self.new('')
|
99
|
+
ask2 = self.new('')
|
100
|
+
ask1.data.merge!(self.data)
|
101
|
+
ask1.data.merge!(other_gt_self)
|
102
|
+
ask2.data.merge!(other.data)
|
103
|
+
ask2.data.merge!(self_gt_other)
|
104
|
+
ask1 >= ask2 ? ask1 : ask2
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def merge_able?(other)
|
109
|
+
if self.<=> other
|
110
|
+
true
|
111
|
+
else
|
112
|
+
self_gt_other = self.-(other)
|
113
|
+
other_gt_self = other.-(self)
|
114
|
+
conflict_exp_keys = self_gt_other.data.keys & other_gt_self.data.keys
|
115
|
+
conflict_exp_keys.empty?
|
116
|
+
end
|
117
|
+
rescue ArgumentError
|
118
|
+
false
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_s
|
122
|
+
@data.values.map(&:to_s).join(',')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Tes
|
4
|
+
module Request
|
5
|
+
class Client
|
6
|
+
# Construction
|
7
|
+
# @param [HTTPClient] driver the http session instance
|
8
|
+
def initialize(driver)
|
9
|
+
@driver = driver
|
10
|
+
http_methods = [:get, :post, :put, :delete, :head]
|
11
|
+
http_methods.each do |m|
|
12
|
+
unless @driver.respond_to?(m)
|
13
|
+
fail_msg = "invalid arg [driver]: not support method: #{m}"
|
14
|
+
raise(ArgumentError, fail_msg)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :driver
|
20
|
+
|
21
|
+
# @param [String] user
|
22
|
+
# @param [Array<String>] asks
|
23
|
+
# @return [Hash]
|
24
|
+
def request_env(user, asks)
|
25
|
+
res = @driver.post('/env', body: {user: user, ask: asks.join("\n")}.to_json)
|
26
|
+
ret = parse_res res
|
27
|
+
|
28
|
+
if ret.is_a?(Hash) and
|
29
|
+
ret[:success] and
|
30
|
+
ret[:data].is_a?(Hash) and
|
31
|
+
ret[:data][:res].is_a?(Hash)
|
32
|
+
res_hash = ret[:data][:res]
|
33
|
+
res_hash.keys.each { |k| res_hash[k.to_s] = res_hash.delete(k) }
|
34
|
+
ret[:data][:res] = res_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
ret
|
38
|
+
end
|
39
|
+
|
40
|
+
def request_res(id, user)
|
41
|
+
res = @driver.post("/res/#{id}/lock", body: {user: user})
|
42
|
+
parse_res res
|
43
|
+
end
|
44
|
+
|
45
|
+
def release_res(id, user)
|
46
|
+
res = @driver.post("/res/#{id}/release", body: {user: user})
|
47
|
+
parse_res res
|
48
|
+
end
|
49
|
+
|
50
|
+
def release_all_res(user)
|
51
|
+
res_list = parse_res @driver.get('/res')
|
52
|
+
res_list[:data].select { |_k, c| c[:users] && c[:users].include?(user) }.each do |id, _c|
|
53
|
+
release_res(id, user)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def parse_res(res)
|
59
|
+
unless res.ok? or res.redirect?
|
60
|
+
fail_msg = "[#{res.status}]:#{res.body}"
|
61
|
+
raise(fail_msg)
|
62
|
+
else
|
63
|
+
res_body = res.body
|
64
|
+
if res.http_header['Content-Type'].any? { |h| h =~ /application\/json/i }
|
65
|
+
res_body = JSON.parse(res_body, :symbolize_names => true) rescue res_body
|
66
|
+
end
|
67
|
+
res_body
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|