anyt 0.8.5 → 1.0.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +4 -1
  4. data/lib/anyt/cli.rb +32 -6
  5. data/lib/anyt/client.rb +12 -7
  6. data/lib/anyt/command.rb +31 -18
  7. data/lib/anyt/config.rb +15 -7
  8. data/lib/anyt/dummy/application.rb +8 -6
  9. data/lib/anyt/dummy/config.ru +4 -0
  10. data/lib/anyt/dummy/tmp/development_secret.txt +1 -0
  11. data/lib/anyt/ext/minitest.rb +23 -6
  12. data/lib/anyt/remote_control.rb +33 -0
  13. data/lib/anyt/rpc.rb +4 -6
  14. data/lib/anyt/tests.rb +5 -7
  15. data/lib/anyt/tests/core/ping_test.rb +2 -2
  16. data/lib/anyt/tests/core/welcome_test.rb +2 -2
  17. data/lib/anyt/tests/features/channel_state_test.rb +47 -0
  18. data/lib/anyt/tests/features/remote_disconnect_test.rb +30 -0
  19. data/lib/anyt/tests/features/server_restart_test.rb +28 -0
  20. data/lib/anyt/tests/request/channel_test.rb +28 -0
  21. data/lib/anyt/tests/request/connection_test.rb +23 -21
  22. data/lib/anyt/tests/request/disconnect_reasons_test.rb +23 -0
  23. data/lib/anyt/tests/request/disconnection_test.rb +50 -32
  24. data/lib/anyt/tests/streams/broadcast_test.rb +13 -13
  25. data/lib/anyt/tests/streams/multiple_clients_test.rb +10 -10
  26. data/lib/anyt/tests/streams/multiple_test.rb +10 -10
  27. data/lib/anyt/tests/streams/single_test.rb +10 -10
  28. data/lib/anyt/tests/streams/stop_test.rb +57 -0
  29. data/lib/anyt/tests/subscriptions/ack_test.rb +9 -11
  30. data/lib/anyt/tests/subscriptions/params_test.rb +6 -7
  31. data/lib/anyt/tests/subscriptions/perform_test.rb +16 -18
  32. data/lib/anyt/tests/subscriptions/transmissions_test.rb +6 -7
  33. data/lib/anyt/version.rb +1 -1
  34. metadata +26 -68
  35. data/.gitignore +0 -43
  36. data/.rubocop.yml +0 -80
  37. data/Gemfile +0 -6
  38. data/LICENSE.txt +0 -21
  39. data/Makefile +0 -5
  40. data/anyt.gemspec +0 -42
  41. data/circle.yml +0 -14
  42. data/etc/tests/channel_broadcast_test.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b45eb7502a2f6155788228c055c05232ad0ab7baf47509e70cb783479047eb07
4
- data.tar.gz: 62266da53d1c2f5a58376fd64b340b7e6b94dac05af6ca16c1580f84723bf177
3
+ metadata.gz: 44aafe91585ca0592acd050ba53ee3d71e3ecb908dc00889af176532a39bdbcf
4
+ data.tar.gz: ec28d88e061d67b4481be7acd479376bd6ada9522727d0734316af1a20d4564e
5
5
  SHA512:
