mirror of
https://github.com/eddieoz/LoRa-Mesh-Analyzer.git
synced 2026-03-28 17:42:59 +01:00
253 lines
9.7 KiB
Python
253 lines
9.7 KiB
Python
import unittest
|
|
from unittest.mock import Mock
|
|
from mesh_analyzer.active_tests import ActiveTester
|
|
|
|
|
|
class TestHopCountCalculation(unittest.TestCase):
|
|
"""
|
|
Tests to verify that hop counts are correctly calculated.
|
|
A hop count should equal the number of intermediate nodes (excluding source and destination).
|
|
"""
|
|
|
|
def setUp(self):
|
|
# Mock interface
|
|
self.mock_interface = Mock()
|
|
self.tester = ActiveTester(
|
|
interface=self.mock_interface,
|
|
local_node_id="!42bb5074"
|
|
)
|
|
|
|
def test_user_example_forward_route(self):
|
|
"""
|
|
Test with the exact example from the user:
|
|
Route to destination: !42bb5074 --> !433c9a58 --> !51165eae --> !433f0ea4
|
|
Intermediate hops: !433c9a58, !51165eae = 2 hops
|
|
|
|
The route array excludes the source (!42bb5074) but includes destination (!433f0ea4)
|
|
So route = [!433c9a58, !51165eae, !433f0ea4] (3 elements)
|
|
Expected hops = len(route) - 2 = 3 - 2 = 1... wait that's wrong
|
|
|
|
Actually, checking the log output format more carefully:
|
|
"Route to !433f0ea4: !433c9a58 -> !51165eae (1 hops)"
|
|
|
|
This suggests the route array does NOT include the source or destination,
|
|
only the intermediate nodes. So:
|
|
route = [!433c9a58, !51165eae] (2 elements)
|
|
|
|
But user says it should be 2 hops, and currently shows 1 hop.
|
|
With len(route) - 1, we get: 2 - 1 = 1 hop ✓ (matches current wrong output)
|
|
With len(route) - 2, we get: 2 - 2 = 0 hops ✗ (wrong!)
|
|
|
|
Wait, let me re-read the logs more carefully:
|
|
"Route traced towards destination:"
|
|
"!42bb5074 --> !433c9a58 (-19.5dB) --> !51165eae (-14.0dB) --> !433f0ea4 (-14.75dB)"
|
|
|
|
This shows the FULL path including source and destination.
|
|
|
|
"Route to !433f0ea4: !433c9a58 -> !51165eae (1 hops)"
|
|
|
|
This shows only intermediate nodes in the route array: [!433c9a58, !51165eae]
|
|
But user says it should be 2 hops.
|
|
|
|
Hmm, let me count the intermediate nodes:
|
|
- From !42bb5074 to !433c9a58: 1st hop
|
|
- From !433c9a58 to !51165eae: 2nd hop
|
|
- From !51165eae to !433f0ea4: 3rd hop (to destination)
|
|
|
|
So there are 2 intermediate NODES (!433c9a58, !51165eae).
|
|
But wait - the number of hops is the number of links, not nodes.
|
|
|
|
Actually, re-reading the user's clarification:
|
|
"where: Route to !433f0ea4: !433c9a58 -> !51165eae (1 hops) should be 2 hops"
|
|
|
|
Ah! The route shows [!433c9a58, !51165eae] which represents the path.
|
|
The destination !433f0ea4 is NOT in this array.
|
|
|
|
So to count hops:
|
|
- Source (!42bb5074) to !433c9a58: 1 hop
|
|
- !433c9a58 to !51165eae: 1 hop
|
|
- !51165eae to !433f0ea4: 1 hop
|
|
Total: 3 hops
|
|
|
|
But user says it should be 2 hops. Let me check the "Route back":
|
|
"Route back: !9ea09e84 -> !51165eae -> !7b778173 (2 hops) should be 3 hops"
|
|
|
|
Route back has 3 nodes [!9ea09e84, !51165eae, !7b778173]
|
|
Currently shows 2 hops (len - 1 = 3 - 1 = 2)
|
|
Should be 3 hops
|
|
|
|
So with len - 2, we'd get: 3 - 2 = 1 hop (wrong!)
|
|
|
|
Wait, I think I'm confusing myself. Let me think about what "hops" means.
|
|
|
|
Looking at "Route traced back to us:"
|
|
"!433f0ea4 --> 9ea09e84 (-5.75dB) --> !51165eae (-5.25dB) --> !7b778173 (1.75dB) --> !42bb5074 (6.5dB)"
|
|
|
|
This is the FULL route with 5 nodes total.
|
|
Source: !433f0ea4
|
|
Destination: !42bb5074
|
|
Intermediate: 9ea09e84, !51165eae, !7b778173
|
|
|
|
The log says "Route back: !9ea09e84 -> !51165eae -> !7b778173 (2 hops)"
|
|
|
|
So route_back array = [9ea09e84, !51165eae, !7b778173, !42bb5074] (includes destination)
|
|
Or route_back array = [9ea09e84, !51165eae, !7b778173] (excludes destination)
|
|
|
|
With current calculation len - 1:
|
|
If array has 4 elements: 4 - 1 = 3 hops
|
|
If array has 3 elements: 3 - 1 = 2 hops ✓ (matches current output)
|
|
|
|
User says it should be 3 hops (the number of intermediate nodes).
|
|
|
|
So the array must have 4 elements: [9ea09e84, !51165eae, !7b778173, !42bb5074]
|
|
With len - 2: 4 - 2 = 2 hops ✗
|
|
With len - 1: 4 - 1 = 3 hops ✓
|
|
|
|
But wait, that would make the current code correct...
|
|
|
|
Actually, I think the issue is that the route array includes the DESTINATION but not the SOURCE.
|
|
|
|
Let me verify with the forward route:
|
|
Full path: !42bb5074 --> !433c9a58 --> !51165eae --> !433f0ea4
|
|
Route array (excluding source, including dest): [!433c9a58, !51165eae, !433f0ea4]
|
|
Current calculation: len - 1 = 3 - 1 = 2 hops
|
|
|
|
But the log shows: "Route to !433f0ea4: !433c9a58 -> !51165eae (1 hops)"
|
|
|
|
This means the route array does NOT include the destination!
|
|
Route array: [!433c9a58, !51165eae]
|
|
Current calculation: len - 1 = 2 - 1 = 1 hop ✓ (matches log)
|
|
User says it should be 2 hops
|
|
|
|
If we use len (no subtraction): 2 - 0 = 2 hops ✓
|
|
|
|
So the correct formula is just `len(route)`!
|
|
Not `len(route) - 1` and not `len(route) - 2`.
|
|
|
|
Let me verify with route back:
|
|
Full path: !433f0ea4 --> 9ea09e84 --> !51165eae --> !7b778173 --> !42bb5074
|
|
Route array (intermediate nodes only): [9ea09e84, !51165eae, !7b778173]
|
|
Current: len - 1 = 3 - 1 = 2 hops ✓ (matches log)
|
|
User: should be 3 hops
|
|
With len: 3 - 0 = 3 hops ✓
|
|
|
|
So the route array contains ONLY the intermediate relay nodes, not the source or destination!
|
|
The hop count should just be `len(route)`.
|
|
"""
|
|
# Simulate a traceroute packet with the exact data from the user's example
|
|
# Route forward: !42bb5074 -> !433c9a58 -> !51165eae -> !433f0ea4
|
|
# Intermediate nodes (what goes in route array): [!433c9a58, !51165eae]
|
|
|
|
# Convert hex IDs to integers for route array
|
|
node_433c9a58 = 0x433c9a58
|
|
node_51165eae = 0x51165eae
|
|
node_433f0ea4 = 0x433f0ea4
|
|
|
|
packet = {
|
|
'fromId': '!433f0ea4',
|
|
'decoded': {
|
|
'traceroute': {
|
|
'route': [node_433c9a58, node_51165eae], # Intermediate nodes only
|
|
'routeBack': []
|
|
}
|
|
},
|
|
'rxSnr': -14.75
|
|
}
|
|
|
|
# Record the result
|
|
self.tester.record_result('!433f0ea4', packet)
|
|
|
|
# Check that hop count was calculated correctly
|
|
results = self.tester.test_results
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]['hops_to'], 2,
|
|
"Forward route should have 2 hops (2 intermediate nodes)")
|
|
|
|
def test_user_example_return_route(self):
|
|
"""
|
|
Route back: !433f0ea4 -> 9ea09e84 -> !51165eae -> !7b778173 -> !42bb5074
|
|
Intermediate hops: 9ea09e84, !51165eae, !7b778173 = 3 hops
|
|
Route array: [9ea09e84, !51165eae, !7b778173]
|
|
"""
|
|
# Create fresh tester for this test
|
|
fresh_tester = ActiveTester(
|
|
interface=self.mock_interface,
|
|
local_node_id="!42bb5074"
|
|
)
|
|
|
|
node_9ea09e84 = 0x9ea09e84
|
|
node_51165eae = 0x51165eae
|
|
node_7b778173 = 0x7b778173
|
|
|
|
packet = {
|
|
'fromId': '!433f0ea4',
|
|
'decoded': {
|
|
'traceroute': {
|
|
'route': [],
|
|
'routeBack': [node_9ea09e84, node_51165eae, node_7b778173]
|
|
}
|
|
},
|
|
'rxSnr': 6.5
|
|
}
|
|
|
|
fresh_tester.record_result('!433f0ea4', packet)
|
|
|
|
results = fresh_tester.test_results
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]['hops_back'], 3,
|
|
"Return route should have 3 hops (3 intermediate nodes)")
|
|
|
|
def test_direct_connection(self):
|
|
"""
|
|
Test direct connection with no intermediate hops.
|
|
Route: Source -> Destination (no intermediate nodes)
|
|
Route array: []
|
|
Expected hops: 0
|
|
"""
|
|
packet = {
|
|
'fromId': '!11111111',
|
|
'decoded': {
|
|
'traceroute': {
|
|
'route': [], # Direct connection, no intermediate nodes
|
|
'routeBack': []
|
|
}
|
|
},
|
|
'rxSnr': 8.0
|
|
}
|
|
|
|
self.tester.record_result('!11111111', packet)
|
|
|
|
results = self.tester.test_results
|
|
self.assertEqual(results[0]['hops_to'], 0, "Direct connection should have 0 hops")
|
|
self.assertEqual(results[0]['hops_back'], 0, "Direct connection should have 0 hops")
|
|
|
|
def test_single_intermediate_node(self):
|
|
"""
|
|
Test single intermediate hop.
|
|
Route: Source -> Relay -> Destination
|
|
Route array: [relay_node]
|
|
Expected hops: 1
|
|
"""
|
|
relay_node = 0x22222222
|
|
|
|
packet = {
|
|
'fromId': '!33333333',
|
|
'decoded': {
|
|
'traceroute': {
|
|
'route': [relay_node], # One intermediate node
|
|
'routeBack': [relay_node]
|
|
}
|
|
},
|
|
'rxSnr': 5.0
|
|
}
|
|
|
|
self.tester.record_result('!33333333', packet)
|
|
|
|
results = self.tester.test_results
|
|
self.assertEqual(results[0]['hops_to'], 1, "Route with 1 intermediate node should have 1 hop")
|
|
self.assertEqual(results[0]['hops_back'], 1, "Route with 1 intermediate node should have 1 hop")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|