anyt 0.8.3 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) 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 +13 -7
  6. data/lib/anyt/command.rb +31 -18
  7. data/lib/anyt/config.rb +15 -7
  8. data/lib/anyt/dummy/application.rb +9 -11
  9. data/lib/anyt/dummy/config.ru +4 -0
  10. data/lib/anyt/dummy/routes.rb +4 -0
  11. data/lib/anyt/dummy/tmp/development_secret.txt +1 -0
  12. data/lib/anyt/ext/minitest.rb +23 -6
  13. data/lib/anyt/remote_control.rb +33 -0
  14. data/lib/anyt/rpc.rb +4 -6
  15. data/lib/anyt/tests.rb +5 -7
  16. data/lib/anyt/tests/core/ping_test.rb +2 -2
  17. data/lib/anyt/tests/core/welcome_test.rb +3 -3
  18. data/lib/anyt/tests/features/channel_state_test.rb +79 -0
  19. data/lib/anyt/tests/features/remote_disconnect_test.rb +30 -0
  20. data/lib/anyt/tests/features/server_restart_test.rb +28 -0
  21. data/lib/anyt/tests/request/channel_test.rb +28 -0
  22. data/lib/anyt/tests/request/connection_test.rb +23 -21
  23. data/lib/anyt/tests/request/disconnect_reasons_test.rb +23 -0
  24. data/lib/anyt/tests/request/disconnection_test.rb +50 -32
  25. data/lib/anyt/tests/streams/broadcast_test.rb +13 -13
  26. data/lib/anyt/tests/streams/multiple_clients_test.rb +10 -10
  27. data/lib/anyt/tests/streams/multiple_test.rb +10 -10
  28. data/lib/anyt/tests/streams/single_test.rb +35 -10
  29. data/lib/anyt/tests/streams/stop_test.rb +57 -0
  30. data/lib/anyt/tests/subscriptions/ack_test.rb +9 -11
  31. data/lib/anyt/tests/subscriptions/params_test.rb +6 -7
  32. data/lib/anyt/tests/subscriptions/perform_test.rb +16 -18
  33. data/lib/anyt/tests/subscriptions/transmissions_test.rb +6 -7
  34. data/lib/anyt/version.rb +1 -1
  35. metadata +28 -70
  36. data/.gitignore +0 -43
  37. data/.rubocop.yml +0 -80
  38. data/Gemfile +0 -6
  39. data/LICENSE.txt +0 -21
  40. data/Makefile +0 -5
  41. data/anyt.gemspec +0 -42
  42. data/circle.yml +0 -14
  43. data/etc/tests/channel_broadcast_test.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc11a5ff81965c5f58e9fa188dc50b77c922b55b6ebd4bb37e7f008619b7ebdd
4
- data.tar.gz: 11dba8960b7d37b7220451a2bf3b2b6ca611c91e48a19a1a9c719af72527b942
3
+ metadata.gz: cc9f527d5ead1058291854236f7b011c64df27da0be2559bb27893c1dbb471fc
4
+ data.tar.gz: 0dce342de8720db9d2208e4fc44c7c1ba3b489c0d82bce7b936152df2c70ccca
5
5
  SHA512:
6
- metadata.gz: 5f81a9f004d643dd129b601026bf25981b6ad769970cff6bd00cbd74f3fa278bc258798e5a4ac7875c61ed9b0c3dffe312ebade12d0c514f6ffac8984612e5d4
7
- data.tar.gz: 9d79a8212dc54a3b9230b71bfb469232a4e71f03c4df0a9844dab8a30f3fdc809a503270d0ab8e64242b5a9b1b5b963366e7e8d089aa952804373d170c050f68
6
+ metadata.gz: 6237b496a2cfb034d4b0d83eff127be84c360a124039bf14c32e4886732f9b9cff9def7e1515168dc87027ca85bcc64b78dfdd4d10988a1e84206b9fc4be7b3a
7
+ data.tar.gz: 9cd9d41ebdaf1321d13a08685b1955fe92875fb86fda8c4a230b1df9d30e7e39e5564a2614fb1693666a203926770cbdf3d9a0fda7265f3893524baa7cb8f89a
@@ -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
 
@@ -48,6 +51,7 @@ module Anyt
48
51
  end
49
52
 
50
53
  ws.on(:message) do |event|
54
+ next if event.type == :ping
51
55
  if event.type == :close
52
56
  closed.set
53
57
  else
@@ -67,13 +71,15 @@ module Anyt
67
71
  end
68
72
  end
69
73
 
70
- open.wait!(WAIT_WHEN_EXPECTING_EVENT)
74
+ open.wait!(WAIT_WHEN_EXPECTING_EVENT * @timeout_multiplier)
71
75
  end
72
76
  # rubocop: enable Metrics/BlockLength
73
77
  # rubocop: enable Metrics/AbcSize
74
78
  # rubocop: enable Metrics/MethodLength
75
79
 
76
80
  def receive(timeout: WAIT_WHEN_EXPECTING_EVENT)
81
+ timeout *= @timeout_multiplier
82
+
77
83
  raise TimeoutError, "Timed out to receive message" unless
78
84
  @has_messages.try_acquire(1, timeout)
79
85
 
@@ -87,17 +93,17 @@ module Anyt
87
93
  @ws.send(JSON.generate(message))
88
94
  end
89
95
 
90
- def close
91
- sleep WAIT_WHEN_NOT_EXPECTING_EVENT
96
+ def close(allow_messages: false)
97
+ sleep WAIT_WHEN_NOT_EXPECTING_EVENT * @timeout_multiplier
92
98
 
93
- raise "#{@messages.size} messages unprocessed" unless @messages.empty?
99
+ raise "#{@messages.size} messages unprocessed" unless allow_messages || @messages.empty?
94
100
 
95
101
  @ws.close
96
102
  wait_for_close
97
103
  end
98
104
 
99
105
  def wait_for_close
100
- @closed.wait(WAIT_WHEN_EXPECTING_EVENT)
106
+ @closed.wait(WAIT_WHEN_EXPECTING_EVENT * @timeout_multiplier)
101
107
  end
102
108
 
103
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,31 +10,29 @@ 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
- initializer "routes" do
21
- Rails.application.routes.draw do
22
- mount ActionCable.server => "/cable"
23
- end
24
- end
20
+ config.paths["config/routes.rb"] << File.join(__dir__, "routes.rb")
25
21
  end
26
22
 
27
23
  module ApplicationCable
28
24
  class Connection < ActionCable::Connection::Base
29
25
  delegate :params, to: :request
30
26
 
27
+ identified_by :uid
28
+
31
29
  def connect
32
- logger.info "Connected"
30
+ logger.debug "Connected"
33
31
  Anyt::ConnectHandlers.call(self)
34
32
  end
35
33
 
36
34
  def disconnect
37
- logger.info "Disconnected"
35
+ logger.debug "Disconnected"
38
36
  end
39
37
  end
40
38
  end
@@ -44,8 +42,8 @@ module ApplicationCable
44
42
  end
45
43
  end
46
44
 
47
- ActionCable.server.config.cable = { "adapter" => "redis" }
45
+ ActionCable.server.config.cable = {"adapter" => "redis"}
48
46
  ActionCable.server.config.connection_class = -> { ApplicationCable::Connection }
49
47
  ActionCable.server.config.disable_request_forgery_protection = true
50
48
  ActionCable.server.config.logger =
51
- 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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ end
@@ -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