rsmp-validator 0.1.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.
- checksums.yaml +7 -0
- data/config/cross_rs4s.yaml +55 -0
- data/config/gem_supervisor.yaml +56 -0
- data/config/gem_tlc.yaml +56 -0
- data/config/gem_tlc_secrets.yaml +3 -0
- data/config/kapsch_etx.yaml +54 -0
- data/config/lightmotion_satellite.yaml +56 -0
- data/config/secrets.yaml +3 -0
- data/config/secrets_example.yaml +6 -0
- data/config/semaforica_cartesio.yaml +56 -0
- data/config/simulator/node_log.yaml +17 -0
- data/config/simulator/supervisor.yaml +11 -0
- data/config/simulator/tlc.yaml +56 -0
- data/config/sus.rb +13 -0
- data/config/swarco_itc3.yaml +55 -0
- data/config/tecsen_tmacs_supervisor.yaml +57 -0
- data/config/validator.rb +37 -0
- data/config/validator.yaml +5 -0
- data/config/validator_example.yaml +23 -0
- data/config/validator_log.yaml +19 -0
- data/exe/rsmp-validator +121 -0
- data/lib/doc_gen/parser.rb +276 -0
- data/lib/doc_gen/renderer.rb +153 -0
- data/lib/rsmp/validator/async_context.rb +15 -0
- data/lib/rsmp/validator/auto_node.rb +82 -0
- data/lib/rsmp/validator/auto_site.rb +30 -0
- data/lib/rsmp/validator/auto_supervisor.rb +23 -0
- data/lib/rsmp/validator/config_normalizer.rb +103 -0
- data/lib/rsmp/validator/configuration/loader.rb +79 -0
- data/lib/rsmp/validator/configuration/secrets.rb +54 -0
- data/lib/rsmp/validator/configuration/validation.rb +115 -0
- data/lib/rsmp/validator/configuration.rb +129 -0
- data/lib/rsmp/validator/helpers/alarms.rb +66 -0
- data/lib/rsmp/validator/helpers/clock.rb +16 -0
- data/lib/rsmp/validator/helpers/connection.rb +73 -0
- data/lib/rsmp/validator/helpers/handshake.rb +110 -0
- data/lib/rsmp/validator/helpers/input.rb +42 -0
- data/lib/rsmp/validator/helpers/security.rb +26 -0
- data/lib/rsmp/validator/helpers/signal_plans.rb +37 -0
- data/lib/rsmp/validator/helpers/signal_priority.rb +130 -0
- data/lib/rsmp/validator/helpers/startup.rb +157 -0
- data/lib/rsmp/validator/helpers/status.rb +22 -0
- data/lib/rsmp/validator/lifecycle.rb +99 -0
- data/lib/rsmp/validator/log.rb +11 -0
- data/lib/rsmp/validator/mode_detection.rb +84 -0
- data/lib/rsmp/validator/options/site_test_options.rb +58 -0
- data/lib/rsmp/validator/options/supervisor_test_options.rb +51 -0
- data/lib/rsmp/validator/site_tester.rb +113 -0
- data/lib/rsmp/validator/supervisor_tester.rb +76 -0
- data/lib/rsmp/validator/tester.rb +101 -0
- data/lib/rsmp/validator/version.rb +5 -0
- data/lib/rsmp/validator/version_filter.rb +44 -0
- data/lib/rsmp/validator.rb +50 -0
- data/schemas/site_test.json +36 -0
- data/schemas/supervisor_test.json +28 -0
- data/test/site/core/aggregated_status_spec.rb +43 -0
- data/test/site/core/connect_spec.rb +104 -0
- data/test/site/core/core_spec.rb +9 -0
- data/test/site/core/disconnect_spec.rb +54 -0
- data/test/site/site_spec.rb +5 -0
- data/test/site/tlc/alarm_spec.rb +134 -0
- data/test/site/tlc/clock_spec.rb +252 -0
- data/test/site/tlc/detector_logics_spec.rb +76 -0
- data/test/site/tlc/emergency_routes_spec.rb +106 -0
- data/test/site/tlc/input_spec.rb +102 -0
- data/test/site/tlc/invalid_command_spec.rb +103 -0
- data/test/site/tlc/invalid_status_spec.rb +70 -0
- data/test/site/tlc/modes_spec.rb +260 -0
- data/test/site/tlc/output_spec.rb +58 -0
- data/test/site/tlc/signal_groups_spec.rb +96 -0
- data/test/site/tlc/signal_plans_spec.rb +287 -0
- data/test/site/tlc/signal_priority_spec.rb +144 -0
- data/test/site/tlc/subscribe_spec.rb +71 -0
- data/test/site/tlc/system_spec.rb +76 -0
- data/test/site/tlc/tlc_spec.rb +7 -0
- data/test/site/tlc/traffic_data_spec.rb +151 -0
- data/test/site/tlc/traffic_situations_spec.rb +50 -0
- data/test/supervisor/aggregated_status_spec.rb +18 -0
- data/test/supervisor/connect_spec.rb +219 -0
- data/test/supervisor/supervisor_spec.rb +11 -0
- metadata +190 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
module Validator
|
|
3
|
+
# Methods for detecting test mode and building the appropriate tester and auto node.
|
|
4
|
+
module ModeDetection
|
|
5
|
+
# Print an error message and exit.
|
|
6
|
+
def abort_with_error(error)
|
|
7
|
+
warn "Error: #{error}".colorize(:red)
|
|
8
|
+
exit 1
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Initial connectivity check to verify we can connect to the site/supervisor being tested.
|
|
12
|
+
def check_connection
|
|
13
|
+
log "Initial #{mode} connection check"
|
|
14
|
+
if mode == :site
|
|
15
|
+
SiteTester.instance.connected { nil }
|
|
16
|
+
elsif mode == :supervisor
|
|
17
|
+
SupervisorTester.instance.connected { nil }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Set whether we are testing a site or a supervisor.
|
|
22
|
+
def select_mode(mode)
|
|
23
|
+
if self.mode
|
|
24
|
+
abort_with_error 'Cannot run tests in both test/site/ and test/supervisor/' if self.mode != mode
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
case mode
|
|
29
|
+
when :site, :supervisor
|
|
30
|
+
self.mode = mode
|
|
31
|
+
else
|
|
32
|
+
abort_with_error "Unknown test mode: #{mode}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Determine mode from test file paths.
|
|
37
|
+
def determine_mode(sus_config)
|
|
38
|
+
paths = sus_config.paths.any? ? sus_config.paths : sus_config.test_paths
|
|
39
|
+
site_dir = File.expand_path('test/site', sus_config.root)
|
|
40
|
+
supervisor_dir = File.expand_path('test/supervisor', sus_config.root)
|
|
41
|
+
|
|
42
|
+
paths.each do |path_str|
|
|
43
|
+
expanded = File.expand_path(path_str, sus_config.root)
|
|
44
|
+
inferred = infer_mode_from_path(expanded, site_dir, supervisor_dir)
|
|
45
|
+
select_mode inferred if inferred
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
abort_with_error 'Could not determine test mode (site or supervisor) from test paths' unless mode
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Determine the test mode from a single expanded path.
|
|
52
|
+
def infer_mode_from_path(path, site_dir, supervisor_dir)
|
|
53
|
+
return :site if path == site_dir || path.start_with?("#{site_dir}/")
|
|
54
|
+
return :supervisor if path == supervisor_dir || path.start_with?("#{supervisor_dir}/")
|
|
55
|
+
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Build the tester instance.
|
|
60
|
+
def build_tester
|
|
61
|
+
if mode == :site
|
|
62
|
+
SiteTester.instance = SiteTester.new
|
|
63
|
+
elsif mode == :supervisor
|
|
64
|
+
SupervisorTester.instance = SupervisorTester.new
|
|
65
|
+
else
|
|
66
|
+
abort_with_error "Unknown test mode: #{mode}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Build the auto node (local site or supervisor to be tested).
|
|
71
|
+
def build_auto_node
|
|
72
|
+
return unless auto_node_config
|
|
73
|
+
|
|
74
|
+
if mode == :site
|
|
75
|
+
self.auto_node = AutoSite.new
|
|
76
|
+
elsif mode == :supervisor
|
|
77
|
+
self.auto_node = AutoSupervisor.new
|
|
78
|
+
else
|
|
79
|
+
abort_with_error "Unknown test mode: #{mode}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'rsmp'
|
|
2
|
+
|
|
3
|
+
module RSMP
|
|
4
|
+
module Validator
|
|
5
|
+
module SiteTest
|
|
6
|
+
# Configuration options for site testing.
|
|
7
|
+
class Options < RSMP::Options
|
|
8
|
+
def schema_file
|
|
9
|
+
'site_test.json'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def schema_path
|
|
13
|
+
File.expand_path("../../../schemas/#{schema_file}", __dir__)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def apply_defaults(options)
|
|
19
|
+
return options unless options.is_a?(Hash)
|
|
20
|
+
|
|
21
|
+
local_supervisor = options['local_supervisor']
|
|
22
|
+
return options unless local_supervisor.is_a?(Hash)
|
|
23
|
+
|
|
24
|
+
local_supervisor['sites'] ||= {}
|
|
25
|
+
local_supervisor['sites']['default'] =
|
|
26
|
+
merge_default_defaults(options, local_supervisor.dig('sites', 'default'))
|
|
27
|
+
local_supervisor = RSMP::Supervisor::Options.new(local_supervisor).to_h
|
|
28
|
+
options.merge('local_supervisor' => local_supervisor)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def merge_default_defaults(options, default)
|
|
32
|
+
merged_default = default.is_a?(Hash) ? default.dup : {}
|
|
33
|
+
|
|
34
|
+
site_defaults.each do |key|
|
|
35
|
+
value = options[key]
|
|
36
|
+
next if value.nil? || merged_default.key?(key)
|
|
37
|
+
|
|
38
|
+
merged_default[key] = value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
merged_default
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def site_defaults
|
|
45
|
+
%w[
|
|
46
|
+
sxls
|
|
47
|
+
core_version
|
|
48
|
+
rsmp_versions
|
|
49
|
+
intervals
|
|
50
|
+
timeouts
|
|
51
|
+
components
|
|
52
|
+
skip_validation
|
|
53
|
+
]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'rsmp'
|
|
2
|
+
|
|
3
|
+
module RSMP
|
|
4
|
+
module Validator
|
|
5
|
+
module SupervisorTest
|
|
6
|
+
# Configuration options for supervisor testing.
|
|
7
|
+
class Options < RSMP::Options
|
|
8
|
+
def schema_file
|
|
9
|
+
'supervisor_test.json'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def schema_path
|
|
13
|
+
File.expand_path("../../../schemas/#{schema_file}", __dir__)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def apply_defaults(options)
|
|
19
|
+
return options unless options.is_a?(Hash)
|
|
20
|
+
|
|
21
|
+
local_site = options['local_site']
|
|
22
|
+
return options unless local_site.is_a?(Hash)
|
|
23
|
+
|
|
24
|
+
%w[
|
|
25
|
+
sxls
|
|
26
|
+
core_version
|
|
27
|
+
rsmp_versions
|
|
28
|
+
intervals
|
|
29
|
+
timeouts
|
|
30
|
+
components
|
|
31
|
+
skip_validation
|
|
32
|
+
].each do |key|
|
|
33
|
+
value = options[key]
|
|
34
|
+
next if value.nil? || local_site.key?(key)
|
|
35
|
+
|
|
36
|
+
local_site[key] = value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
site_options_class = if local_site['type'] == 'tlc'
|
|
40
|
+
RSMP::TLC::TrafficControllerSite::Options
|
|
41
|
+
else
|
|
42
|
+
RSMP::Site::Options
|
|
43
|
+
end
|
|
44
|
+
local_site = site_options_class.new(local_site).to_h
|
|
45
|
+
|
|
46
|
+
options.merge('local_site' => local_site)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
module Validator
|
|
3
|
+
# Helper class for testing RSMP sites.
|
|
4
|
+
# Runs a local RSMP supervisor inside an Async reactor.
|
|
5
|
+
# Only one site is expected to connect to the supervisor.
|
|
6
|
+
class SiteTester < RSMP::Validator::Tester
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :instance
|
|
9
|
+
|
|
10
|
+
def connected(options = {}, &)
|
|
11
|
+
instance.connected(options, &)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def reconnected(options = {}, &)
|
|
15
|
+
instance.reconnected(options, &)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def disconnected(&)
|
|
19
|
+
instance.disconnected(&)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def isolated(options = {}, &)
|
|
23
|
+
instance.isolated(options, &)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stop
|
|
27
|
+
instance.stop
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def parse_config
|
|
32
|
+
setup_supervisor_config
|
|
33
|
+
validate_timeouts
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def setup_supervisor_config
|
|
37
|
+
@supervisor_config = config['local_supervisor']
|
|
38
|
+
raise "config 'local_supervisor' is missing" unless @supervisor_config
|
|
39
|
+
|
|
40
|
+
@supervisor_config['max_sites'] ||= 1
|
|
41
|
+
@supervisor_config['sites'] ||= {}
|
|
42
|
+
@supervisor_config['sites']['default'] ||= {}
|
|
43
|
+
apply_security_codes
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def apply_security_codes
|
|
47
|
+
security_codes = config.dig('secrets', 'security_codes')
|
|
48
|
+
return unless security_codes && !@supervisor_config['sites']['default']['security_codes']
|
|
49
|
+
|
|
50
|
+
@supervisor_config['sites']['default']['security_codes'] = security_codes
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_timeouts
|
|
54
|
+
%w[
|
|
55
|
+
connect
|
|
56
|
+
ready
|
|
57
|
+
status_response
|
|
58
|
+
status_update
|
|
59
|
+
subscribe
|
|
60
|
+
command
|
|
61
|
+
command_response
|
|
62
|
+
alarm
|
|
63
|
+
disconnect
|
|
64
|
+
].each do |key|
|
|
65
|
+
raise "config 'timeouts/#{key}' is missing" unless config['timeouts'][key]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def build_node(options)
|
|
70
|
+
logger = create_supervisor_logger(@supervisor_config)
|
|
71
|
+
supervisor_settings = ConfigNormalizer.normalize_supervisor_settings(@supervisor_config.deep_merge(options))
|
|
72
|
+
|
|
73
|
+
RSMP::Supervisor.new(
|
|
74
|
+
supervisor_settings: supervisor_settings,
|
|
75
|
+
logger: logger,
|
|
76
|
+
collect: options['collect']
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def wait_for_connection
|
|
81
|
+
@proxy = @node.proxies.first
|
|
82
|
+
return if @proxy
|
|
83
|
+
|
|
84
|
+
log 'Waiting for site to connect'
|
|
85
|
+
@proxy = @node.wait_for_site(:any, timeout: config['timeouts']['connect'])
|
|
86
|
+
rescue RSMP::TimeoutError
|
|
87
|
+
raise RSMP::ConnectionError, "Site did not connect within #{config['timeouts']['connect']}s"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def wait_for_handshake
|
|
91
|
+
return if @proxy.ready?
|
|
92
|
+
|
|
93
|
+
log 'Waiting for handshake to complete'
|
|
94
|
+
@proxy.wait_for_state :ready, timeout: config['timeouts']['ready']
|
|
95
|
+
log 'Ready'
|
|
96
|
+
return if @initial_unsubscribe_done
|
|
97
|
+
|
|
98
|
+
@proxy.unsubscribe_from_all component: RSMP::Validator.get_config('main_component')
|
|
99
|
+
@initial_unsubscribe_done = true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def create_supervisor_logger(supervisor_config)
|
|
103
|
+
log_settings = supervisor_config.is_a?(Hash) ? supervisor_config['log'] : nil
|
|
104
|
+
logger_settings = RSMP::Validator.node_log_settings.merge('prefix' => '[SUPERVISOR]')
|
|
105
|
+
return RSMP::Logger.new(logger_settings) unless log_settings && !log_settings.empty?
|
|
106
|
+
|
|
107
|
+
logger_settings.merge!(log_settings)
|
|
108
|
+
logger_settings.delete('stream') if log_settings['path']
|
|
109
|
+
RSMP::Logger.new(logger_settings)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'rsmp'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
require 'colorize'
|
|
4
|
+
|
|
5
|
+
module RSMP
|
|
6
|
+
module Validator
|
|
7
|
+
# Helper class for testing RSMP supervisors.
|
|
8
|
+
# Runs a local RSMP site inside an Async reactor.
|
|
9
|
+
class SupervisorTester < RSMP::Validator::Tester
|
|
10
|
+
class << self
|
|
11
|
+
attr_accessor :instance
|
|
12
|
+
|
|
13
|
+
def connected(options = {}, &)
|
|
14
|
+
instance.connected(options, &)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def reconnected(options = {}, &)
|
|
18
|
+
instance.reconnected(options, &)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def disconnected(&)
|
|
22
|
+
instance.disconnected(&)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def isolated(options = {}, &)
|
|
26
|
+
instance.isolated(options, &)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def parse_config
|
|
31
|
+
@site_config = config['local_site']
|
|
32
|
+
raise "config 'local_site' is missing" unless @site_config
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_node(options)
|
|
36
|
+
klass = case @site_config['type']
|
|
37
|
+
when 'tlc'
|
|
38
|
+
RSMP::TLC::TrafficControllerSite
|
|
39
|
+
else
|
|
40
|
+
RSMP::Site
|
|
41
|
+
end
|
|
42
|
+
site_settings = ConfigNormalizer.normalize_site_settings(@site_config.deep_merge(options))
|
|
43
|
+
logger = create_site_logger(@site_config)
|
|
44
|
+
@site = klass.new(
|
|
45
|
+
site_settings: site_settings,
|
|
46
|
+
logger: logger,
|
|
47
|
+
collect: options['collect']
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def wait_for_connection
|
|
52
|
+
log 'Waiting for connection to supervisor'
|
|
53
|
+
@proxy = @node.find_supervisor :any
|
|
54
|
+
@proxy.wait_for_state %i[connected ready], timeout: config['timeouts']['connect']
|
|
55
|
+
rescue RSMP::TimeoutError
|
|
56
|
+
raise RSMP::ConnectionError, "Could not connect to supervisor within #{config['timeouts']['connect']}s"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def wait_for_handshake
|
|
60
|
+
@proxy.wait_for_state :ready, timeout: config['timeouts']['ready']
|
|
61
|
+
rescue RSMP::TimeoutError
|
|
62
|
+
raise RSMP::ConnectionError, "Handshake didn't complete within #{config['timeouts']['ready']}s"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def create_site_logger(site_config)
|
|
66
|
+
log_settings = site_config.is_a?(Hash) ? site_config['log'] : nil
|
|
67
|
+
logger_settings = RSMP::Validator.node_log_settings.merge('prefix' => '[TLC] ')
|
|
68
|
+
return RSMP::Logger.new(logger_settings) unless log_settings && !log_settings.empty?
|
|
69
|
+
|
|
70
|
+
logger_settings.merge!(log_settings)
|
|
71
|
+
logger_settings.delete('stream') if log_settings['path']
|
|
72
|
+
RSMP::Logger.new(logger_settings)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'rsmp'
|
|
2
|
+
require 'colorize'
|
|
3
|
+
|
|
4
|
+
module RSMP
|
|
5
|
+
module Validator
|
|
6
|
+
# Base class for testing either a site or a supervisor.
|
|
7
|
+
# Handles running the corresponding local site/supervisor inside an Async reactor.
|
|
8
|
+
class Tester
|
|
9
|
+
include RSMP::Validator::Log
|
|
10
|
+
|
|
11
|
+
def self.sentinel_errors
|
|
12
|
+
@sentinel_errors ||= []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def config
|
|
16
|
+
RSMP::Validator.config
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Ensures that the site is connected.
|
|
20
|
+
# If the site is already connected, the block will be called immediately.
|
|
21
|
+
# Otherwise waits until the site is connected before calling the block.
|
|
22
|
+
def connected(options = {})
|
|
23
|
+
start options, 'Connecting'
|
|
24
|
+
wait_for_proxy
|
|
25
|
+
yield Async::Task.current, @node, @proxy
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Disconnects the site if connected, then waits until the site is connected
|
|
29
|
+
# before calling the block.
|
|
30
|
+
def reconnected(options = {})
|
|
31
|
+
stop 'Reconnecting'
|
|
32
|
+
start options
|
|
33
|
+
wait_for_proxy
|
|
34
|
+
yield Async::Task.current, @node, @proxy
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Like connected, except that the connection is closed after the test.
|
|
38
|
+
def isolated(options = {})
|
|
39
|
+
stop 'Isolating'
|
|
40
|
+
start options, 'Connecting'
|
|
41
|
+
wait_for_proxy
|
|
42
|
+
yield Async::Task.current, @node, @proxy
|
|
43
|
+
stop 'Isolating'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Disconnects the site if connected before calling the block.
|
|
47
|
+
def disconnected
|
|
48
|
+
stop 'Disconnecting'
|
|
49
|
+
yield Async::Task.current
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Stop the rsmp supervisor
|
|
53
|
+
def stop(why = nil)
|
|
54
|
+
if @node
|
|
55
|
+
log why if why
|
|
56
|
+
@node.ignore_errors RSMP::DisconnectError do
|
|
57
|
+
@node.stop
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
@node = nil
|
|
61
|
+
@proxy = nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def initialize
|
|
67
|
+
parse_config
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Start the tester node inside an async task that will persist between tests.
|
|
71
|
+
def start(options = {}, _why = nil)
|
|
72
|
+
return if @node
|
|
73
|
+
|
|
74
|
+
RSMP::Validator.reactor.async do |task|
|
|
75
|
+
task.annotate 'node runner'
|
|
76
|
+
|
|
77
|
+
@node = build_node options
|
|
78
|
+
|
|
79
|
+
RSMP::Validator.reactor.async do |sentinel|
|
|
80
|
+
sentinel.annotate 'sentinel'
|
|
81
|
+
while @node
|
|
82
|
+
e = @node.error_queue.dequeue
|
|
83
|
+
log "Sentinel warning: #{e.class}: #{e}"
|
|
84
|
+
self.class.sentinel_errors << e
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
@node.start
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Wait until communication has been established, and handshake completed.
|
|
93
|
+
def wait_for_proxy
|
|
94
|
+
wait_for_connection
|
|
95
|
+
wait_for_handshake
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def parse_config; end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
# Main module for RSMP Validator functionality.
|
|
3
|
+
module Validator
|
|
4
|
+
# Helpers for filtering tests by RSMP core and SXL version.
|
|
5
|
+
module VersionFilter
|
|
6
|
+
# Check if the configured SXL version satisfies the given requirement.
|
|
7
|
+
# @param requirement [String] Gem::Requirement-compatible string, e.g. ">= 1.0.7"
|
|
8
|
+
def self.sxl_matches?(requirement, name: nil)
|
|
9
|
+
sxl = configured_sxl(name)
|
|
10
|
+
version_satisfies?(requirement, sxl && sxl['version'])
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Check if the configured core version satisfies the given requirement.
|
|
14
|
+
# @param requirement [String] Gem::Requirement-compatible string, e.g. ">= 3.2"
|
|
15
|
+
def self.core_matches?(requirement)
|
|
16
|
+
version_satisfies?(requirement, RSMP::Validator.config['core_version'])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Helper that does the version comparison.
|
|
20
|
+
def self.version_satisfies?(requirement, version_str)
|
|
21
|
+
return false unless version_str
|
|
22
|
+
|
|
23
|
+
version = Gem::Version.new(version_str)
|
|
24
|
+
Gem::Requirement.new(requirement).satisfied_by?(version)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.configured_sxl(name = nil)
|
|
28
|
+
sxls = RSMP::Validator.config['sxls'] || []
|
|
29
|
+
return sxls.first unless name
|
|
30
|
+
|
|
31
|
+
sxls.find { |sxl| sxl['name'] == name.to_s }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Convenience module methods delegating to VersionFilter
|
|
36
|
+
def self.sxl_matches?(requirement, name: nil)
|
|
37
|
+
VersionFilter.sxl_matches?(requirement, name: name)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.core_matches?(requirement)
|
|
41
|
+
VersionFilter.core_matches?(requirement)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'rsmp'
|
|
2
|
+
require 'colorize'
|
|
3
|
+
require_relative 'validator/version'
|
|
4
|
+
require_relative 'validator/log'
|
|
5
|
+
require_relative 'validator/options/site_test_options'
|
|
6
|
+
require_relative 'validator/options/supervisor_test_options'
|
|
7
|
+
require_relative 'validator/config_normalizer'
|
|
8
|
+
require_relative 'validator/configuration'
|
|
9
|
+
require_relative 'validator/version_filter'
|
|
10
|
+
require_relative 'validator/lifecycle'
|
|
11
|
+
require_relative 'validator/mode_detection'
|
|
12
|
+
require_relative 'validator/tester'
|
|
13
|
+
require_relative 'validator/site_tester'
|
|
14
|
+
require_relative 'validator/supervisor_tester'
|
|
15
|
+
require_relative 'validator/auto_node'
|
|
16
|
+
require_relative 'validator/auto_site'
|
|
17
|
+
require_relative 'validator/auto_supervisor'
|
|
18
|
+
require_relative 'validator/async_context'
|
|
19
|
+
require_relative 'validator/helpers/connection'
|
|
20
|
+
require_relative 'validator/helpers/status'
|
|
21
|
+
require_relative 'validator/helpers/input'
|
|
22
|
+
require_relative 'validator/helpers/clock'
|
|
23
|
+
require_relative 'validator/helpers/security'
|
|
24
|
+
require_relative 'validator/helpers/signal_plans'
|
|
25
|
+
require_relative 'validator/helpers/alarms'
|
|
26
|
+
require_relative 'validator/helpers/startup'
|
|
27
|
+
require_relative 'validator/helpers/handshake'
|
|
28
|
+
require_relative 'validator/helpers/signal_priority'
|
|
29
|
+
|
|
30
|
+
module RSMP
|
|
31
|
+
# Main module for RSMP Validator functionality.
|
|
32
|
+
# Handles configuration, logging, and coordination between sus and the RSMP gem.
|
|
33
|
+
module Validator
|
|
34
|
+
extend Configuration
|
|
35
|
+
extend Lifecycle
|
|
36
|
+
extend ModeDetection
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
include RSMP::Logging
|
|
40
|
+
|
|
41
|
+
attr_accessor :config, :config_log_settings, :mode, :logger, :auto_node_config,
|
|
42
|
+
:auto_node_log_settings, :auto_node, :node_log_settings
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get the global Async reactor used for RSMP communication
|
|
46
|
+
def self.reactor
|
|
47
|
+
@reactor
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "site_test.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"local_supervisor": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"log": { "type": "object" }
|
|
10
|
+
},
|
|
11
|
+
"additionalProperties": true
|
|
12
|
+
},
|
|
13
|
+
"core_version": { "type": "string" },
|
|
14
|
+
"sxls": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"propertyNames": { "not": { "const": "core" } },
|
|
17
|
+
"additionalProperties": { "type": "string" }
|
|
18
|
+
},
|
|
19
|
+
"rsmp_versions": {
|
|
20
|
+
"oneOf": [
|
|
21
|
+
{ "type": "string" },
|
|
22
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"timeouts": { "type": "object" },
|
|
26
|
+
"components": { "type": "object" },
|
|
27
|
+
"items": { "type": "object" },
|
|
28
|
+
"startup_sequence": { "type": ["string", "null"] },
|
|
29
|
+
"skip_validation": { "type": "array", "items": { "type": "string" } },
|
|
30
|
+
"secrets": { "type": "object" },
|
|
31
|
+
"alarm_triggers": { "type": "object" },
|
|
32
|
+
"log": { "type": "object" }
|
|
33
|
+
},
|
|
34
|
+
"required": ["local_supervisor"],
|
|
35
|
+
"additionalProperties": true
|
|
36
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "supervisor_test.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"local_site": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"log": { "type": "object" }
|
|
10
|
+
},
|
|
11
|
+
"additionalProperties": true
|
|
12
|
+
},
|
|
13
|
+
"core_version": { "type": "string" },
|
|
14
|
+
"sxls": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"propertyNames": { "not": { "const": "core" } },
|
|
17
|
+
"additionalProperties": { "type": "string" }
|
|
18
|
+
},
|
|
19
|
+
"timeouts": { "type": "object" },
|
|
20
|
+
"components": { "type": "object" },
|
|
21
|
+
"items": { "type": "object" },
|
|
22
|
+
"skip_validation": { "type": "array", "items": { "type": "string" } },
|
|
23
|
+
"secrets": { "type": "object" },
|
|
24
|
+
"log": { "type": "object" }
|
|
25
|
+
},
|
|
26
|
+
"required": ["local_site"],
|
|
27
|
+
"additionalProperties": true
|
|
28
|
+
}
|