/* Copyright (C) 2022-2023 Martin.JM This file is part of Gadgetbridge. Gadgetbridge is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gadgetbridge is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei; import static org.mockito.Matchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Context; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.btbr.Transaction; import nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request; @RunWith(MockitoJUnitRunner.class) public class TestResponseManager { HuaweiSupportProvider supportProvider = new HuaweiSupportProvider(new HuaweiLESupport()) { @Override public boolean isBLE() { return true; } @Override public Context getContext() { return null; } @Override public GBDevice getDevice() { return null; } @Override public byte[] getSerial() { return new byte[0]; } @Override public String getDeviceMac() { return null; } @Override public byte[] getMacAddress() { return new byte[0]; } @Override public byte[] getAndroidId() { return new byte[0]; } @Override public short getNotificationId() { return 0; } @Override public TransactionBuilder createBrTransactionBuilder(String taskName) { return null; } @Override public nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder createLeTransactionBuilder(String taskName) { return null; } @Override public void performConnected(Transaction transaction) throws IOException { } @Override public void performConnected(nodomain.freeyourgadget.gadgetbridge.service.btle.Transaction transaction) throws IOException { } @Override public void evaluateGBDeviceEvent(GBDeviceEvent deviceEvent) { } @Override public BluetoothGattCharacteristic getLeCharacteristic(UUID uuid) { return null; } @Override public HuaweiPacket.ParamsProvider getParamsProvider() { return null; } @Override public void addInProgressRequest(Request request) { } @Override public void removeInProgressRequests(Request request) { } @Override public void setSecretKey(byte[] authKey) { } @Override public byte[] getSecretKey() { return new byte[0]; } @Override public void addTotalFitnessData(int steps, int calories, int distance) { } @Override public void addSleepActivity(int timestamp, short duration, byte type) { } @Override public void addStepData(int timestamp, short steps, short calories, short distance, byte spo, byte heartrate) { } @Override public Long addWorkoutTotalsData(Workout.WorkoutTotals.Response packet) { return null; } @Override public void addWorkoutSampleData(Long workoutId, List dataList) { } @Override public void addWorkoutPaceData(Long workoutId, List paceList) { } @Override public void sendSetMusic() { } }; Field handlersField; Field receivedPacketField; Field asynchronousResponseField; @Before public void beforeClass() throws NoSuchFieldException { handlersField = ResponseManager.class.getDeclaredField("handlers"); handlersField.setAccessible(true); asynchronousResponseField = ResponseManager.class.getDeclaredField("asynchronousResponse"); asynchronousResponseField.setAccessible(true); receivedPacketField = ResponseManager.class.getDeclaredField("receivedPacket"); receivedPacketField.setAccessible(true); } @Test public void testAddHandler() throws IllegalAccessException { Request input = new Request(supportProvider); List expectedHandlers = Collections.synchronizedList(new ArrayList()); expectedHandlers.add(input); ResponseManager responseManager = new ResponseManager(supportProvider); responseManager.addHandler(input); Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); } @Test public void testRemoveHandler() throws IllegalAccessException { Request input = new Request(supportProvider); Request extra = new Request(supportProvider); List inputHandlers = Collections.synchronizedList(new ArrayList()); inputHandlers.add(extra); inputHandlers.add(input); inputHandlers.add(extra); List expectedHandlers = Collections.synchronizedList(new ArrayList()); expectedHandlers.add(extra); expectedHandlers.add(extra); ResponseManager responseManager = new ResponseManager(supportProvider); handlersField.set(responseManager, inputHandlers); responseManager.removeHandler(input); Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); } @Test public void testHandleDataCompletePacketSynchronous() throws Exception { // Note that this is not a proper packet, but that doesn't matter as we're not testing // the packet parsing. byte[] input = {0x01, 0x02, 0x03, 0x04}; AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); mockHuaweiPacket.complete = true; when(mockHuaweiPacket.parse((byte[]) any())) .thenReturn(mockHuaweiPacket); Request request1 = Mockito.mock(Request.class); when(request1.handleResponse((HuaweiPacket) any())) .thenReturn(true); Request request2 = Mockito.mock(Request.class); // FIXME: Removed due to UnnecessaryStubbingException after mockito-core update //when(request2.handleResponse((HuaweiPacket) any())) // .thenReturn(false); List inputHandlers = Collections.synchronizedList(new ArrayList()); inputHandlers.add(request1); inputHandlers.add(request2); List expectedHandlers = Collections.synchronizedList(new ArrayList()); expectedHandlers.add(request2); ResponseManager responseManager = new ResponseManager(supportProvider); handlersField.set(responseManager, inputHandlers); receivedPacketField.set(responseManager, mockHuaweiPacket); asynchronousResponseField.set(responseManager, mockAsynchronousResponse); responseManager.handleData(input); Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); Assert.assertNull(receivedPacketField.get(responseManager)); verify(mockHuaweiPacket, times(1)).parse(input); verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); verify(request1, times(1)).handleResponse(mockHuaweiPacket); verify(request1, times(1)).handleResponse(); verify(request2, times(0)).handleResponse((HuaweiPacket) any()); verify(request2, times(0)).handleResponse(); } @Test public void testHandleDataCompletePacketAsynchronous() throws Exception { // Note that this is not a proper packet, but that doesn't matter as we're not testing // the packet parsing. byte[] input = {0x01, 0x02, 0x03, 0x04}; AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); mockHuaweiPacket.complete = true; when(mockHuaweiPacket.parse((byte[]) any())) .thenReturn(mockHuaweiPacket); Request request1 = Mockito.mock(Request.class); when(request1.handleResponse((HuaweiPacket) any())) .thenReturn(false); Request request2 = Mockito.mock(Request.class); when(request2.handleResponse((HuaweiPacket) any())) .thenReturn(false); List inputHandlers = Collections.synchronizedList(new ArrayList()); inputHandlers.add(request1); inputHandlers.add(request2); List expectedHandlers = Collections.synchronizedList(new ArrayList()); expectedHandlers.add(request1); expectedHandlers.add(request2); ResponseManager responseManager = new ResponseManager(supportProvider); handlersField.set(responseManager, inputHandlers); receivedPacketField.set(responseManager, mockHuaweiPacket); asynchronousResponseField.set(responseManager, mockAsynchronousResponse); responseManager.handleData(input); Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); Assert.assertNull(receivedPacketField.get(responseManager)); verify(mockHuaweiPacket, times(1)).parse(input); verify(mockAsynchronousResponse, times(1)).handleResponse(mockHuaweiPacket); verify(request1, times(1)).handleResponse(mockHuaweiPacket); verify(request1, times(0)).handleResponse(); verify(request2, times(1)).handleResponse(mockHuaweiPacket); verify(request2, times(0)).handleResponse(); } @Test public void testHandleDataTwoPartialPacketsSynchronous() throws Exception { // Note that this is not a proper packet, but that doesn't matter as we're not testing // the packet parsing. byte[] input1 = {0x01, 0x02, 0x03, 0x04}; byte[] input2 = {0x05, 0x06, 0x07, 0x08}; AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); mockHuaweiPacket.complete = false; when(mockHuaweiPacket.parse((byte[]) any())) .thenReturn(mockHuaweiPacket); Request request1 = Mockito.mock(Request.class); when(request1.handleResponse((HuaweiPacket) any())) .thenReturn(true); Request request2 = Mockito.mock(Request.class); // FIXME: Removed due to UnnecessaryStubbingException after mockito-core update //when(request2.handleResponse((HuaweiPacket) any())) // .thenReturn(false); List inputHandlers = Collections.synchronizedList(new ArrayList()); inputHandlers.add(request1); inputHandlers.add(request2); List expectedHandlers1 = Collections.synchronizedList(new ArrayList()); expectedHandlers1.add(request1); expectedHandlers1.add(request2); List expectedHandlers2 = Collections.synchronizedList(new ArrayList()); expectedHandlers2.add(request2); ResponseManager responseManager = new ResponseManager(supportProvider); handlersField.set(responseManager, inputHandlers); receivedPacketField.set(responseManager, mockHuaweiPacket); asynchronousResponseField.set(responseManager, mockAsynchronousResponse); responseManager.handleData(input1); Assert.assertEquals(expectedHandlers1, handlersField.get(responseManager)); Assert.assertEquals(mockHuaweiPacket, receivedPacketField.get(responseManager)); verify(mockHuaweiPacket, times(1)).parse(input1); verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); verify(request1, times(0)).handleResponse(mockHuaweiPacket); verify(request1, times(0)).handleResponse(); verify(request2, times(0)).handleResponse((HuaweiPacket) any()); verify(request2, times(0)).handleResponse(); mockHuaweiPacket.complete = true; responseManager.handleData(input2); Assert.assertEquals(expectedHandlers2, handlersField.get(responseManager)); Assert.assertNull(receivedPacketField.get(responseManager)); verify(mockHuaweiPacket, times(1)).parse(input2); verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); verify(request1, times(1)).handleResponse(mockHuaweiPacket); verify(request1, times(1)).handleResponse(); verify(request2, times(0)).handleResponse((HuaweiPacket) any()); verify(request2, times(0)).handleResponse(); } @Test public void testHandleDataTwoPartialPacketsAsynchronous() throws Exception { // Note that this is not a proper packet, but that doesn't matter as we're not testing // the packet parsing. byte[] input1 = {0x01, 0x02, 0x03, 0x04}; byte[] input2 = {0x05, 0x06, 0x07, 0x08}; AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); mockHuaweiPacket.complete = false; when(mockHuaweiPacket.parse((byte[]) any())) .thenReturn(mockHuaweiPacket); Request request1 = Mockito.mock(Request.class); when(request1.handleResponse((HuaweiPacket) any())) .thenReturn(false); Request request2 = Mockito.mock(Request.class); when(request2.handleResponse((HuaweiPacket) any())) .thenReturn(false); List inputHandlers = Collections.synchronizedList(new ArrayList()); inputHandlers.add(request1); inputHandlers.add(request2); List expectedHandlers = Collections.synchronizedList(new ArrayList()); expectedHandlers.add(request1); expectedHandlers.add(request2); ResponseManager responseManager = new ResponseManager(supportProvider); handlersField.set(responseManager, inputHandlers); receivedPacketField.set(responseManager, mockHuaweiPacket); asynchronousResponseField.set(responseManager, mockAsynchronousResponse); responseManager.handleData(input1); Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); Assert.assertEquals(mockHuaweiPacket, receivedPacketField.get(responseManager)); verify(mockHuaweiPacket, times(1)).parse(input1); verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); verify(request1, times(0)).handleResponse(mockHuaweiPacket); verify(request1, times(0)).handleResponse(); verify(request2, times(0)).handleResponse((HuaweiPacket) any()); verify(request2, times(0)).handleResponse(); mockHuaweiPacket.complete = true; responseManager.handleData(input2); Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); Assert.assertNull(receivedPacketField.get(responseManager)); verify(mockHuaweiPacket, times(1)).parse(input2); verify(mockAsynchronousResponse, times(1)).handleResponse((HuaweiPacket) any()); verify(request1, times(1)).handleResponse(mockHuaweiPacket); verify(request1, times(0)).handleResponse(); verify(request2, times(1)).handleResponse((HuaweiPacket) any()); verify(request2, times(0)).handleResponse(); } }