Skip to content

Commit c0875ae

Browse files
committed
create unit tests for formatter.py
1 parent 6551fe5 commit c0875ae

File tree

8 files changed

+703
-21
lines changed

8 files changed

+703
-21
lines changed

meshtastic/formatter.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,57 @@
55

66
"""Defines the formatting of outputs using factories"""
77

8-
class FormatterFactory():
8+
class FormatterFactory:
99
"""Factory of formatters"""
1010
def __init__(self):
11-
self.formatters = {
11+
self.infoFormatters = {
1212
"json": FormatAsJson,
1313
"default": FormatAsText
1414
}
1515

16-
def getFormatter(self, formatSpec: str):
16+
def getInfoFormatter(self, formatSpec: str = 'default'):
1717
"""returns the formatter for info data. If no valid formatter is found, default to text"""
18-
return self.formatters.get(formatSpec.lower(), self.formatters["default"])
18+
return self.infoFormatters.get(formatSpec.lower(), self.infoFormatters["default"])
1919

2020

21-
class InfoFormatter():
21+
class InfoFormatter:
2222
"""responsible to format info data"""
2323
def format(self, data: dict, formatSpec: str | None = None) -> str:
2424
"""returns formatted string according to formatSpec for info data"""
2525
if not formatSpec:
2626
formatSpec = 'default'
27-
formatter = FormatterFactory().getFormatter(formatSpec)
27+
formatter = FormatterFactory().getInfoFormatter(formatSpec)
2828
return formatter().formatInfo(data)
2929

3030

31-
class FormatAsJson():
31+
class AbstractFormatter:
32+
"""Abstract base class for all derived formatters"""
33+
@property
34+
def getType(self) -> str:
35+
return type(self).__name__
36+
37+
def formatInfo(self, data: dict):
38+
"""interface definition for formatting info data
39+
Need to be implemented in the derived class"""
40+
raise NotImplementedError
41+
42+
43+
class FormatAsJson(AbstractFormatter):
3244
"""responsible to return the data as JSON string"""
3345
def formatInfo(self, data: dict) -> str:
3446
"""Info as JSON"""
3547

3648
# Remove the bytes entry of PSK before serialization of JSON
37-
for c in data['Channels']:
38-
del c['psk']
49+
if 'Channels' in data:
50+
for c in data['Channels']:
51+
if '__psk__' in c:
52+
del c['__psk__']
3953
jsonData = json.dumps(data, indent=2)
4054
print(jsonData)
4155
return jsonData
4256

4357

44-
class FormatAsText():
58+
class FormatAsText(AbstractFormatter):
4559
"""responsible to print the data. No string return"""
4660
def formatInfo(self, data: dict) -> str:
4761
"""Info printed as plain text"""
@@ -62,11 +76,11 @@ def showMeshInfo(self, data: dict):
6276
owner = f"Owner: {data['Owner'][0]}({data['Owner'][1]})"
6377

6478
myinfo = ""
65-
if dx := data.get('My Info', None) is not None:
79+
if (dx := data.get('My Info', None)) is not None:
6680
myinfo = f"My info: {json.dumps(dx)}"
6781

6882
metadata = ""
69-
if dx := data.get('Metadata', None) is not None:
83+
if (dx := data.get('Metadata', None)) is not None:
7084
metadata = f"Metadata: {json.dumps(dx)}"
7185

7286
mesh = f"\nNodes in mesh:{json.dumps(data.get('Nodes', {}), indent=2)}"
@@ -76,20 +90,21 @@ def showMeshInfo(self, data: dict):
7690

7791
def showNodeInfo(self, data: dict):
7892
"""Show human-readable description of our node"""
79-
if dx := data.get('Preferences', None) is not None:
93+
if (dx := data.get('Preferences', None)) is not None:
8094
print(f"Preferences: {json.dumps(dx, indent=2)}")
8195

82-
if dx := data.get('Module preferences', None) is not None:
96+
if (dx := data.get('Module preferences', None)) is not None:
8397
print(f"Module preferences: {json.dumps(dx, indent=2)}")
8498

85-
if dx := data.get('Channels', None) is not None:
99+
if (dx := data.get('Channels', None)) is not None:
86100
print("Channels:")
87101
for idx, c in enumerate(dx):
88102
if channel_pb2.Channel.Role.Name(c['role']) != "DISABLED":
89-
print(f" Index {idx}: {channel_pb2.Channel.Role.Name(c['role'])} psk={pskToString(c['psk'])} {json.dumps(c['settings'])}")
103+
print(f" Index {idx}: {channel_pb2.Channel.Role.Name(c['role'])} psk={pskToString(c['__psk__'])} {json.dumps(c['settings'])}")
90104
print("")
91-
publicURL = data['publicURL']
92-
print(f"\nPrimary channel URL: {publicURL}")
93-
adminURL = data['adminURL']
94-
if adminURL != publicURL:
105+
publicURL = data.get('publicURL', None)
106+
if publicURL:
107+
print(f"\nPrimary channel URL: {publicURL}")
108+
adminURL = data.get('adminURL', None)
109+
if adminURL and adminURL != publicURL:
95110
print(f"Complete URL (includes all channels): {adminURL}")

meshtastic/node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def getChannelInfo(self) -> dict:
8484
chanConfig = {}
8585
if self.channels:
8686
logger.debug(f"self.channels:{self.channels}")
87-
chanConfig = [{"role": c.role, "psk": c.settings.psk, "settings": MessageToDict(c.settings, always_print_fields_with_no_presence=True)} for c in self.channels]
87+
chanConfig = [{"role": c.role, "__psk__": c.settings.psk, "settings": MessageToDict(c.settings, always_print_fields_with_no_presence=True)} for c in self.channels]
8888
publicURL = self.getURL(includeAll=False)
8989
adminURL = self.getURL(includeAll=True)
9090
return {"Channels": chanConfig, "publicURL": publicURL, "adminURL": adminURL}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
{
2+
"Owner": [
3+
"Meshtastic Test",
4+
"MT"
5+
],
6+
"My Info": {
7+
"myNodeNum": 1234,
8+
"minAppVersion": 30200,
9+
"deviceId": "S==",
10+
"pioEnv": "rak4631"
11+
},
12+
"Metadata": {
13+
"firmwareVersion": "2.6.11.60ec05e",
14+
"deviceStateVersion": 24,
15+
"canShutdown": true,
16+
"hasBluetooth": true,
17+
"hasEthernet": true,
18+
"positionFlags": 811,
19+
"hwModel": "RAK4631",
20+
"hasPKC": true,
21+
"excludedModules": 4480
22+
},
23+
"Nodes": {
24+
"!11223344": {
25+
"num": 381,
26+
"user": {
27+
"id": "!11223344",
28+
"longName": "Meshtastic Test",
29+
"shortName": "MT",
30+
"macaddr": "11:22:33:44:55:66",
31+
"hwModel": "RAK4631",
32+
"publicKey": "he!",
33+
"isUnmessagable": true
34+
},
35+
"deviceMetrics": {
36+
"batteryLevel": 101,
37+
"voltage": 4.151,
38+
"channelUtilization": 0.0,
39+
"airUtilTx": 0.16225,
40+
"uptimeSeconds": 192342
41+
},
42+
"position": {
43+
"latitudeI": 470000000,
44+
"longitudeI": 70000000,
45+
"altitude": 2000,
46+
"time": 1764344445,
47+
"latitude": 47.0000000,
48+
"longitude": 7.0000000
49+
},
50+
"lastHeard": 1764344445,
51+
"isFavorite": true
52+
}
53+
},
54+
"Preferences": {
55+
"position": {
56+
"positionBroadcastSecs": 259200,
57+
"fixedPosition": true,
58+
"gpsUpdateInterval": 3600,
59+
"gpsAttemptTime": 900,
60+
"positionFlags": 811,
61+
"broadcastSmartMinimumDistance": 100,
62+
"broadcastSmartMinimumIntervalSecs": 30,
63+
"gpsMode": "ENABLED"
64+
},
65+
"lora": {
66+
"usePreset": true,
67+
"region": "EU_868",
68+
"hopLimit": 3,
69+
"txEnabled": true,
70+
"txPower": 27,
71+
"sx126xRxBoostedGain": true,
72+
"ignoreMqtt": true
73+
}
74+
},
75+
"Module preferences": {
76+
"mqtt": {
77+
"address": "mqtt.meshtastic.org",
78+
"username": "meshdev",
79+
"password": "large4cats",
80+
"encryptionEnabled": true,
81+
"root": "msh/EU_868",
82+
"mapReportSettings": {
83+
"publishIntervalSecs": 3600
84+
}
85+
},
86+
"cannedMessage": {},
87+
"audio": {},
88+
"remoteHardware": {},
89+
"neighborInfo": {},
90+
"ambientLighting": {
91+
"current": 10,
92+
"red": 219,
93+
"green": 138,
94+
"blue": 117
95+
},
96+
"detectionSensor": {
97+
},
98+
"paxcounter": {}
99+
},
100+
"Channels": [
101+
{
102+
"role": 1,
103+
"__psk__": "01",
104+
"settings": {
105+
"psk": "AQ==",
106+
"moduleSettings": {
107+
"positionPrecision": 13,
108+
"isMuted": false
109+
},
110+
"channelNum": 0,
111+
"name": "",
112+
"id": 0,
113+
"uplinkEnabled": false,
114+
"downlinkEnabled": false
115+
}
116+
},
117+
{
118+
"role": 0,
119+
"settings": {
120+
"channelNum": 0,
121+
"psk": "",
122+
"name": "",
123+
"id": 0,
124+
"uplinkEnabled": false,
125+
"downlinkEnabled": false
126+
}
127+
},
128+
{
129+
"role": 0,
130+
"settings": {
131+
"channelNum": 0,
132+
"psk": "",
133+
"name": "",
134+
"id": 0,
135+
"uplinkEnabled": false,
136+
"downlinkEnabled": false
137+
}
138+
},
139+
{
140+
"role": 0,
141+
"settings": {
142+
"channelNum": 0,
143+
"psk": "",
144+
"name": "",
145+
"id": 0,
146+
"uplinkEnabled": false,
147+
"downlinkEnabled": false
148+
}
149+
},
150+
{
151+
"role": 0,
152+
"settings": {
153+
"channelNum": 0,
154+
"psk": "",
155+
"name": "",
156+
"id": 0,
157+
"uplinkEnabled": false,
158+
"downlinkEnabled": false
159+
}
160+
},
161+
{
162+
"role": 0,
163+
"settings": {
164+
"channelNum": 0,
165+
"psk": "",
166+
"name": "",
167+
"id": 0,
168+
"uplinkEnabled": false,
169+
"downlinkEnabled": false
170+
}
171+
},
172+
{
173+
"role": 0,
174+
"settings": {
175+
"channelNum": 0,
176+
"psk": "",
177+
"name": "",
178+
"id": 0,
179+
"uplinkEnabled": false,
180+
"downlinkEnabled": false
181+
}
182+
},
183+
{
184+
"role": 0,
185+
"settings": {
186+
"channelNum": 0,
187+
"psk": "",
188+
"name": "",
189+
"id": 0,
190+
"uplinkEnabled": false,
191+
"downlinkEnabled": false
192+
}
193+
}
194+
],
195+
"publicURL": "https://meshtastic.org/e/#CgcSAQE6AggNEg8IATgDQANIAVAbaAHABgE",
196+
"adminURL": "https://meshtastic.org/e/#CgcSAQE6AggNEg8IATgDQANIAVAbaAHABgE"
197+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"Owner": [
3+
"Meshtastic Test",
4+
"MT"
5+
],
6+
"My Info": {},
7+
"Metadata": {},
8+
"Nodes": {},
9+
"Preferences": {},
10+
"Module preferences": {},
11+
"Channels": []
12+
}

0 commit comments

Comments
 (0)