Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

560 linhas
14 KiB

  1. package utils
  2. import (
  3. "testing"
  4. "github.com/AFASystems/presence/internal/pkg/model"
  5. )
  6. func TestParseADFast(t *testing.T) {
  7. tests := []struct {
  8. name string
  9. input []byte
  10. expected [][2]int
  11. }{
  12. {
  13. name: "Empty input",
  14. input: []byte{},
  15. expected: [][2]int{},
  16. },
  17. {
  18. name: "Single AD structure",
  19. input: []byte{0x02, 0x01, 0x06},
  20. expected: [][2]int{{0, 2}},
  21. },
  22. {
  23. name: "Multiple AD structures",
  24. input: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
  25. expected: [][2]int{{0, 2}, {3, 6}},
  26. },
  27. {
  28. name: "Complex AD structures",
  29. input: []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5},
  30. expected: [][2]int{{0, 2}, {2, 28}},
  31. },
  32. {
  33. name: "Zero length AD structure",
  34. input: []byte{0x00, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
  35. expected: [][2]int{{2, 5}},
  36. },
  37. {
  38. name: "AD structure exceeding bounds",
  39. input: []byte{0x05, 0x01, 0x06},
  40. expected: [][2]int{},
  41. },
  42. {
  43. name: "Incomplete AD structure",
  44. input: []byte{0x03, 0x01},
  45. expected: [][2]int{},
  46. },
  47. {
  48. name: "Valid then invalid structure",
  49. input: []byte{0x02, 0x01, 0x06, 0xFF, 0x01, 0x06},
  50. expected: [][2]int{{0, 2}},
  51. },
  52. }
  53. for _, tt := range tests {
  54. t.Run(tt.name, func(t *testing.T) {
  55. result := ParseADFast(tt.input)
  56. if len(result) != len(tt.expected) {
  57. t.Errorf("ParseADFast() length = %v, expected %v", len(result), len(tt.expected))
  58. return
  59. }
  60. for i, r := range result {
  61. if r[0] != tt.expected[i][0] || r[1] != tt.expected[i][1] {
  62. t.Errorf("ParseADFast()[%d] = %v, expected %v", i, r, tt.expected[i])
  63. }
  64. }
  65. })
  66. }
  67. }
  68. func TestRemoveFlagBytes(t *testing.T) {
  69. tests := []struct {
  70. name string
  71. input []byte
  72. expected []byte
  73. }{
  74. {
  75. name: "Empty input",
  76. input: []byte{},
  77. expected: []byte{},
  78. },
  79. {
  80. name: "Single byte input",
  81. input: []byte{0x01},
  82. expected: []byte{0x01},
  83. },
  84. {
  85. name: "No flag bytes",
  86. input: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01},
  87. expected: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01},
  88. },
  89. {
  90. name: "With flag bytes",
  91. input: []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02},
  92. expected: []byte{0x1A, 0xFF, 0x4C, 0x00, 0x02},
  93. },
  94. {
  95. name: "Flag type is 0x01",
  96. input: []byte{0x02, 0x01, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
  97. expected: []byte{0x05, 0x01, 0x02, 0x03, 0x04},
  98. },
  99. {
  100. name: "Flag type is not 0x01",
  101. input: []byte{0x02, 0x02, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
  102. expected: []byte{0x02, 0x02, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
  103. },
  104. {
  105. name: "Length exceeds bounds",
  106. input: []byte{0xFF, 0x01, 0x06},
  107. expected: []byte{0xFF, 0x01, 0x06},
  108. },
  109. }
  110. for _, tt := range tests {
  111. t.Run(tt.name, func(t *testing.T) {
  112. result := RemoveFlagBytes(tt.input)
  113. if len(result) != len(tt.expected) {
  114. t.Errorf("RemoveFlagBytes() length = %v, expected %v", len(result), len(tt.expected))
  115. return
  116. }
  117. for i, b := range result {
  118. if b != tt.expected[i] {
  119. t.Errorf("RemoveFlagBytes()[%d] = %v, expected %v", i, b, tt.expected[i])
  120. }
  121. }
  122. })
  123. }
  124. }
  125. func TestIsValidADStructure(t *testing.T) {
  126. tests := []struct {
  127. name string
  128. data []byte
  129. expected bool
  130. }{
  131. {
  132. name: "Empty data",
  133. data: []byte{},
  134. expected: false,
  135. },
  136. {
  137. name: "Single byte",
  138. data: []byte{0x01},
  139. expected: false,
  140. },
  141. {
  142. name: "Valid minimal structure",
  143. data: []byte{0x01, 0x01},
  144. expected: true,
  145. },
  146. {
  147. name: "Valid structure",
  148. data: []byte{0x02, 0x01, 0x06},
  149. expected: true,
  150. },
  151. {
  152. name: "Zero length",
  153. data: []byte{0x00, 0x01, 0x06},
  154. expected: false,
  155. },
  156. {
  157. name: "Length exceeds data",
  158. data: []byte{0x05, 0x01, 0x06},
  159. expected: false,
  160. },
  161. {
  162. name: "Length exactly matches",
  163. data: []byte{0x02, 0x01, 0x06},
  164. expected: true,
  165. },
  166. {
  167. name: "Large valid structure",
  168. data: []byte{0x1F, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x01, 0x02, 0x03, 0x04, 0x05},
  169. expected: true,
  170. },
  171. }
  172. for _, tt := range tests {
  173. t.Run(tt.name, func(t *testing.T) {
  174. result := isValidADStructure(tt.data)
  175. if result != tt.expected {
  176. t.Errorf("isValidADStructure() = %v, expected %v", result, tt.expected)
  177. }
  178. })
  179. }
  180. }
  181. func TestCheckIngics(t *testing.T) {
  182. tests := []struct {
  183. name string
  184. ad []byte
  185. expected bool
  186. }{
  187. {
  188. name: "Valid Ingics beacon",
  189. ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x12, 0x34, 0x01},
  190. expected: true,
  191. },
  192. {
  193. name: "Invalid - too short",
  194. ad: []byte{0x05, 0xFF, 0x59, 0x00},
  195. expected: false,
  196. },
  197. {
  198. name: "Invalid - wrong manufacturer ID",
  199. ad: []byte{0x08, 0xFF, 0x59, 0x01, 0x80, 0xBC, 0x12, 0x34, 0x01},
  200. expected: false,
  201. },
  202. {
  203. name: "Invalid - wrong type",
  204. ad: []byte{0x08, 0xFE, 0x59, 0x00, 0x80, 0xBC, 0x12, 0x34, 0x01},
  205. expected: false,
  206. },
  207. {
  208. name: "Valid with minimum length",
  209. ad: []byte{0x06, 0xFF, 0x59, 0x00, 0x80, 0xBC},
  210. expected: true,
  211. },
  212. {
  213. name: "Empty data",
  214. ad: []byte{},
  215. expected: false,
  216. },
  217. {
  218. name: "Partial match only",
  219. ad: []byte{0x06, 0xFF, 0x59, 0x00, 0x80},
  220. expected: false,
  221. },
  222. }
  223. for _, tt := range tests {
  224. t.Run(tt.name, func(t *testing.T) {
  225. result := checkIngics(tt.ad)
  226. if result != tt.expected {
  227. t.Errorf("checkIngics() = %v, expected %v", result, tt.expected)
  228. }
  229. })
  230. }
  231. }
  232. func TestParseIngicsState(t *testing.T) {
  233. tests := []struct {
  234. name string
  235. ad []byte
  236. expected model.BeaconEvent
  237. }{
  238. {
  239. name: "Valid Ingics data",
  240. ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05},
  241. expected: model.BeaconEvent{
  242. Battery: 0x1234, // 4660 in little endian
  243. Event: 0x05,
  244. Type: "Ingics",
  245. },
  246. },
  247. {
  248. name: "Zero battery",
  249. ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x00, 0x00, 0x00},
  250. expected: model.BeaconEvent{
  251. Battery: 0,
  252. Event: 0,
  253. Type: "Ingics",
  254. },
  255. },
  256. {
  257. name: "Max battery value",
  258. ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0xFF, 0xFF, 0xFF},
  259. expected: model.BeaconEvent{
  260. Battery: 0xFFFF,
  261. Event: 0xFF,
  262. Type: "Ingics",
  263. },
  264. },
  265. }
  266. for _, tt := range tests {
  267. t.Run(tt.name, func(t *testing.T) {
  268. result := parseIngicsState(tt.ad)
  269. if result.Battery != tt.expected.Battery {
  270. t.Errorf("parseIngicsState() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
  271. }
  272. if result.Event != tt.expected.Event {
  273. t.Errorf("parseIngicsState() Event = %v, expected %v", result.Event, tt.expected.Event)
  274. }
  275. if result.Type != tt.expected.Type {
  276. t.Errorf("parseIngicsState() Type = %v, expected %v", result.Type, tt.expected.Type)
  277. }
  278. })
  279. }
  280. }
  281. func TestCheckEddystoneTLM(t *testing.T) {
  282. tests := []struct {
  283. name string
  284. ad []byte
  285. expected bool
  286. }{
  287. {
  288. name: "Valid Eddystone TLM",
  289. ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
  290. expected: true,
  291. },
  292. {
  293. name: "Invalid - too short",
  294. ad: []byte{0x03, 0x16, 0xAA},
  295. expected: false,
  296. },
  297. {
  298. name: "Invalid - wrong type",
  299. ad: []byte{0x12, 0x15, 0xAA, 0xFE, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
  300. expected: false,
  301. },
  302. {
  303. name: "Invalid - wrong company ID",
  304. ad: []byte{0x12, 0x16, 0xAA, 0xFF, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
  305. expected: false,
  306. },
  307. {
  308. name: "Invalid - wrong TLM type",
  309. ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x21, 0x00, 0x01, 0x02, 0x03, 0x04},
  310. expected: false,
  311. },
  312. {
  313. name: "Valid with minimum length",
  314. ad: []byte{0x04, 0x16, 0xAA, 0xFE, 0x20},
  315. expected: true,
  316. },
  317. {
  318. name: "Empty data",
  319. ad: []byte{},
  320. expected: false,
  321. },
  322. }
  323. for _, tt := range tests {
  324. t.Run(tt.name, func(t *testing.T) {
  325. result := checkEddystoneTLM(tt.ad)
  326. if result != tt.expected {
  327. t.Errorf("checkEddystoneTLM() = %v, expected %v", result, tt.expected)
  328. }
  329. })
  330. }
  331. }
  332. func TestParseEddystoneState(t *testing.T) {
  333. tests := []struct {
  334. name string
  335. ad []byte
  336. expected model.BeaconEvent
  337. }{
  338. {
  339. name: "Valid Eddystone TLM data",
  340. ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x34, 0x12, 0x78, 0x56, 0x00},
  341. expected: model.BeaconEvent{
  342. Battery: 0x1234, // 4660 in big endian (note: different from Ingics)
  343. Type: "Eddystone",
  344. },
  345. },
  346. {
  347. name: "Zero battery",
  348. ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x00, 0x00, 0x78, 0x56, 0x00},
  349. expected: model.BeaconEvent{
  350. Battery: 0,
  351. Type: "Eddystone",
  352. },
  353. },
  354. {
  355. name: "Max battery value",
  356. ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0xFF, 0xFF, 0x78, 0x56, 0x00},
  357. expected: model.BeaconEvent{
  358. Battery: 0xFFFF,
  359. Type: "Eddystone",
  360. },
  361. },
  362. }
  363. for _, tt := range tests {
  364. t.Run(tt.name, func(t *testing.T) {
  365. result := parseEddystoneState(tt.ad)
  366. if result.Battery != tt.expected.Battery {
  367. t.Errorf("parseEddystoneState() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
  368. }
  369. if result.Type != tt.expected.Type {
  370. t.Errorf("parseEddystoneState() Type = %v, expected %v", result.Type, tt.expected.Type)
  371. }
  372. })
  373. }
  374. }
  375. func TestCheckMinewB7(t *testing.T) {
  376. tests := []struct {
  377. name string
  378. ad []byte
  379. expected bool
  380. }{
  381. {
  382. name: "Valid Minew B7",
  383. ad: []byte{0x08, 0x16, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04},
  384. expected: true,
  385. },
  386. {
  387. name: "Invalid - too short",
  388. ad: []byte{0x03, 0x16, 0xE1},
  389. expected: false,
  390. },
  391. {
  392. name: "Invalid - wrong type",
  393. ad: []byte{0x08, 0x15, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04},
  394. expected: false,
  395. },
  396. {
  397. name: "Invalid - wrong company ID",
  398. ad: []byte{0x08, 0x16, 0xE1, 0xFE, 0x01, 0x02, 0x03, 0x04},
  399. expected: false,
  400. },
  401. {
  402. name: "Valid with minimum length",
  403. ad: []byte{0x04, 0x16, 0xE1, 0xFF},
  404. expected: true,
  405. },
  406. {
  407. name: "Empty data",
  408. ad: []byte{},
  409. expected: false,
  410. },
  411. }
  412. for _, tt := range tests {
  413. t.Run(tt.name, func(t *testing.T) {
  414. result := checkMinewB7(tt.ad)
  415. if result != tt.expected {
  416. t.Errorf("checkMinewB7() = %v, expected %v", result, tt.expected)
  417. }
  418. })
  419. }
  420. }
  421. func TestLoopADStructures(t *testing.T) {
  422. tests := []struct {
  423. name string
  424. data []byte
  425. ranges [][2]int
  426. id string
  427. expected model.BeaconEvent
  428. }{
  429. {
  430. name: "Ingics beacon found",
  431. data: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05, 0x02, 0x01, 0x06},
  432. ranges: [][2]int{{0, 8}, {8, 11}},
  433. id: "test-beacon",
  434. expected: model.BeaconEvent{
  435. ID: "test-beacon",
  436. Name: "test-beacon",
  437. Battery: 0x1234,
  438. Event: 0x05,
  439. Type: "Ingics",
  440. },
  441. },
  442. {
  443. name: "Eddystone beacon found",
  444. data: []byte{0x02, 0x01, 0x06, 0x12, 0x16, 0xAA, 0xFE, 0x20, 0x34, 0x12, 0x78, 0x56},
  445. ranges: [][2]int{{0, 2}, {2, 14}},
  446. id: "eddystone-test",
  447. expected: model.BeaconEvent{
  448. ID: "eddystone-test",
  449. Name: "eddystone-test",
  450. Battery: 0x1234,
  451. Type: "Eddystone",
  452. },
  453. },
  454. {
  455. name: "Minew B7 beacon found",
  456. data: []byte{0x08, 0x16, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x06},
  457. ranges: [][2]int{{0, 8}, {8, 11}},
  458. id: "minew-test",
  459. expected: model.BeaconEvent{
  460. ID: "minew-test",
  461. Name: "minew-test",
  462. Type: "", // Minew B7 returns empty BeaconEvent
  463. },
  464. },
  465. {
  466. name: "No matching beacon type",
  467. data: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
  468. ranges: [][2]int{{0, 2}, {2, 5}},
  469. id: "unknown-test",
  470. expected: model.BeaconEvent{},
  471. },
  472. {
  473. name: "Invalid AD structure",
  474. data: []byte{0x02, 0x01, 0x06, 0xFF, 0x01, 0x06},
  475. ranges: [][2]int{{0, 2}, {2, 4}},
  476. id: "invalid-test",
  477. expected: model.BeaconEvent{},
  478. },
  479. {
  480. name: "Empty data",
  481. data: []byte{},
  482. ranges: [][2]int{},
  483. id: "empty-test",
  484. expected: model.BeaconEvent{
  485. ID: "empty-test",
  486. Name: "empty-test",
  487. },
  488. },
  489. }
  490. for _, tt := range tests {
  491. t.Run(tt.name, func(t *testing.T) {
  492. result := LoopADStructures(tt.data, tt.ranges, tt.id)
  493. if result.ID != tt.expected.ID {
  494. t.Errorf("LoopADStructures() ID = %v, expected %v", result.ID, tt.expected.ID)
  495. }
  496. if result.Name != tt.expected.Name {
  497. t.Errorf("LoopADStructures() Name = %v, expected %v", result.Name, tt.expected.Name)
  498. }
  499. if result.Type != tt.expected.Type {
  500. t.Errorf("LoopADStructures() Type = %v, expected %v", result.Type, tt.expected.Type)
  501. }
  502. if result.Battery != tt.expected.Battery {
  503. t.Errorf("LoopADStructures() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
  504. }
  505. })
  506. }
  507. }
  508. func TestLoopADStructuresPriority(t *testing.T) {
  509. // Test that Ingics is checked first
  510. data := []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05, 0x12, 0x16, 0xAA, 0xFE, 0x20, 0x78, 0x56}
  511. ranges := [][2]int{{0, 8}, {8, 15}}
  512. result := LoopADStructures(data, ranges, "priority-test")
  513. // Should detect Ingics first, not Eddystone
  514. if result.Type != "Ingics" {
  515. t.Errorf("LoopADStructures() Type = %v, expected Ingics (priority test)", result.Type)
  516. }
  517. }
  518. // Benchmark tests
  519. func BenchmarkParseADFast(b *testing.B) {
  520. data := []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5}
  521. for i := 0; i < b.N; i++ {
  522. ParseADFast(data)
  523. }
  524. }
  525. func BenchmarkRemoveFlagBytes(b *testing.B) {
  526. data := []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5}
  527. for i := 0; i < b.N; i++ {
  528. RemoveFlagBytes(data)
  529. }
  530. }