wsdirector-cli 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2baebc5541a7fa47137932138d2605c7d7c7666542ef1967a056b884660d06df
4
- data.tar.gz: b1179e52ccc50773d88fae780ce58d6a7038b45ad3ae29ac52427f594301de05
3
+ metadata.gz: 28d6654bce4665a9d0c1434c33ba359e31adb6680c51aafc3ccb1c5a91b32d56
4
+ data.tar.gz: 6e9c3fd2cfe938ba5f8a5f3e3519204d93cd56d428fed93be89b4056e53f004e
5
5
  SHA512:
6
- metadata.gz: 79a5b74d539c88e58932f67e91730652a13f3051d753cd14539c5b55a8a410134e9b16bca26debff21aa51db7943a0263bb859b1ff68c0444d2e7ba0d617312c
7
- data.tar.gz: 505976c437539b628b9325a0f3b7f423e55c4df33f29fc00476aaf2bbaf9f5e786cb6dc5bca9fb1cb10e6aca92dec9265e6a8fbfde704ecfc5be2734e6b4ec73
6
+ metadata.gz: 1ce990e199cd5562bb03760a8678349b74f549014cf997c821b3ad6f4518e7e45e349a26e89fcfb22762f2a775d57604bf84c35511dd5294097590e14389c47a
7
+ data.tar.gz: 0f4287fedbc4f0488e2a19fe59aa6de276da620345e9024bcd7baa5f04a473c1a5fd36556f9ba49f4a82575101934737df6d1d470069c85fa751436fbea16c0f
@@ -2,6 +2,33 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.4.0 (2020-08-24)
6
+
7
+ - **Drop Ruby 2.4 support**. ([@palkan][])
8
+
9
+ - Add sampling support. ([@palkan][])
10
+
11
+ You can specify a `sample` option for a step to only run this step by specified number of clients from the group:
12
+
13
+ ```yml
14
+ - perform:
15
+ sample: ":scale / 2"
16
+ channel: "chat"
17
+ params:
18
+ id: 2
19
+ action: "speak"
20
+ data:
21
+ message: "Hello!"
22
+ ```
23
+
24
+ Useful in combination with `:scale`.
25
+
26
+ **NOTE:** Sample size is always greater or equal to 1.
27
+
28
+ - Add ERB support. ([@palkan][])
29
+
30
+ Now you can, for example, access `ENV` from scenarios.
31
+
5
32
  ## 0.3.0 (2018-05-02)
6
33
 
