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

getTopicMatch, get_topic_split, #lockAndListen, #register_subscription, #subscribe_to, #track, #unregister_subscription, #wait_for

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