33// BluetoothExplorerPluginEngineTests
44//
55// Exercises the WASM runner against hand-written WAT fixtures compiled at test time, plus the
6- // bundled battery-level.wasm , and checks parity against the native parsers.
6+ // bundled GATT plugins , and checks parity against the native parsers.
77//
88
99import Foundation
@@ -144,7 +144,9 @@ struct WasmRunnerTests {
144144 let wasm = try [ UInt8] ( wat2wasm ( batteryWAT) )
145145 let wasmPlugin = try WasmParserPlugin ( manifest: batteryManifest ( ) , moduleBytes: wasm)
146146 let native = NativeWellKnownCharacteristicParser ( )
147- for level : UInt8 in [ 0 , 1 , 42 , 99 , 100 , 255 ] {
147+ // 0...100 only: the GATT plugin enforces the spec's valid range, unlike the legacy
148+ // native parser, so 255 is deliberately excluded here and asserted separately below.
149+ for level : UInt8 in [ 0 , 1 , 42 , 99 , 100 ] {
148150 let request = ParseRequest ( kind: . characteristic, uuid: . bit16( 0x2A19 ) , payload: Data ( [ level] ) )
149151 let wasmResult = try #require( await wasmPlugin. parse ( request) )
150152 let nativeResult = try #require( await native. parse ( request) )
@@ -162,24 +164,26 @@ struct PluginLoaderTests {
162164 let result = PluginLoader . loadBundled ( from: PluginEngineResources . bundle)
163165 #expect( result. failures. isEmpty, " load failures: \( result. failures. map ( \. message) ) " )
164166 let battery = try #require( result. loaded. first {
165- $0. manifest. identifier == " org.pureswift.plugin.battery-level "
167+ $0. manifest. identifier == " org.pureswift.plugin.gatt-battery "
166168 } )
167169 // The bundled module (validated against its manifest sha256) decodes a real value.
168170 let request = ParseRequest ( kind: . characteristic, uuid: . bit16( 0x2A19 ) , payload: Data ( [ 88 ] ) )
169171 let decoded = try #require( await battery. plugin. parse ( request) )
170- #expect( decoded. title == " Battery " )
172+ #expect( decoded. title == " Battery Level " )
171173 #expect( decoded. fields. first? . value == . uint( 88 ) )
172174 }
173175
174176 @Test ( " Bundled Embedded Swift plugin matches the native parser across values " )
175177 func bundledSwiftPluginParity( ) async throws {
176178 let result = PluginLoader . loadBundled ( from: PluginEngineResources . bundle)
177179 let battery = try #require( result. loaded. first {
178- $0. manifest. identifier == " org.pureswift.plugin.battery-level "
180+ $0. manifest. identifier == " org.pureswift.plugin.gatt-battery "
179181 } )
180182 let native = NativeWellKnownCharacteristicParser ( )
181183 var failures = [ UInt8] ( )
182- for level : UInt8 in [ 0 , 1 , 42 , 99 , 100 , 255 ] {
184+ // 0...100 only: the GATT plugin enforces the spec's valid range, unlike the legacy
185+ // native parser, so 255 is deliberately excluded here and asserted separately below.
186+ for level : UInt8 in [ 0 , 1 , 42 , 99 , 100 ] {
183187 let request = ParseRequest ( kind: . characteristic, uuid: . bit16( 0x2A19 ) , payload: Data ( [ level] ) )
184188 guard let wasmResult = await battery. plugin. parse ( request) else {
185189 failures. append ( level)
@@ -191,13 +195,19 @@ struct PluginLoaderTests {
191195 #expect( wasmResult. fields. first? . unit == " % " , " level \( level) " )
192196 }
193197 #expect( failures. isEmpty, " levels returning nil: \( failures) " )
198+
199+ // Out-of-range battery level: the plugin rejects it (GATTBatteryPercentage is 0...100)
200+ // where the legacy native parser happily returns 255.
201+ let invalid = ParseRequest ( kind: . characteristic, uuid: . bit16( 0x2A19 ) , payload: Data ( [ 255 ] ) )
202+ #expect( await battery. plugin. parse ( invalid) == nil )
203+ #expect( await native. parse ( invalid) != nil )
194204 }
195205
196206 @Test ( " Bundled plugin declines a payload it does not recognize " )
197207 func bundledSwiftPluginDeclines( ) async throws {
198208 let result = PluginLoader . loadBundled ( from: PluginEngineResources . bundle)
199209 let battery = try #require( result. loaded. first {
200- $0. manifest. identifier == " org.pureswift.plugin.battery-level "
210+ $0. manifest. identifier == " org.pureswift.plugin.gatt-battery "
201211 } )
202212 // Empty payload: the guest returns 0 ("not mine"), which must surface as nil, not an error.
203213 let empty = ParseRequest ( kind: . characteristic, uuid: . bit16( 0x2A19 ) , payload: Data ( ) )
@@ -212,19 +222,19 @@ struct PluginLoaderTests {
212222 let manifestURLs = PluginEngineResources . bundle. urls (
213223 forResourcesWithExtension: " json " , subdirectory: " Plugins " ) ?? [ ]
214224 let batteryManifestURL = try #require( manifestURLs. first {
215- $0. lastPathComponent == " battery-level .bleplugin.json"
225+ $0. lastPathComponent == " gatt-battery .bleplugin.json"
216226 } )
217227 // Write a manifest copy with a wrong hash next to the real module and load it.
218228 let tempDir = URL ( fileURLWithPath: NSTemporaryDirectory ( ) )
219229 . appendingPathComponent ( " bleplug- \( UUID ( ) . uuidString) " )
220230 try FileManager . default. createDirectory ( at: tempDir, withIntermediateDirectories: true )
221231 defer { try ? FileManager . default. removeItem ( at: tempDir) }
222232
223- let moduleURL = batteryManifestURL. deletingLastPathComponent ( ) . appendingPathComponent ( " battery-level .wasm" )
224- try Data ( contentsOf: moduleURL) . write ( to: tempDir. appendingPathComponent ( " battery-level .wasm" ) )
233+ let moduleURL = batteryManifestURL. deletingLastPathComponent ( ) . appendingPathComponent ( " gatt-battery .wasm" )
234+ try Data ( contentsOf: moduleURL) . write ( to: tempDir. appendingPathComponent ( " gatt-battery .wasm" ) )
225235 let badManifest = """
226236 { " manifestVersion " :1, " id " : " x " , " name " : " x " , " version " : " 1.0.0 " , " abi " :1,
227- " module " : " battery-level .wasm" , " sha256 " : " deadbeef " , " matches " :{ " characteristicUUIDs " :[ " 2A19 " ]}}
237+ " module " : " gatt-battery .wasm" , " sha256 " : " deadbeef " , " matches " :{ " characteristicUUIDs " :[ " 2A19 " ]}}
228238 """
229239 let badManifestURL = tempDir. appendingPathComponent ( " x.bleplugin.json " )
230240 try Data ( badManifest. utf8) . write ( to: badManifestURL)
0 commit comments