wsdirector-cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.