anyt 0.8.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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