Class: MQTT::Testing::SubHandler

Inherits:
SubHandler show all
Defined in:
lib/mqtt/sub_testing.rb

Overview

This class is meant purely for testing.

It completely removes the need for a external MQTT broker, and captures errors.
Message processing can be done step-by-step, for better analysis of errors.
Its interface is identical to the main class, making it indistinguishable.

Instance Attribute Summary collapse

Attributes inherited from SubHandler

#jsonifyHashes

Instance Method Summary collapse

Methods inherited from SubHandler

#lockAndListen, #subscribe_to, #track, #wait_for

Methods inherited from BaseHandler

#destroy!, getTopicMatch, get_topic_split, #register_subscription, #unregister_subscription

Constructor Details

#initialize(jsonify: true, test_handler: nil) ⇒ SubHandler

Initialize the test class

Parameters:

  • jsonify (Boolean) (defaults to: true)

    Whether or not Hashes and Arrays should be converted to JSON before sending.

  • test_handler (nil, MiniTest::Test) (defaults to: nil)

    The test handler to use to report errors or pass sanity checks. Must support flunk and pass!



129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/mqtt/sub_testing.rb', line 129

def initialize(jsonify: true, test_handler: nil)
  @callbackList    = Array.new();
  @retained_topics = Hash.new();
  @publish_queue   = Queue.new();
  @message_log   = Hash.new() do |h, key| h = Array.new() end;

  @trackerHash = Hash.new();

  @jsonifyHashes = jsonify;

  @test_handler = test_handler;
end

Instance Attribute Details

#message_logObject (readonly)

Returns the value of attribute message_log.



12
13
14
# File 'lib/mqtt/sub_testing.rb', line 12

def message_log
  @message_log
end

#publish_queueObject (readonly)

Returns the value of attribute publish_queue.



13
14
15
# File 'lib/mqtt/sub_testing.rb', line 13

def publish_queue
  @publish_queue
end

#retained_topicsObject

Returns the value of attribute retained_topics.



14
15
16
# File 'lib/mqtt/sub_testing.rb', line 14

def retained_topics
  @retained_topics
end

#test_handlerObject

Returns the value of attribute test_handler.



16
17
18
# File 'lib/mqtt/sub_testing.rb', line 16

def test_handler
  @test_handler
end

Instance Method Details

#assert_garbage_resilientObject



87
88
89
90
91
92
93
94
95
96
# File 'lib/mqtt/sub_testing.rb', line 87

def assert_garbage_resilient()
  @callbackList.each do |c|
    t = Array.new() {|a,k| a[k] = SecureRandom.random_bytes(100)}
    c.offer(t, SecureRandom.random_bytes(100));
  end

  if(@test_handler)
    @test_handler.pass("No garbage message problem detected.");
  end
end

#full_resetObject

Perform a complete reset of the testing unit, clearing out all

queues and removing all callbacks. This should mainly be used
inside the teardown or setup routines, before creating a new
testing instance, to prevent uncleaned garbage.


119
120
121
122
# File 'lib/mqtt/sub_testing.rb', line 119

def full_reset()
  @callbackList.clear();
  prepare();
end

#prepare(retained: true) ⇒ Object

Prepare the code for the next test by cleaning out all queues.

The list of callbacks is not affected

Parameters:

  • retained (Boolean, Hash<String, String>) (defaults to: true)

    Either a bool whether or not to clear retained messages, or a Hash with Topic-Keys containing String-Data to use.



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/mqtt/sub_testing.rb', line 102

def prepare(retained: true)
  @publish_queue.clear();
  if(retained.is_a? Hash)
    @retained_topics = retained.clone
    @retained_topics.each do |topic, data|
      publish_to(topic, data);
    end
  elsif(retained)
    @retained_topics = Hash.new();
  end
  @message_log.clear()
end

#process_all(max_loops: 500, error_on_loop: true) ⇒ Object

Process all messages until the queue is empty.

Do remember that the callbacks can publish new data, which then gets processed again!

Parameters:

  • max_loops (Integer) (defaults to: 500)

    Amount of loops to do before aborting

  • error_on_loop (Boolean) (defaults to: true)

    Raise an error if too many loops happened?



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/mqtt/sub_testing.rb', line 65

def process_all(max_loops: 500, error_on_loop: true)
  until @publish_queue.empty?
    process_message
    max_loops -= 1;

    if(max_loops == 0)
      if(error_on_loop)
        if(@test_handler)
          @test_handler.flunk("MQTT Loop recursion detected")
        else
          raise RuntimeError, "MQTT Loop recursion detected"
        end
      end
      return
    end
  end

  if(error_on_loop and @test_handler)
    @test_handler.pass("No MQTT Loop recursion.");
  end
end

#process_messageObject

Process a single MQTT message in queue, recording errors etc.



55
56
57
58
59
# File 'lib/mqtt/sub_testing.rb', line 55

def process_message
  return if @publish_queue.empty?
  packet = @publish_queue.pop
  call_interested(packet[0], packet[1]);
end

#publish_to(topic, data, qos: nil, retain: false) ⇒ Object

Note:

The published data is not immediately processed. Use process_message or process_all

Publish a message to topic.

Parameters:

  • topic (String)

    The topic to push to.

  • data (String)

    The data to be transmitted.



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/mqtt/sub_testing.rb', line 42

def publish_to(topic, data, qos: nil, retain: false)
  if(@jsonifyHashes and (data.is_a? Array or data.is_a? Hash))
    data = data.to_json
  else
    data = data.to_s;
  end

  @publish_queue     << [topic, data];
  @message_log[topic]  << data;
  @retained_topics[topic] = data if retain;
end