From 7e54554fee4fede39fc1ce831a4d5b4e0d66015f Mon Sep 17 00:00:00 2001 From: rubn Date: Thu, 31 Oct 2024 12:54:25 +0100 Subject: [PATCH] :construction: #70 --- ...rvice.java => EsptoolFallBackService.java} | 10 ++ ...t.java => EsptoolFallBackServiceTest.java} | 4 +- .../espflow/service/EsptoolServiceTest.java | 169 ++++++++++++++++-- ...toolServiceReadFlashArgumentsProvider.java | 39 ++++ 4 files changed, 202 insertions(+), 20 deletions(-) rename src/main/java/com/esp/espflow/service/{EspDeviceInfoFallBackService.java => EsptoolFallBackService.java} (71%) rename src/test/java/com/esp/espflow/service/{EspDeviceInfoFallBackServiceTest.java => EsptoolFallBackServiceTest.java} (87%) create mode 100644 src/test/java/com/esp/espflow/service/provider/EsptoolServiceReadFlashArgumentsProvider.java diff --git a/src/main/java/com/esp/espflow/service/EspDeviceInfoFallBackService.java b/src/main/java/com/esp/espflow/service/EsptoolFallBackService.java similarity index 71% rename from src/main/java/com/esp/espflow/service/EspDeviceInfoFallBackService.java rename to src/main/java/com/esp/espflow/service/EsptoolFallBackService.java index 19cdc40..f1b774a 100644 --- a/src/main/java/com/esp/espflow/service/EspDeviceInfoFallBackService.java +++ b/src/main/java/com/esp/espflow/service/EsptoolFallBackService.java @@ -1,6 +1,7 @@ package com.esp.espflow.service; import com.esp.espflow.entity.EspDeviceInfo; +import com.esp.espflow.exceptions.CanNotBeReadDeviceException; import com.esp.espflow.mappers.EspDeviceInfoMapper; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; @@ -28,4 +29,13 @@ public Mono fallback(String parsedPort) { .port(parsedPort) .build()); } + + /** + * This allows us to raise the exception type {@link CanNotBeReadDeviceException}, when the port list is empty. + * + * @return A {@link Mono} + */ + public Mono portListingIsEmpty() { + return Mono.error(new CanNotBeReadDeviceException("Possibly empty ports")); + } } diff --git a/src/test/java/com/esp/espflow/service/EspDeviceInfoFallBackServiceTest.java b/src/test/java/com/esp/espflow/service/EsptoolFallBackServiceTest.java similarity index 87% rename from src/test/java/com/esp/espflow/service/EspDeviceInfoFallBackServiceTest.java rename to src/test/java/com/esp/espflow/service/EsptoolFallBackServiceTest.java index 31fc2b3..fc9c3df 100644 --- a/src/test/java/com/esp/espflow/service/EspDeviceInfoFallBackServiceTest.java +++ b/src/test/java/com/esp/espflow/service/EsptoolFallBackServiceTest.java @@ -11,10 +11,10 @@ * @author rubn */ @ExtendWith(MockitoExtension.class) -class EspDeviceInfoFallBackServiceTest { +class EsptoolFallBackServiceTest { @InjectMocks - private EspDeviceInfoFallBackService service; + private EsptoolFallBackService service; @Test void fallback() { diff --git a/src/test/java/com/esp/espflow/service/EsptoolServiceTest.java b/src/test/java/com/esp/espflow/service/EsptoolServiceTest.java index 048d589..f1c4ef0 100644 --- a/src/test/java/com/esp/espflow/service/EsptoolServiceTest.java +++ b/src/test/java/com/esp/espflow/service/EsptoolServiceTest.java @@ -3,16 +3,20 @@ import com.esp.espflow.entity.EspDeviceInfo; import com.esp.espflow.enums.BaudRates; import com.esp.espflow.exceptions.CanNotBeReadDeviceException; +import com.esp.espflow.service.provider.ComPortServiceArgumentsProvider; import com.esp.espflow.service.provider.EsptoolServiceArgumentsProvider; import com.esp.espflow.service.provider.EsptoolServiceNoFlashSizeArgumentsProvider; import com.esp.espflow.service.provider.EsptoolServiceRawFlashIdFromPortArgumentsProvider; +import com.esp.espflow.service.provider.EsptoolServiceReadFlashArgumentsProvider; import com.esp.espflow.util.GetOsName; +import com.fazecast.jSerialComm.SerialPort; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.ArrayUtils; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; @@ -20,20 +24,36 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; import java.util.Set; +import java.util.function.Predicate; import static com.esp.espflow.util.EspFlowConstants.BAUD_RATE; +import static com.esp.espflow.util.EspFlowConstants.CHIP_IS; +import static com.esp.espflow.util.EspFlowConstants.CHIP_TYPE; +import static com.esp.espflow.util.EspFlowConstants.CRYSTAL_IS; +import static com.esp.espflow.util.EspFlowConstants.DETECTED_FLASH_SIZE; import static com.esp.espflow.util.EspFlowConstants.ESPTOOL_PY; import static com.esp.espflow.util.EspFlowConstants.ESPTOOL_PY_NOT_FOUND; import static com.esp.espflow.util.EspFlowConstants.ESPTOOL_PY_VERSION; import static com.esp.espflow.util.EspFlowConstants.FLASH_ID; +import static com.esp.espflow.util.EspFlowConstants.MAC; import static com.esp.espflow.util.EspFlowConstants.PORT; +import static com.esp.espflow.util.EspFlowConstants.SERIAL_PORT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -41,6 +61,7 @@ * @author rubn */ @Log4j2 +@TestMethodOrder(MethodOrderer.class) @ExtendWith(MockitoExtension.class) class EsptoolServiceTest { @@ -54,33 +75,103 @@ class EsptoolServiceTest { private ComPortService comPortService; @Mock - private EspDeviceInfoFallBackService espDeviceInfoFallbackService; + private EsptoolFallBackService esptoolFallbackService; @Mock private EsptoolPathService esptoolPathService; - @Test @SetSystemProperty(key = "os.name", value = "linux") - void readAllDevices() { - - when(comPortService.getPortsListWithFriendlyName()) - .thenReturn(Set.of("/dev/ttyUSB1@Serial-1", "/dev/ttyUSB2@Serial-2")); + @ParameterizedTest + @ArgumentsSource(ComPortServiceArgumentsProvider.class) + @DisplayName("we read all the devices, the reading is completely correct") + void readAllDevices(SerialPort[] actualSerialPorts, String expectedDevUsb1, + String expectedDevUsb2) { + + final Predicate predicate = line -> line.startsWith(SERIAL_PORT) + || line.startsWith(MAC) + || line.startsWith(DETECTED_FLASH_SIZE) + || line.startsWith(CRYSTAL_IS) + || line.startsWith(CHIP_TYPE) + || line.startsWith(CHIP_IS); + + ReflectionTestUtils.setField(esptoolService, "predicate", predicate); + + when(comPortService.getPortsListWithFriendlyName()).thenReturn(Set.of(expectedDevUsb1)); + + String[] commands = new String[]{ + "/tmp/esptool-bundle-dir/esptool-linux-amd64/esptool", + "--port", + "/dev/ttyUSB1", + "--baud", + "115200", + "flash_id" + }; + + Flux actualLines = Flux.just( + "Serial port /dev/ttyUSB1", + "Detecting chip type... ESP32-S3", + "Chip is ESP32-S3 (QFN56) (revision v0.0)", + "Detected flash size: 8MB"); + + when(commandService.processInputStreamLineByLine(commands)).thenReturn(actualLines); + + when(esptoolPathService.esptoolPath()) + .thenReturn("/tmp/esptool-bundle-dir/esptool-linux-amd64/esptool"); EspDeviceInfo expectedEspDeviceInfo = EspDeviceInfo.builder() .port("/dev/ttyUSB1") - .build(); - - EspDeviceInfo expectedEspDeviceInfo2 = EspDeviceInfo.builder() - .port("/dev/ttyUSB2") + .descriptivePortName("Serial-1") + .chipType("ESP32-S3") + .chipIs("ESP32-S3 (QFN56) (revision v0.0)") + //.crystalIs("24MHz") + //.macAddress("2c:f4:32:10:1d:bf") + .decimal("8388608") + .hex("800000") + .detectedFlashSize("8MB") .build(); StepVerifier.create(esptoolService.readAllDevices()) .expectNext(expectedEspDeviceInfo) - .expectNext(expectedEspDeviceInfo2) .verifyComplete(); + verify(esptoolFallbackService, times(0)).fallback("/dev/ttyUSB1"); + verify(esptoolFallbackService, times(0)).portListingIsEmpty(); + verifyNoInteractions(esptoolFallbackService); + + } + + @Test + @SetSystemProperty(key = "os.name", value = "linux") + @DisplayName("Si al leer los puertos tenemos un Set vacio, procesamos un CanNotBeReadDeviceException con mensaje de error" + + " Possibly empty ports 🤔") + void readAllDevicesEmptyPorts() { + + when(comPortService.getPortsListWithFriendlyName()).thenReturn(Set.of()); + + assertThat(comPortService.getPortsListWithFriendlyName()).isEmpty(); + + when(esptoolFallbackService.portListingIsEmpty()) + .thenReturn(Mono.error(new CanNotBeReadDeviceException("Possibly empty ports"))); + + StepVerifier.create(esptoolService.readAllDevices()) + .expectErrorMatches(error -> error.getMessage().contains("Possibly empty ports")) + .verify(); + + verify(esptoolFallbackService, times(1)).portListingIsEmpty(); + verifyNoMoreInteractions(esptoolFallbackService); + + } + + @Test + void aTst() { + + var empty = Flux.fromIterable(Set.of()); + + StepVerifier.create(empty) + .verifyComplete(); } + @ParameterizedTest @ArgumentsSource(EsptoolServiceArgumentsProvider.class) @SneakyThrows @@ -110,8 +201,8 @@ void readFlashIdWithCustomPort(String portForInputStream, @DisplayName("esptool.py --port /dev/ttyACM0 --baud 115200 flash_id, " + "indicates that the response of the console is incomplete and the microcontroller reading was not correct") void flashSizeIsNullFromInputWhenMapping(String portForInputStream, - String portWithFriendlyName, - Flux actualLines, EspDeviceInfo expectedLines) { + String portWithFriendlyName, + Flux actualLines, EspDeviceInfo expectedLines) { //The method processInputStreamLineByLine should receive the parsed port without the vendor String[] commands = {"esptool.py", "--port", portForInputStream, "--baud", "115200", "flash_id"}; @@ -119,7 +210,7 @@ void flashSizeIsNullFromInputWhenMapping(String portForInputStream, when(commandService.processInputStreamLineByLine(commands)) .thenReturn(actualLines); - when(espDeviceInfoFallbackService.fallback(portForInputStream)).thenReturn(Mono.just(expectedLines)); + when(esptoolFallbackService.fallback(portForInputStream)).thenReturn(Mono.just(expectedLines)); when(esptoolPathService.esptoolPath()).thenReturn("esptool.py"); @@ -127,8 +218,8 @@ void flashSizeIsNullFromInputWhenMapping(String portForInputStream, .expectNext(expectedLines) .verifyComplete(); - verify(espDeviceInfoFallbackService, times(1)).fallback(portForInputStream); - verifyNoMoreInteractions(espDeviceInfoFallbackService); + verify(esptoolFallbackService, times(1)).fallback(portForInputStream); + verifyNoMoreInteractions(esptoolFallbackService); } @@ -191,7 +282,6 @@ void readRawFlashIdFromPort(Flux actualLines, String expetedFirtsLine, } @Test - @SetSystemProperty(key = "os.name", value = "linux") @DisplayName("Simple test, counter all devices") void countAllDevices() { @@ -203,5 +293,48 @@ void countAllDevices() { } + @ParameterizedTest + @ArgumentsSource(EsptoolServiceReadFlashArgumentsProvider.class) + @SetSystemProperty(key = "java.osname", value = "linux") + @DisplayName("The entire flash of the device is read using the ALL parameter, the console will display the percentage in real time, en linux") + void downloadFlash(Flux actualLines, String expectedLinesFromConsole) { + + String[] commands = {"esptool.py", "--port", "/dev/ttyUSB1", "--baud", "115200", "read_flash", + "0", "ALL", "esp8266-backupflash.bin"}; + + when(commandService.processCommandsWithCustomCharset(commands)).thenReturn(actualLines); + + StepVerifier.create(esptoolService.downloadFlash(commands)) + .expectNext(expectedLinesFromConsole) + .verifyComplete(); + + } + + @Test + @SetSystemProperty(key = "java.io.tmpdir", value = "/tmp") + @SetSystemProperty(key = "os.name", value = "linux") + @DisplayName("Creacion de fichero de backup en directorio temporal") + void createEspBackUpFlashDirIfNotExists() throws IOException { + + Files.deleteIfExists(Path.of("/tmp/esp-backup-flash-dir")); + + assertThatCode(() -> + this.esptoolService.createEspBackUpFlashDirIfNotExists()).doesNotThrowAnyException(); + + } + + @Test + @SetSystemProperty(key = "os.name", value = "linux") + @SetSystemProperty(key = "java.io.tmpdir", value = "not-dir") + @DisplayName("Error creating directory /esp-backup-flash-dir in a directory that does not exist") + void createEspBackUpFlashDirIfNotExistsFailure() throws IOException { + + Files.deleteIfExists(Path.of("not-dir/esp-backup-flash-dir")); + + assertThatExceptionOfType(IOException.class) + .isThrownBy(() -> this.esptoolService.createEspBackUpFlashDirIfNotExists()) + .withMessage("Error creating directory /esp-backup-flash-dir on not-dir/esp-backup-flash-dir"); + + } } diff --git a/src/test/java/com/esp/espflow/service/provider/EsptoolServiceReadFlashArgumentsProvider.java b/src/test/java/com/esp/espflow/service/provider/EsptoolServiceReadFlashArgumentsProvider.java new file mode 100644 index 0000000..9ff6f9a --- /dev/null +++ b/src/test/java/com/esp/espflow/service/provider/EsptoolServiceReadFlashArgumentsProvider.java @@ -0,0 +1,39 @@ +package com.esp.espflow.service.provider; + +import com.esp.espflow.entity.EspDeviceInfo; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import reactor.core.publisher.Flux; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Stream; + +public class EsptoolServiceReadFlashArgumentsProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + + + String actualReadedLines = "Detecting chip type...\n" + + "ESP8266\n" + + "Chip is ESP8266EX\n" + + "Features: WiFi\n" + + "Crystal is 26MHz\n" + + "MAC: 2c:f4:32:10:1d:bf\n" + + "Uploading stub...\n" + + "Running stub...\n" + + "Stub running...\n" + + "2457 (100 %)\n" + + "2457 (100 %)\n" + + "Read 2457 bytes at 0x00000000 in 2.7 seconds (7.3 kbit/s)...\n" + + "Hard resetting via RTS pin..."; + + Flux actualLines = Flux.just(actualReadedLines); + + return Stream.of( + Arguments.of(actualLines, actualReadedLines) + ); + } +}