anyt 0.8.2 → 1.0.1

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 (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 +33 -7
  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 +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 +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 +32 -74
  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: b518f7b895effeba08e97fd20a64dee5c7e0a09faff5b9df9072ae76cb9f02fa
4
- data.tar.gz: fd4f53b7cb1601fa652e1d74d87b79081721ad2e1ed7e18dec93ca976a787a1f
3
+ metadata.gz: 75c73a6373cc4d26033b8fd5caf3d8b5601d06644227f7ee279818b8fc5e4afd
4
+ data.tar.gz: d187153da7f0e4b3d7aae6d5a67594a66817beac1364e7741b0b2fb98b2ef7b3
5
5
  SHA512:
6
- metadata.gz: b8ab2923833c1a868ec9cb2e3c6532d9990c43fb95aa58cd1819bd904409e45b753be2ec8497ed23bc5c1e4a18ba6aa9802068852f8ebca08d60fc0c5b683914
7
- data.tar.gz: f15eadfbc5f611697ad4bf25f0ec41ddc39e60f7389074a88a6e8ad6aada7de5301e10ba68e512deadb643d6e3b20c0496dbaa6f04d997086520da9c3d1083a6
6
+ metadata.gz: 1b06df7c02346d0c6986f42e5e71b33875c99ee0924c41a31ea4de574b3b65eaf15069f410962aaf6d62a1af511efd9505f49ade5cbf9815f2bdec00e5a7ff51
7
+ data.tar.gz: 4955e07d7fac679f27f55604faea71d513bc66cfaf85abcd2e6f46db375a706a6d9d281ad36529f7d124a2e7242a777d4c83d11ec6020bfe9160f0bfbb076bfe
@@ -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
@@ -21,18 +21,30 @@ module Anyt
21
21
  ActionCable.server.config.logger = Rails.logger = AnyCable.logger
22
22
 
23
23
  result = 1
24
-
24
+
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