7
34
  - Add `debug` action and options. ([@palkan][])
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](http://cultofmartians.com/tasks/websocket-director.html)
2
- [![Gem Version](https://badge.fury.io/rb/wsdirector-cli.svg)](https://rubygems.org/gems/wsdirector-cli) [![Build Status](https://travis-ci.org/palkan/wsdirector.svg?branch=master)](https://travis-ci.org/palkan/wsdirector) [![CircleCI](https://circleci.com/gh/palkan/wsdirector.svg?style=svg)](https://circleci.com/gh/palkan/wsdirector)
2
+ [![Gem Version](https://badge.fury.io/rb/wsdirector-cli.svg)](https://rubygems.org/gems/wsdirector-cli)
3
+ [![Build](https://github.com/palkan/wsdirector/workflows/Build/badge.svg)](https://github.com/palkan/wsdirector/actions)
3
4
 
4
5
  # WebSocket Director
5
6
 
@@ -10,7 +11,7 @@ Suitable for testing any websocket server implementation, like [Action Cable](ht
10
11
  ## Installation
11
12
 
12
13
  ```bash
13
- $ gem install wsdirector-cli
14
+ gem install wsdirector-cli
14
15
  ```
15
16
 
16
17
  ## Usage
@@ -61,7 +62,6 @@ You can create more complex scenarios with multiple client groups:
61
62
 
62
63
  Run with scale factor:
63
64
 
64
-
65
65
  ```bash
66
66
  wsdirector script.yml ws://websocket.server:9876 -s 10
67
67
 
@@ -153,7 +153,6 @@ wsdirector -i '{"receive": "hello"}' localhost:9898/ws
153
153
 
154
154
  Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/wsdirector.
155
155
 
156
-
157
156
  ## License
158
157
 
159
158
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -9,7 +9,8 @@ require "wsdirector/runner"
9
9
  module WSDirector
10
10
  # Command line interface for WsDirector
11
11
  class CLI
12
- def initialize; end
12
+ def initialize
13
+ end
13
14
 
14
15
  def run
15
16
  parse_args!
@@ -31,7 +32,7 @@ module WSDirector
31
32
  exit 1
32
33
  end
33
34
  rescue Error => e
34
- STDERR.puts e.message
35
+ warn e.message
35
36
  exit 1
36
37
  end
37
38
 
@@ -55,7 +56,7 @@ module WSDirector
55
56
  end
56
57
 
57
58
  opts.on("-v", "--version", "Print versin") do
58
- STDOUT.puts WSDirector::VERSION
59
+ $stdout.puts WSDirector::VERSION
59
60
  exit 0
60
61
  end
61
62
  end
@@ -4,7 +4,7 @@ module WSDirector
4
4
  # WSDirector configuration
5
5
  class Configuration
6
6
  attr_accessor :ws_url, :scenario_path, :colorize, :scale,
7
- :sync_timeout
7
+ :sync_timeout
8
8
 
9
9
  def initialize
10
10
  reset!
@@ -9,10 +9,10 @@ module WSDirector
9
9
  def deep_dup
10
10
  each_with_object(dup) do |(key, value), hash|
11
11
  hash[key] = if value.is_a?(::Hash) || value.is_a?(::Array)
12
- value.deep_dup
13
- else
14
- value
15
- end
12
+ value.deep_dup
13
+ else
14
+ value
15
+ end
16
16
  end
17
17
  end
18
18
  end
@@ -4,7 +4,7 @@ module WSDirector
4
4
  module Protocols
5
5
  # ActionCable protocol
6
6
  class ActionCable < Base
7
- WELCOME_MSG = { type: "welcome" }.to_json
7
+ WELCOME_MSG = {type: "welcome"}.to_json
8
8
  PING_IGNORE = /['"]type['"]:\s*['"]ping['"]/
9
9
 
10
10
  # Add ping ignore and make sure that we receive Welcome message
@@ -19,14 +19,14 @@ module WSDirector
19
19
  def subscribe(step)
20
20
  identifier = extract_identifier(step)
21
21
 
22
- client.send({ command: "subscribe", identifier: identifier }.to_json)
22
+ client.send({command: "subscribe", identifier: identifier}.to_json)
23
23
 
24
24
  begin
25
25
  receive(
26
- "data" => { "type" => "confirm_subscription", "identifier" => identifier }
26
+ "data" => {"type" => "confirm_subscription", "identifier" => identifier}
27
27
  )
28
28
  rescue UnmatchedExpectationError => e
29
- raise unless e.message =~ /reject_subscription/
29
+ raise unless /reject_subscription/.match?(e.message)
30
30
  raise UnmatchedExpectationError, "Subscription rejected to #{identifier}"
31
31
  end
32
32
  end
@@ -39,7 +39,7 @@ module WSDirector
39
39
 
40
40
  data = step.fetch("data", {}).merge(action: action).to_json
41
41
 
42
- client.send({ command: "message", data: data, identifier: identifier }.to_json)
42
+ client.send({command: "message", data: data, identifier: identifier}.to_json)
43
43
  end
44
44
 
45
45
  def receive(step)
@@ -47,7 +47,7 @@ module WSDirector
47
47
 
48
48
  identifier = extract_identifier(step)
49
49
  message = step.fetch("data", {})
50
- super("data" => { "identifier" => identifier, "message" => message })
50
+ super("data" => {"identifier" => identifier, "message" => message})
51
51
  end
52
52
 
53
53
  def receive_all(step)
@@ -58,7 +58,7 @@ module WSDirector
58
58
  messages.each do |msg|
59
59
  next unless msg.key?("channel")
60
60
  identifier = extract_identifier(msg)
61
- msg["data"] = { "identifier" => identifier, "message" => msg["data"] }
61
+ msg["data"] = {"identifier" => identifier, "message" => msg["data"]}
62
62
  end
63
63
 
64
64
  super
@@ -19,6 +19,9 @@ module WSDirector
19
19
  def handle_step(step)
20
20
  type = step.delete("type")
21
21
  raise Error, "Unknown step: #{type}" unless respond_to?(type)
22
+
23
+ return unless task.sampled?(step)
24
+
22
25
  public_send(type, step)
23
26
  end
24
27
 
@@ -83,8 +86,8 @@ module WSDirector
83
86
  end
84
87
  rescue ThreadError
85
88
  raise NoMessageError,
86
- "Expected to receive #{total_expected} messages " \
87
- "but received only #{total_received}"
89
+ "Expected to receive #{total_expected} messages " \
90
+ "but received only #{total_received}"
88
91
  end
89
92
  # rubocop: enable Metrics/CyclomaticComplexity
90
93
 
@@ -11,6 +11,9 @@ module WSDirector
11
11
 
12
12
  @all = Concurrent::AtomicFixnum.new(0)
13
13
  @failures = Concurrent::AtomicFixnum.new(0)
14
+
15
+ @sampling_mutex = Mutex.new
16
+ @sampling_counter = Hash.new { |h, k| h[k] = 0 }
14
17
  end
15
18
 
16
19
  # Called when client successfully finished it's work
@@ -37,8 +40,17 @@ module WSDirector
37
40
  failures.value
38
41
  end
39
42
 
43
+ def track_sample(id, max)
44
+ sampling_mutex.synchronize do
45
+ return false if sampling_counter[id] >= max
46
+
47
+ sampling_counter[id] += 1
48
+ true
49
+ end
50
+ end
51
+
40
52
  private
41
53
 
42
- attr_reader :all, :success, :failures
54
+ attr_reader :all, :success, :failures, :sampling_counter, :sampling_mutex
43
55
  end
44
56
  end
@@ -28,7 +28,7 @@ module WSDirector
28
28
  Array.new(client.fetch("multiplier")) do
29
29
  Thread.new do
30
30
  Task.new(client.deep_dup, global_holder: global_holder, result: result)
31
- .run
31
+ .run
32
32
  end
33
33
  end
34
34
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "erb"
4
+
3
5
  module WSDirector
4
6
  # Read and parse YAML scenario
5
7
  class ScenarioReader
@@ -7,32 +9,35 @@ module WSDirector
7
9
  include WSDirector::Utils
8
10
 
9
11
  def parse(file_path)
10
- contents = YAML.load_file(file_path)
12
+ contents = ::YAML.load(ERB.new(File.read(file_path)).result) # rubocop:disable Security/YAMLLoad
11
13
 
12
14
  if contents.first.key?("client")
13
15
  parse_multiple_scenarios(contents)
14
16
  else
15
- { "total" => 1, "clients" => [parse_simple_scenario(contents)] }
17
+ {"total" => 1, "clients" => [parse_simple_scenario(contents)]}
16
18
  end
17
19
  end
18
20
 
19
21
  private
20
22
 
21
23
  def handle_steps(steps)
22
- steps.flat_map do |step|
24
+ steps.flat_map.with_index do |step, id|
23
25
  if step.is_a?(Hash)
24
26
  type, data = step.to_a.first
27
+
28
+ data["sample"] = [1, parse_multiplier(data["sample"])].max if data["sample"]
29
+
25
30
  multiplier = parse_multiplier(data.delete("multiplier") || "1")
26
- Array.new(multiplier) { { "type" => type }.merge(data) }
31
+ Array.new(multiplier) { {"type" => type, "id" => id}.merge(data) }
27
32
  else
28
- { "type" => step }
33
+ {"type" => step, "id" => id}
29
34
  end
30
35
  end
31
36
  end
32
37
 
33
38
  def parse_simple_scenario(
34
- steps,
35
- multiplier: 1, name: "default", ignore: nil, protocol: "base"
39
+ steps,
40
+ multiplier: 1, name: "default", ignore: nil, protocol: "base"
36
41
  )
37
42
  {
38
43
  "multiplier" => multiplier,
@@ -59,7 +64,7 @@ module WSDirector
59
64
  protocol: client.fetch("protocol", "base")
60
65
  )
61
66
  end
62
- { "total" => total_count, "clients" => clients }
67
+ {"total" => total_count, "clients" => clients}
63
68
  end
64
69
 
65
70
  def parse_ingore(str)
@@ -28,6 +28,14 @@ module WSDirector
28
28
  result.failed(e.message)
29
29
  end
30
30
 
31
+ def sampled?(step)
32
+ return true unless step["sample"]
33
+
34
+ id, max = step["id"], step["sample"]
35
+
36
+ result.track_sample(id, max)
37
+ end
38
+
31
39
  private
32
40
 
33
41
  attr_reader :steps, :result, :protocol
@@ -2,12 +2,12 @@
2
2
 
3
3
  module WSDirector
4
4
  module Utils # :nodoc:
5
- MULTIPLIER_FORMAT = /^[-+*\\\d ]+$/
5
+ MULTIPLIER_FORMAT = /^[-+*\/\\\d ]+$/
6
6
 
7
7
  def parse_multiplier(str)
8
8
  prepared = str.to_s.gsub(":scale", WSDirector.config.scale.to_s)
9
9
  raise WSDirector::Error, "Unknown multiplier format: #{str}" unless
10
- prepared =~ MULTIPLIER_FORMAT
10
+ MULTIPLIER_FORMAT.match?(prepared)
11
11
 
12
12
  eval(prepared) # rubocop:disable Security/Eval
13
13
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WSDirector
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wsdirector-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kirill Arkhipov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-05-02 00:00:00.000000000 Z
13
+ date: 2020-08-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: websocket-client-simple
@@ -114,28 +114,28 @@ dependencies:
114
114
  name: bundler
115
115
  requirement: !ruby/object:Gem::Requirement
116
116
  requirements:
117
- - - "~>"
117
+ - - ">="
118
118
  - !ruby/object:Gem::Version
119
- version: '1.13'
119
+ version: '1.16'
120
120
  type: :development
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
123
123
  requirements:
124
- - - "~>"
124
+ - - ">="
125
125
  - !ruby/object:Gem::Version
126
- version: '1.13'
126
+ version: '1.16'
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: rake
129
129
  requirement: !ruby/object:Gem::Requirement
130
130
  requirements:
131
- - - "~>"
131
+ - - ">="
132
132
  - !ruby/object:Gem::Version
133
133
  version: '10.0'
134
134
  type: :development
135
135
  prerelease: false
136
136
  version_requirements: !ruby/object:Gem::Requirement
137
137
  requirements:
138
- - - "~>"
138
+ - - ">="
139
139
  - !ruby/object:Gem::Version
140
140
  version: '10.0'
141
141
  - !ruby/object:Gem::Dependency
@@ -166,20 +166,6 @@ dependencies:
166
166
  - - "~>"
167
167
  - !ruby/object:Gem::Version
168
168
  version: '5.9'
169
- - !ruby/object:Gem::Dependency
170
- name: rubocop
171
- requirement: !ruby/object:Gem::Requirement
172
- requirements:
173
- - - "~>"
174
- - !ruby/object:Gem::Version
175
- version: '0.50'
176
- type: :development
177
- prerelease: false
178
- version_requirements: !ruby/object:Gem::Requirement
179
- requirements:
180
- - - "~>"
181
- - !ruby/object:Gem::Version
182
- version: '0.50'
183
169
  description: Command line tool for testing websocket servers using scenarios.
184
170
  email:
185
171
  - kirillvs@mail.ru
@@ -224,15 +210,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
224
210
  requirements:
225
211
  - - ">="
226
212
  - !ruby/object:Gem::Version
227
- version: 2.4.0
213
+ version: 2.5.0
228
214
  required_rubygems_version: !ruby/object:Gem::Requirement
229
215
  requirements:
230
216
  - - ">="
231
217
  - !ruby/object:Gem::Version
232
218
  version: '0'
233
219
  requirements: []
234
- rubyforge_project:
235
- rubygems_version: 2.7.4
220
+ rubygems_version: 3.0.6
236
221
  signing_key:
237
222
  specification_version: 4
238
223
  summary: Command line tool for testing websocket servers using scenarios.