6
- metadata.gz: 764bf51ba64a579c4906633cf19d46d156e143b66a642c96a285e6fb862cc34357da75f8c3ec2627afc4cedc3d72ca38d95a69990b53d42fbbfc9d8280b0521b
7
- data.tar.gz: 57611bdee9877c384c651c8e45c928ecc85af77c888cb985c794fe9903735aed4660d3619afc5dd8ff0d191cf8509327baeeda65bfff9a8d73c33357607103c7
6
+ metadata.gz: 0f0063256b7a36de58cae43b69767d4eb07c39624cd94604c88eb925c126f921d1b790a69c8fca49a7175217095e61bd2a88af0c7e5e17cdb25f951583c0309f
7
+ data.tar.gz: a197e0307571c9c601573787911b6a0111dfc1dd1e838d5f7853b20b76972b8c841029b1dac522669bdad0e9689640234685b55cdbf3a3fd685ed182ce8a000c
@@ -1,4 +1,4 @@
1
- Copyright 2016 palkan
1
+ Copyright 2016-2020 palkan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
- [![Gem Version](https://badge.fury.io/rb/anyt.svg)](https://rubygems.org/gems/anyt) [![Circle CI](https://circleci.com/gh/anycable/anycablebility/tree/master.svg?style=svg)](https://circleci.com/gh/anycable/anycablebility/tree/master) [![Gitter](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/anycable/anycablebility)
1
+ [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/anycable-conformance-tool.html#task)
2
+ [![Gem Version](https://badge.fury.io/rb/anyt.svg)](https://rubygems.org/gems/anyt)
3
+ ![Test](https://github.com/anycable/anyt/workflows/Test/badge.svg)
4
+ [![Gitter](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/anycable/anycablebility)
2
5
 
3
6
  # AnyCable Conformance Testing Tool
4
7
 
@@ -4,13 +4,13 @@ require "logger"
4
4
  require "optparse"
5
5
 
6
6
  require "anyt/version"
7
+ require "anyt/remote_control"
7
8
  require "anyt/rpc"
8
9
  require "anyt/command"
9
10
  require "anyt/tests"
10
11
 
11
- # rubocop: disable Metrics/AbcSize
12
- # rubocop: disable Metrics/MethodLength
13
- # rubocop: disable Metrics/BlockLength
12
+ $stdout.sync = true
13
+
14
14
  module Anyt
15
15
  module Cli # :nodoc:
16
16
  class << self
@@ -25,14 +25,26 @@ module Anyt
25
25
  $stdout.puts "Starting AnyT v#{Anyt::VERSION} (pid: #{Process.pid})\n"
26
26
 
27
27
  begin
28
+ # "Enable" AnyCable as early as possible to activate all the features in tests
29
+ unless Anyt.config.use_action_cable
30
+ ActionCable.server.config.cable = {"adapter" => "any_cable"}
31
+ require "anycable-rails"
32
+ end
33
+
28
34
  # Load all test scenarios
29
35
  Tests.load_tests
30
36
 
37
+ Rails.application.initialize!
38
+
31
39
  # Start RPC server (unless specified otherwise, e.g. when
32
40
  # we want to test Action Cable itself)
33
41
  unless @skip_rpc
34
- require "anycable-rails"
35
42
  RPC.start
43
+
44
+ if @only_rpc
45
+ RPC.server.wait_till_terminated
46
+ return
47
+ end
36
48
  end
37
49
 
38
50
  # Start webosocket server under test
@@ -77,6 +89,10 @@ module Anyt
77
89
  @skip_rpc = flag
78
90
  end
79
91
 
92
+ cli.on("--only-rpc", TrueClass, "Run only RPC server") do |flag|
93
+ @only_rpc = flag
94
+ end
95
+
80
96
  cli.on("--self-check", "Run tests again Action Cable itself") do
81
97
  @skip_rpc = true
82
98
  dummy_path = ::File.expand_path(
@@ -84,19 +100,29 @@ module Anyt
84
100
  ::File.join(::File.dirname(__FILE__), "dummy")
85
101
  )
86
102
  Anyt.config.command = "bundle exec puma #{dummy_path}"
103
+ Anyt.config.use_action_cable = true
87
104
  end
88
105
 
89
106
  cli.on("--only test1,test2,test3", Array, "Run only specified tests") do |only_tests|
90
107
  Anyt.config.only_tests = only_tests
91
108
  end
92
109
 
110
+ cli.on("--except test1,test2,test3", Array, "Exclude specified tests") do |except_tests|
111
+ Anyt.config.except_tests = except_tests
112
+ end
113
+
93
114
  cli.on("--wait-command=TIMEOUT", Integer,
94
- "Number of seconds to wait for WS server initialization") do |timeout|
115
+ "Number of seconds to wait for WS server initialization") do |timeout|
95
116
  Anyt.config.wait_command = timeout
96
117
  end
97
118
 
119
+ cli.on("--timeout-multiplier=VALUE", Float,
120
+ "Default exceptation timeouts multiplier") do |val|
121
+ Anyt.config.timeout_multiplier = val
122
+ end
123
+
98
124
  cli.on("-rPATH", "--require=PATH",
99
- "Path to additional tests (e.g. features/*.rb") do |path|
125
+ "Path to additional tests (e.g. features/*.rb") do |path|
100
126
  Anyt.config.tests_relative_path = path
101
127
  ENV["ANYT_TESTS_RELATIVE_PATH"] = path
102
128
  end
@@ -17,14 +17,17 @@ module Anyt
17
17
  # rubocop: disable Metrics/BlockLength
18
18
  def initialize(
19
19
  ignore: [], url: Anyt.config.target_url, qs: "",
20
- cookies: "", headers: {}
20
+ cookies: "", headers: {},
21
+ timeout_multiplier: Anyt.config.timeout_multiplier
21
22
  )
22
23
  ignore_message_types = @ignore_message_types = ignore
23
24
  messages = @messages = Queue.new
24
25
  closed = @closed = Concurrent::Event.new
25
26
  has_messages = @has_messages = Concurrent::Semaphore.new(0)
26
27
 
27
- headers = headers.merge('cookie' => cookies)
28
+ @timeout_multiplier = timeout_multiplier
29
+
30
+ headers = headers.merge("cookie" => cookies)
28
31
 
29
32
  open = Concurrent::Promise.new
30
33
 
@@ -68,13 +71,15 @@ module Anyt
68
71
  end
69
72
  end
70
73
 
71
- open.wait!(WAIT_WHEN_EXPECTING_EVENT)
74
+ open.wait!(WAIT_WHEN_EXPECTING_EVENT * @timeout_multiplier)
72
75
  end
73
76
  # rubocop: enable Metrics/BlockLength
74
77
  # rubocop: enable Metrics/AbcSize
75
78
  # rubocop: enable Metrics/MethodLength
76
79
 
77
80
  def receive(timeout: WAIT_WHEN_EXPECTING_EVENT)
81
+ timeout *= @timeout_multiplier
82
+
78
83
  raise TimeoutError, "Timed out to receive message" unless
79
84
  @has_messages.try_acquire(1, timeout)
80
85
 
@@ -88,17 +93,17 @@ module Anyt
88
93
  @ws.send(JSON.generate(message))
89
94
  end
90
95
 
91
- def close
92
- sleep WAIT_WHEN_NOT_EXPECTING_EVENT
96
+ def close(allow_messages: false)
97
+ sleep WAIT_WHEN_NOT_EXPECTING_EVENT * @timeout_multiplier
93
98
 
94
- raise "#{@messages.size} messages unprocessed" unless @messages.empty?
99
+ raise "#{@messages.size} messages unprocessed" unless allow_messages || @messages.empty?
95
100
 
96
101
  @ws.close
97
102
  wait_for_close
98
103
  end
99
104
 
100
105
  def wait_for_close
101
- @closed.wait(WAIT_WHEN_EXPECTING_EVENT)
106
+ @closed.wait(WAIT_WHEN_EXPECTING_EVENT * @timeout_multiplier)
102
107
  end
103
108
 
104
109
  def closed?
@@ -1,50 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "childprocess"
4
+
3
5
  module Anyt
4
6
  # Runs system command (websocket server)
5
7
  module Command
6
8
  class << self
7
- attr_accessor :running
8
-
9
9
  # rubocop: disable Metrics/MethodLength
10
10
  # rubocop: disable Metrics/AbcSize
11
11
  def run
12
- return if @running
12
+ return if running?
13
13
 
14
14
  AnyCable.logger.debug "Running command: #{Anyt.config.command}"
15
15
 
16
- out = AnyCable.config.debug ? STDOUT : IO::NULL
16
+ @process = ChildProcess.build(*Anyt.config.command.split(/\s+/))
17
+
18
+ process.io.inherit! if AnyCable.config.debug
17
19
 
18
- @pid = Process.spawn(
19
- Anyt.config.command,
20
- out: out,
21
- err: out
22
- )
20
+ process.detach = true
23
21
 
24
- Process.detach(@pid)
22
+ process.environment["ANYCABLE_DEBUG"] = "1" if AnyCable.config.debug?
23
+ process.environment["ANYT_REMOTE_CONTROL_PORT"] = Anyt.config.remote_control_port
25
24
 
26
- AnyCable.logger.debug "Command PID: #{@pid}"
25
+ process.start
27
26
 
28
- @running = true
27
+ AnyCable.logger.debug "Command PID: #{process.pid}"
29
28
 
30
29
  sleep Anyt.config.wait_command
30
+ raise "Command failed to start" unless running?
31
31
  end
32
32
  # rubocop: enable Metrics/MethodLength
33
33
  # rubocop: enable Metrics/AbcSize
34
34
 
35
- def stop
36
- return unless @running
35
+ def restart
36
+ return unless running?
37
37
 
38
- AnyCable.logger.debug "Terminate PID: #{@pid}"
38
+ AnyCable.logger.debug "Restarting command PID: #{process.pid}"
39
39
 
40
- Process.kill("SIGKILL", @pid)
40
+ stop
41
+ process.wait
41
42
 
42
- @running = false
43
+ run
44
+ end
45
+
46
+ def stop
47
+ return unless running?
48
+
49
+ AnyCable.logger.debug "Terminate PID: #{process.pid}"
50
+
51
+ process.stop
43
52
  end
44
53
 
45
54
  def running?
46
- @running == true
55
+ process&.alive?
47
56
  end
57
+
58
+ private
59
+
60
+ attr_reader :process
48
61
  end
49
62
  end
50
63
  end
@@ -6,10 +6,14 @@ module Anyt
6
6
  # Anyt configuration
7
7
  class Config < Anyway::Config
8
8
  attr_config :command,
9
- :only_tests,
10
- :tests_relative_path,
11
- target_url: "ws://localhost:9292/cable",
12
- wait_command: 2
9
+ :only_tests,
10
+ :except_tests,
11
+ :tests_relative_path,
12
+ remote_control_port: 8919,
13
+ use_action_cable: false,
14
+ target_url: "ws://localhost:9292/cable",
15
+ wait_command: 2,
16
+ timeout_multiplier: 1
13
17
 
14
18
  def tests_path
15
19
  return unless tests_relative_path
@@ -18,12 +22,16 @@ module Anyt
18
22
  end
19
23
 
20
24
  def filter_tests?
21
- !only_tests.nil?
25
+ only_tests || except_tests
22
26
  end
23
27
 
24
28
  def tests_filter
25
- @tests_filter ||= begin
26
- /(#{only_tests.join('|')})/
29
+ only_rxp = /(#{only_tests.join('|')})/ if only_tests
30
+ except_rxp = /(#{except_tests.join('|')})/ if except_tests
31
+
32
+ @tests_filter ||= lambda do |path|
33
+ (only_rxp.nil? || only_rxp.match?(path)) &&
34
+ (except_rxp.nil? || !except_rxp.match?(path))
27
35
  end
28
36
  end
29
37
  end
@@ -10,11 +10,11 @@ require "anycable-rails"
10
10
  require "action_dispatch/middleware/cookies"
11
11
 
12
12
  class TestApp < Rails::Application
13
- secrets.secret_token = "secret_token"
13
+ secrets.secret_token = "secret_token"
14
14
  secrets.secret_key_base = "secret_key_base"
15
15
 
16
16
  config.logger = Logger.new(STDOUT)
17
- config.log_level = :fatal
17
+ config.log_level = AnyCable.config.log_level
18
18
  config.eager_load = true
19
19
 
20
20
  config.paths["config/routes.rb"] << File.join(__dir__, "routes.rb")
@@ -24,13 +24,15 @@ module ApplicationCable
24
24
  class Connection < ActionCable::Connection::Base
25
25
  delegate :params, to: :request
26
26
 
27
+ identified_by :uid
28
+
27
29
  def connect
28
- logger.info "Connected"
30
+ logger.debug "Connected"
29
31
  Anyt::ConnectHandlers.call(self)
30
32
  end
31
33
 
32
34
  def disconnect
33
- logger.info "Disconnected"
35
+ logger.debug "Disconnected"
34
36
  end
35
37
  end
36
38
  end
@@ -40,8 +42,8 @@ module ApplicationCable
40
42
  end
41
43
  end
42
44
 
43
- ActionCable.server.config.cable = { "adapter" => "redis" }
45
+ ActionCable.server.config.cable = {"adapter" => "redis"}
44
46
  ActionCable.server.config.connection_class = -> { ApplicationCable::Connection }
45
47
  ActionCable.server.config.disable_request_forgery_protection = true
46
48
  ActionCable.server.config.logger =
47
- Rails.logger = Logger.new(STDOUT).tap { |l| l.level = Logger::DEBUG }
49
+ Rails.logger
@@ -3,6 +3,10 @@
3
3
  require_relative "./application"
4
4
 
5
5
  require_relative "../tests"
6
+ require_relative "../remote_control"
7
+
8
+ # Start remote control
9
+ Anyt::RemoteControl::Server.start(Anyt.config.remote_control_port)
6
10
 
7
11
  # Load channels from tests
8
12
  Anyt::Tests.load_all_tests
@@ -0,0 +1 @@
1
+ fe2ecbcf229d5547a64bcdaa9ef4e543404ca3fc9d4ee83aaa7ddab34b6a378fe090c2b1836c9ebd3e5ebbf01c239ddac95e219358baaefc1afaa04cd07bf78c
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ENV["TERM"] = "#{ENV["TERM"]}color" unless ENV["TERM"]&.match?(/color/)
3
4
  require "minitest/spec"
4
5
  require "minitest/reporters"
5
6
 
@@ -8,10 +9,26 @@ module Anyt
8
9
  module TestHelpers
9
10
  def self.included(base)
10
11
  base.let(:client) { build_client(ignore: %w[ping welcome]) }
12
+ base.after { @clients&.each { |client| client.close(allow_messages: true) } }
11
13
  end
12
14
 
13
15
  def build_client(*args)
14
- Anyt::Client.new(*args)
16
+ @clients ||= []
17
+ Anyt::Client.new(*args).tap do |client|
18
+ @clients << client
19
+ end
20
+ end
21
+
22
+ def restart_server!
23
+ if Anyt.config.use_action_cable
24
+ remote_client.restart_action_cable
25
+ else
26
+ Command.restart
27
+ end
28
+ end
29
+
30
+ def remote_client
31
+ @remote_client ||= RemoteControl::Client.connect(Anyt.config.remote_control_port)
15
32
  end
16
33
  end
17
34
  end
@@ -38,7 +55,7 @@ module Anyt
38
55
 
39
56
  def handlers_for(connection)
40
57
  handlers.select do |(tag, _)|
41
- connection.params['test'] == tag
58
+ connection.params["test"] == tag
42
59
  end
43
60
  end
44
61
 
@@ -74,7 +91,7 @@ module Minitest::Spec::DSL
74
91
  # Generates Channel class dynamically and
75
92
  # add memoized helper to access its name
76
93
  def channel(id = nil, &block)
77
- class_name = @name.gsub(/\s+/, '_')
94
+ class_name = @name.gsub(/\s+/, "_")
78
95
  class_name += "_#{id}" if id
79
96
  class_name += "_channel"
80
97
 
@@ -99,15 +116,15 @@ module Anyt
99
116
  module MinitestPatch
100
117
  def load_plugins
101
118
  super
102
- extensions.delete('rails')
119
+ extensions.delete("rails")
103
120
  end
104
121
  end
105
122
 
106
123
  # Patch Spec reporter
107
124
  module ReporterPatch # :nodoc:
108
125
  def record_print_status(test)
109
- test_name = test.name.gsub(/^test_/, '').strip
110
- print pad_test(test_name)
126
+ test_name = test.name.gsub(/^test_/, "").strip
127
+ print(magenta { pad_test(test_name) })
111
128
  print_colored_status(test)
112
129
  print(" (%.2fs)" % test.time) unless test.time.nil?
113
130
  puts
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "drb/drb"
4
+
5
+ module Anyt
6
+ # Invoke commands within the running Ruby (Action Cable) server
7
+ module RemoteControl
8
+ class Server
9
+ class << self
10
+ alias start new
11
+ end
12
+
13
+ def initialize(port)
14
+ DRb.start_service(
15
+ "druby://localhost:#{port}",
16
+ Client.new
17
+ )
18
+ end
19
+ end
20
+
21
+ class Client
22
+ def self.connect(port)
23
+ DRb.start_service
24
+
25
+ DRbObject.new_with_uri("druby://localhost:#{port}")
26
+ end
27
+
28
+ def restart_action_cable
29
+ ActionCable.server.restart
30
+ end
31
+ end
32
+ end
33
+ end