anyt 0.8.1 → 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 +40 -5
  5. data/lib/anyt/client.rb +13 -7
  6. data/lib/anyt/command.rb +31 -18
  7. data/lib/anyt/config.rb +21 -6
  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 +26 -14
  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 +47 -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 +10 -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 +32 -73
  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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e638c537e69dc1a664f30c53781c04706c8e08c317badda109e877984742863
4
- data.tar.gz: 850d0dd0c56839c8c92e418516d520a044e6d6d1e835a23d73a0590541e15d48
3
+ metadata.gz: 44aafe91585ca0592acd050ba53ee3d71e3ecb908dc00889af176532a39bdbcf
4
+ data.tar.gz: ec28d88e061d67b4481be7acd479376bd6ada9522727d0734316af1a20d4564e
5
5
  SHA512:
6
- metadata.gz: 2ea1aaea3191e20871dc74547a2bea9239202f0bc6ce1392c9a1f180e6ae0a3512b79ee40fe1c8f5cbf9076a7b53767f7c83136cd0798a127e62589094420e21
7
- data.tar.gz: df2c10e408e26b02cf60d697d675ea2716368d0f3e23b722d6b97bbd982d3159af6d787fad0e0abec7a303b1cc0a08920d90b3fe845ac4c3e657badd138ba558
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
 
@@ -3,13 +3,14 @@
3
3
  require "logger"
4
4
  require "optparse"
5
5
 
6
+ require "anyt/version"
7
+ require "anyt/remote_control"
6
8
  require "anyt/rpc"
7
9
  require "anyt/command"
8
10
  require "anyt/tests"
9
11
 
10
- # rubocop: disable Metrics/AbcSize
11
- # rubocop: disable Metrics/MethodLength
12
- # rubocop: disable Metrics/BlockLength
12
+ $stdout.sync = true
13
+
13
14
  module Anyt
14
15
  module Cli # :nodoc:
15
16
  class << self
@@ -21,15 +22,29 @@ module Anyt
21
22
 
22
23
  result = 1
23
24
 
25
+ $stdout.puts "Starting AnyT v#{Anyt::VERSION} (pid: #{Process.pid})\n"
26
+
24
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
+
25
34
  # Load all test scenarios
26
35
  Tests.load_tests
27
36
 
37
+ Rails.application.initialize!
38
+
28
39
  # Start RPC server (unless specified otherwise, e.g. when
29
40
  # we want to test Action Cable itself)
30
41
  unless @skip_rpc
31
- require "anycable-rails"
32
42
  RPC.start
43
+
44
+ if @only_rpc
45
+ RPC.server.wait_till_terminated
46
+ return
47
+ end
33
48
  end
34
49
 
35
50
  # Start webosocket server under test
@@ -74,6 +89,10 @@ module Anyt
74
89
  @skip_rpc = flag
75
90
  end
76
91
 
92
+ cli.on("--only-rpc", TrueClass, "Run only RPC server") do |flag|
93
+ @only_rpc = flag
94
+ end
95
+
77
96
  cli.on("--self-check", "Run tests again Action Cable itself") do
78
97
  @skip_rpc = true
79
98
  dummy_path = ::File.expand_path(
@@ -81,17 +100,33 @@ module Anyt
81
100
  ::File.join(::File.dirname(__FILE__), "dummy")
82
101
  )
83
102
  Anyt.config.command = "bundle exec puma #{dummy_path}"
103
+ Anyt.config.use_action_cable = true
84
104
  end
85
105
 
86
106
  cli.on("--only test1,test2,test3", Array, "Run only specified tests") do |only_tests|
87
107
  Anyt.config.only_tests = only_tests
88
108
  end
89
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
+
90
114
  cli.on("--wait-command=TIMEOUT", Integer,
91
- "Number of seconds to wait for WS server initialization") do |timeout|
115
+ "Number of seconds to wait for WS server initialization") do |timeout|
92
116
  Anyt.config.wait_command = timeout
93
117
  end
94
118
 
119
+ cli.on("--timeout-multiplier=VALUE", Float,
120
+ "Default exceptation timeouts multiplier") do |val|
121
+ Anyt.config.timeout_multiplier = val
122
+ end
123
+
124
+ cli.on("-rPATH", "--require=PATH",
125
+ "Path to additional tests (e.g. features/*.rb") do |path|
126
+ Anyt.config.tests_relative_path = path
127
+ ENV["ANYT_TESTS_RELATIVE_PATH"] = path
128
+ end
129
+
95
130
  cli.on("--debug", "Enable debug mode.") do
96
131
  AnyCable.config.debug = true
97
132
  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,17 +6,32 @@ module Anyt
6
6
  # Anyt configuration
7
7
  class Config < Anyway::Config
8
8
  attr_config :command,
9
- :only_tests,
10
- target_url: "ws://localhost:9292/cable",
11
- 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
17
+
18
+ def tests_path
19
+ return unless tests_relative_path
20
+
21
+ File.expand_path(tests_relative_path, Dir.pwd)
22
+ end
12
23
 
13
24
  def filter_tests?
14
- !only_tests.nil?
25
+ only_tests || except_tests
15
26
  end
16
27
 
17
28
  def tests_filter
18
- @tests_filter ||= begin
19
- /(#{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))
20
35
  end
21
36
  end
22
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