From 815ceea5721a65243aa9f7b6cb5ac24ad1b0d14b Mon Sep 17 00:00:00 2001 From: Emil Tin Date: Fri, 9 Aug 2024 09:48:12 +0200 Subject: [PATCH] wip --- spec/site/tlc/signal_priority_spec.rb | 170 ++++++++++++++------------ spec/support/priority_helper.rb | 163 ++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 77 deletions(-) create mode 100644 spec/support/priority_helper.rb diff --git a/spec/site/tlc/signal_priority_spec.rb b/spec/site/tlc/signal_priority_spec.rb index fc9d56bd..04770115 100644 --- a/spec/site/tlc/signal_priority_spec.rb +++ b/spec/site/tlc/signal_priority_spec.rb @@ -52,92 +52,108 @@ end end - # Validate that signal priority status are send when priorty is requested + # Validate that a signal priority becomes completed when we cancel it. # # 1. Given the site is connected # 2. And we subscribe to signal priority status - # 2. When we send a signal priority request - # 3. Then we should receive status updates - it 'state goes through received, activated, completed', sxl: '>=1.1' do |example| + # 3. When we send a signal priority request + # 4. Then the request state should become 'received' + # 5. Then the request state should become 'activated' + # 6. When we cancel the request + # 7. Then the state should become 'completed' + + it 'state reaches completed', sxl: '>=1.1' do |example| Validator::Site.connected do |task,supervisor,site| + sequence = ['received','activated','completed'] - # subscribe - component = Validator.get_config('main_component') - log "Subscribing to signal priority request status updates" - status_list = [{'sCI'=>'S0033','n'=>'status','uRt'=>'0'}] - status_list.map! { |item| item.merge!('sOc' => true) } if use_sOc?(site) - site.subscribe_to_status component, status_list - - # start collector - request_id = SecureRandom.uuid()[0..3] # make a message id - num = sequence.length - states = [] - result = nil - collector = nil - collect_task = task.async do - collector = RSMP::Collector.new( - site, - type: "StatusUpdate", - num: num, - timeout: Validator.get_config('timeouts','priority_completion'), - component: component - ) - - def search_for_request_state request_id, message, states - message.attribute('sS').each do |status| - return nil unless status['sCI'] == 'S0033' && status['n'] == 'status' - status['s'].each do |priority| - next unless priority['r'] == request_id # is this our request - state = priority['s'] - next unless state != states.last # did the state change? - log "Priority request reached state '#{state}'" - return state - end - end - nil - end - - result = collector.collect do |message| - state = search_for_request_state request_id, message, states - next unless state - states << state - :keep - end - end + timeout = Validator.get_config('timeouts','priority_completion'), + + prio = Validator::StatusHelpers::PriorityHelper.new(site: site, component: component) - def send_priority_request log, id:nil, site:, component: - # send an unrelated request before our request, to check that it does not interfere - log log - signal_group = Validator.get_config('components','signal_group').keys.first - command_list = build_command_list :M0022, :requestPriority, { - requestId: (id || SecureRandom.uuid()[0..3]), - signalGroupId: signal_group, - type: 'new', - level: 7, - eta: 2, - vehicleType: 'car' - } - site.send_command component, command_list + prio_options = { + signalGroupId: signal_group, + level: 7, + eta: 2, + vehicleType: 'car' + } + + prio.validate_sequence do |state| + case state + when nil: + log "Send an unrelated signal priority request before" + prio.request_unrelated prio_options + log "Send signal priority request, wait for reception." + prio.request prio_options + log "Send an unrelated signal priority request after" + prio.request_unrelated prio_options + prio.next 'received', timeout: timeout + + when 'received': + log "Signal priority request was received, wait for activation." + prio.next 'activated', timeout: timeout + + when 'acticated': + log "Signal priority request was activated, now cancel it and wait for completion." + prio.cancel + prio.next 'completed', timeout: timeout + + when 'completed': + log "Signal priority request was completed." + prio.done end + end + end - send_priority_request "Send an unrelated signal priority request before", - site: site, component: component - send_priority_request "Send our signal priority request", - site: site, component: component, id: request_id - send_priority_request "Send an unrelated signal priority request after", - site: site, component: component - - # wait for collector to complete and check result - collect_task.wait - expect(result).to eq(:ok) - expect(collector.messages).to be_an(Array) - expect(collector.messages.size).to eq(num) - expect(states).to eq(sequence), "Expected state sequence #{sequence}, got #{states}" - ensure - # unsubcribe - unsubscribe_list = status_list.map { |item| item.slice('sCI','n') } - site.unsubscribe_to_status component, unsubscribe_list + # Validate that a signal priority became stale if we do not cancel it. + # + # 1. Given the site is connected + # 2. And we subscribe to signal priority status + # 3. When we send a signal priority request + # 4. Then the request state should become 'received' + # 5. And the request state should become 'activated' + # 7. Then the state should become 'stale' + + it 'state becomes stale', sxl: '>=1.1' do |example| + Validator::Site.connected do |task,supervisor,site| + sequence = ['received','activated','completed'] + timeout = Validator.get_config('timeouts','priority_completion'), + component = Validator.get_config('main_component') + signal_group = Validator.get_config('components','signal_group').keys.first + + prio = Validator::StatusHelpers::PriorityHelper.new(site: site, component: component) + + prio_options = { + signalGroupId: signal_group, + level: 7, + eta: 2, + vehicleType: 'car' + } + + prio.validate_sequence do |state| + case state + when nil: + log "Send an unrelated signal priority request before" + prio.request_unrelated prio_options + log "Send signal priority request, wait for reception." + prio.request prio_options + log "Send an unrelated signal priority request after" + prio.request_unrelated prio_options + prio.next 'received', timeout: timeout + + when 'received': + log "Signal priority request was received, wait for activation." + prio.next 'activated', timeout: timeout + + when 'acticated': + log "Signal priority request was activated, wait for it to become stale." + prio.next 'completed', timeout: timeout + + when 'stale': + log "Signal priority request became stale." + prio.done + end end end + end end diff --git a/spec/support/priority_helper.rb b/spec/support/priority_helper.rb new file mode 100644 index 00000000..e17c6fd1 --- /dev/null +++ b/spec/support/priority_helper.rb @@ -0,0 +1,163 @@ + +module RSMP + class Sequencer < Listener + + def initialize notifier:, component:, filter:, task:, timeout: + @notifier = notifier + @filter = filter + @task = task || notifier.task + @condition = Async::Notification.new + @timeout = timeout + @state = nil + reset + end + + def reset + @next = nil + @messages = [] + @done = false + end + + # Waits for message and passes each message accept by the filter + # to the block. + # The block can do any validation of the + # + # + def validate &block + reset + @notifier.add_listener self + until @done + yield wait_for_message + end + ensure + @notifier.remove_listener self + end + + def wait_for_message + if @messages.empty? + @task.with_timeout(@timeout) { @condition.wait } + end + @messages.shift + rescue Async::TimeoutError + @error = RSMP::TimeoutError + end + + def notify message + if @filter.accept? messages + @messages.push messages + @condition.notifify + end + end + end + + def done + @done = true + end +end + + +module Validator::StatusHelpers + class SignalPriorityHelper < RSMP::Listener + + def request + end + + def request_unrelated + end + + def next state + @next = state + end + + def done + @done = true + end + + private + + def send_priority_request id: nil, type: 'new' + command_list = build_command_list :M0022, :requestPriority, { + requestId: (id || SecureRandom.uuid()[0..3]), + signalGroupId: signal_group, + type: type, + level: 7, + eta: 2, + vehicleType: 'car' + } + site.send_command component, command_list + end + + # look through a status message to find state + # updates for a specific priority request + def search_for_request_state request_id, message, states + message.attribute('sS').each do |status| + return nil unless status['sCI'] == 'S0033' && status['n'] == 'status' + status['s'].each do |priority| + next unless priority['r'] == request_id # is this our request + state = priority['s'] + next unless state != states.last # did the state change? + log "Priority request reached state '#{state}'" + return state + end + end + nil + end + end +end + + + + +# @request_id = SecureRandom.uuid()[0..3] + + + status_list = [{'sCI'=>'S0033','n'=>'status','uRt'=>'0'}] + status_list.map! { |item| item.merge!('sOc' => true) } if use_sOc?(site) + site.subscribe_to_status component, status_list + + # start collector + request_id = SecureRandom.uuid()[0..3] # make a message id + num = sequence.length + states = [] + result = nil + collector = nil + collect_task = task.async do + collector = RSMP::Collector.new( + site, + type: "StatusUpdate", + num: num, + timeout: Validator.get_config('timeouts','priority_completion'), + component: component + ) + + + result = collector.collect do |message| + state = search_for_request_state request_id, message, states + next unless state + states << state + :keep + end + end + + # helper to send priority request + + + #send unrelated requests before and our request, to check that it does not interfere + send_priority_request "Send an unrelated signal priority request before", + site: site, component: component + send_priority_request "Send our signal priority request", + site: site, component: component, id: request_id + send_priority_request "Send an unrelated signal priority request after", + site: site, component: component + + # wait for collector to complete and check result + collect_task.wait + expect(result).to eq(:ok) + expect(collector.messages).to be_an(Array) + expect(collector.messages.size).to eq(num) + expect(states).to eq(sequence), "Expected state sequence #{sequence}, got #{states}" + ensure + # unsubcribe + unsubscribe_list = status_list.map { |item| item.slice('sCI','n') } + site.unsubscribe_to_status component, unsubscribe_list + end