Explorar o código

add test for state update gaps

Christopher Mullins %!s(int64=6) %!d(string=hai) anos
pai
achega
67f08b9613
Modificáronse 3 ficheiros con 61 adicións e 3 borrados
  1. 7 1
      README.md
  2. 8 1
      test/remote/lib/mqtt_client.rb
  3. 46 1
      test/remote/spec/mqtt_spec.rb

+ 7 - 1
README.md

@@ -291,9 +291,11 @@ You can select between versions 5 and 6 of the UDP protocol (documented [here](h
 
 ## Development
 
+This project is developed and built using [PlatformIO](https://platformio.org/).
+
 #### Running tests
 
-Run unit tests with this command:
+On-board unit tests are available using PlatformIO.  Run unit tests with this command:
 
 ```
 pio test -e d1_mini
@@ -301,6 +303,10 @@ pio test -e d1_mini
 
 substituting `d1_mini` for the environment of your choice.
 
+#### Running integration tests
+
+A remote integration test suite built using rspec is available under [`./test/remote`](test/remote).
+
 ## Acknowledgements
 
 * @WoodsterDK added support for LT8900 radios.

+ 8 - 1
test/remote/lib/mqtt_client.rb

@@ -20,6 +20,11 @@ class MqttClient
     @client.connect
   end
 
+  def wait_for_message(topic, timeout = 10)
+    on_message(topic, timeout) { |topic, message| }
+    wait_for_listeners
+  end
+
   def id_topic_suffix(params)
     if params
       "#{sprintf '0x%04X', params[:id]}/#{params[:type]}/#{params[:group_id]}"
@@ -61,7 +66,9 @@ class MqttClient
             raise BreakListenLoopError if yield(topic, message)
           end
         end
-      rescue Timeout::Error
+      rescue Timeout::Error => e
+        puts "Timed out listening for message on: #{topic}"
+        puts e.backtrace.join("\n")
       rescue BreakListenLoopError
       end
     end

+ 46 - 1
test/remote/spec/mqtt_spec.rb

@@ -7,6 +7,7 @@ RSpec.describe 'State' do
     @client.upload_json('/settings', 'settings.json')
 
     @topic_prefix = ENV.fetch('ESPMH_MQTT_TOPIC_PREFIX')
+    @updates_topic = "#{@topic_prefix}updates/:device_id/:device_type/:group_id"
 
     @client.put(
       '/settings', 
@@ -15,7 +16,7 @@ RSpec.describe 'State' do
       mqtt_password: ENV.fetch('ESPMH_MQTT_PASSWORD'),
       mqtt_topic_pattern: "#{@topic_prefix}commands/:device_id/:device_type/:group_id",
       mqtt_state_topic_pattern: "#{@topic_prefix}state/:device_id/:device_type/:group_id",
-      mqtt_update_topic_pattern: "#{@topic_prefix}updates/:device_id/:device_type/:group_id",
+      mqtt_update_topic_pattern: @updates_topic
     )
 
     @mqtt_client = MqttClient.new(
@@ -111,5 +112,49 @@ RSpec.describe 'State' do
 
       expect(accumulated_state).to eq(desired_state)
     end
+
+    it 'should respect the state update interval' do
+      # Wait for MQTT to reconnect
+      @mqtt_client.on_message("#{@topic_prefix}birth") { |x, y| true }
+
+      # Disable updates to prevent the negative effects of spamming commands
+      @client.put(
+        '/settings', 
+        mqtt_update_topic_pattern: '',
+        mqtt_state_rate_limit: 500
+      )
+
+      @mqtt_client.wait_for_listeners
+
+      # Set initial state
+      @client.patch_state({status: 'ON', level: 0}, @id_params)
+
+      last_seen = 0
+      update_timestamp_gaps = []
+      num_updates = 20
+
+      @mqtt_client.on_state(@id_params) do |id, message|
+        next_time = Time.now
+        if last_seen != 0
+          update_timestamp_gaps << next_time - last_seen
+        end
+        last_seen = next_time
+
+        message['level'] == num_updates
+      end
+
+      (1..num_updates).each do |i|
+        @mqtt_client.patch_state(@id_params, level: i)
+        sleep 0.1
+      end
+
+      @mqtt_client.wait_for_listeners
+
+      # Discard first, retained messages mess with it
+      avg = update_timestamp_gaps.sum / update_timestamp_gaps.length
+
+      expect(update_timestamp_gaps.length).to be >= 3
+      expect(avg).to be >= 0.5
+    end
   end
 end