diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b24f244..491f54ad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,12 +5,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source code - uses: actions/checkout@v1 - with: - ref: master + uses: actions/checkout@v2 - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 - name: Build with Gradle run: ./gradlew test checkstyleMain + - name: Checkout source code + uses: codecov/codecov-action@v1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1d00785f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: required -language: java -jdk: - - openjdk11 -before_install: - - chmod +x gradlew -services: - - docker -cache: - directories: - - .autoconf - - $HOME/.m2 - - docker -notifications: - email: - on_success: always - on_failure: always - recipients: - - bren@juanantonio.info -script: - - ./gradlew test checkstyleMain -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index ddc1b45b..4706e257 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ & the [LeJOS](http://www.lejos.org/) way. [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](/LICENSE) -[![Travis CI](https://travis-ci.org/ev3dev-lang-java/ev3dev-lang-java.svg?branch=develop)](https://travis-ci.org/ev3dev-lang-java/ev3dev-lang-java) - +![Java CI](https://github.com/ev3dev-lang-java/ev3dev-lang-java/workflows/Java%20CI/badge.svg) ![ScreenShot](https://raw.githubusercontent.com/jabrena/ev3dev-lang-java/master/docs/images/theThreeAmigos.jpg) # How to test? diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index f9638ed9..fc81d1fd 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -48,6 +48,7 @@ |.*/Sound.java| |.*/EV3Led.java| |.*/Battery.java| + |.*/BatteryOld.java| |.*/NativeFramebuffer.java" /> Only EV3Bricks are supported. */ -public class EV3Led extends EV3DevDevice implements LED { +@Slf4j +public class EV3Led extends EV3DevDevice implements LED, Closeable { /** * Directions of the LED. @@ -22,15 +26,23 @@ public enum Direction { RIGHT } - private static final Logger log = LoggerFactory.getLogger(EV3Led.class); + private static final Direction[] directionArray = {Direction.LEFT,Direction.RIGHT}; + /** + * @deprecated Use EV3LedDirection.LEFT instead. + */ + @Deprecated public static final int LEFT = 0; + /** + * @deprecated Use EV3Led.Direction.RIGHT instead. + */ + @Deprecated public static final int RIGHT = 1; private final Direction direction; - private final String LED_RED; - private final String LED_GREEN; + private final DataChannelRewriter redWriter; + private final DataChannelRewriter greenWriter; /** * Create an EV3LED object associated with the LED of the specified direction. @@ -43,18 +55,19 @@ public EV3Led(final Direction direction) { //TODO Refactor if (direction == null) { - log.error("You are not specifying any button."); + LOGGER.error("You are not specifying any button."); throw new IllegalArgumentException("You are not specifying any button."); } this.direction = direction; + String ledPath = EV3DevFileSystem.getRootPath(); if (direction == Direction.LEFT) { - LED_RED = ev3DevProperties.getProperty("ev3.led.left.red"); - LED_GREEN = ev3DevProperties.getProperty("ev3.led.left.green"); + redWriter = new DataChannelRewriter(ledPath + ev3DevProperties.getProperty("ev3.led.left.red")); + greenWriter = new DataChannelRewriter(ledPath + ev3DevProperties.getProperty("ev3.led.left.green")); } else { - LED_RED = ev3DevProperties.getProperty("ev3.led.right.red"); - LED_GREEN = ev3DevProperties.getProperty("ev3.led.right.green"); + redWriter = new DataChannelRewriter(ledPath + ev3DevProperties.getProperty("ev3.led.right.red")); + greenWriter = new DataChannelRewriter(ledPath + ev3DevProperties.getProperty("ev3.led.right.green")); } } @@ -65,22 +78,9 @@ public EV3Led(final Direction direction) { * @throws RuntimeException if LED feature is not supported on the current platform. * @deprecated Use {@link #EV3Led(Direction)} instead. */ + @Deprecated public EV3Led(final int button) { - checkPlatform(); - - if (button == LEFT) { - LED_RED = ev3DevProperties.getProperty("ev3.led.left.red"); - LED_GREEN = ev3DevProperties.getProperty("ev3.led.left.green"); - direction = Direction.LEFT; - } else if (button == RIGHT) { - LED_RED = ev3DevProperties.getProperty("ev3.led.right.red"); - LED_GREEN = ev3DevProperties.getProperty("ev3.led.right.green"); - direction = Direction.RIGHT; - } else { - log.error("You are not specifying any button."); - throw new IllegalArgumentException("You are not specifying any button."); - } - + this(directionArray[button]); } /** @@ -90,13 +90,12 @@ public EV3Led(final int button) { */ private void checkPlatform() { if (!CURRENT_PLATFORM.equals(EV3DevPlatform.EV3BRICK)) { - log.error("This actuator is specific of: {}", EV3DevPlatform.EV3BRICK); + LOGGER.error("This actuator is specific of: {}", EV3DevPlatform.EV3BRICK); throw new RuntimeException("This actuator is specific of: " + EV3DevPlatform.EV3BRICK); } } //TODO Add Enums for patterns - /** * Sets the pattern of light to be shown with this LED. * @@ -109,21 +108,24 @@ private void checkPlatform() { */ @Override public void setPattern(final int pattern) { - //Off + + final String off = Integer.toString(0); + final String on = Integer.toString(255); + if (pattern == 0) { - Sysfs.writeInteger(LED_RED, 0); - Sysfs.writeInteger(LED_GREEN, 0); + greenWriter.writeString(off); + redWriter.writeString(off); } else if (pattern == 1) { - Sysfs.writeInteger(LED_RED, 0); - Sysfs.writeInteger(LED_GREEN, 255); + greenWriter.writeString(on); + redWriter.writeString(off); } else if (pattern == 2) { - Sysfs.writeInteger(LED_RED, 255); - Sysfs.writeInteger(LED_GREEN, 0); + greenWriter.writeString(off); + redWriter.writeString(on); } else if (pattern == 3) { - Sysfs.writeInteger(LED_RED, 255); - Sysfs.writeInteger(LED_GREEN, 255); + greenWriter.writeString(on); + redWriter.writeString(on); } else if (pattern > 3) { - log.debug("This feature is not implemented"); + LOGGER.debug("This feature is not implemented"); } } @@ -135,4 +137,12 @@ public void setPattern(final int pattern) { public Direction getDirection() { return direction; } + + @Override + public void close() throws IOException { + greenWriter.close(); + redWriter.close(); + } + + } diff --git a/src/main/java/ev3dev/sensors/Battery.java b/src/main/java/ev3dev/sensors/Battery.java index 55c0c225..e55d5c7d 100644 --- a/src/main/java/ev3dev/sensors/Battery.java +++ b/src/main/java/ev3dev/sensors/Battery.java @@ -3,10 +3,12 @@ import ev3dev.hardware.EV3DevDevice; import ev3dev.hardware.EV3DevFileSystem; import ev3dev.hardware.EV3DevPlatform; -import ev3dev.utils.Sysfs; +import ev3dev.utils.DataChannelRereader; import lejos.hardware.Power; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; + +import java.io.Closeable; +import java.io.IOException; /** * The class Battery interacts with EV3Dev to get information about battery used. @@ -15,21 +17,11 @@ * @see https://www.kernel.org/doc/Documentation/power/power_supply_class.txt * @see https://github.com/ev3dev/ev3dev-lang/blob/develop/wrapper-specification.md#direct-attribute-mappings-5 */ -public class Battery extends EV3DevDevice implements Power { - - private static final Logger LOGGER = LoggerFactory.getLogger(Battery.class); - - private final String BATTERY; - private final String BATTERY_EV3; - private final String BATTERY_PISTORMS; - private final String BATTERY_BRICKPI; - private final String BATTERY_BRICKPI3; +@Slf4j +public class Battery extends EV3DevDevice implements Power, Closeable { - private String BATTERY_PATH; - private final String VOLTAGE = "voltage_now"; - private final String CURRENT = "current_now"; - - private String BATTERY_PATH_LOCAL = ""; + private final DataChannelRereader voltageRereader; + private final DataChannelRereader currentRereader; private static Battery instance; @@ -51,53 +43,61 @@ private Battery() { LOGGER.debug("Init sensor"); - BATTERY = ev3DevProperties.getProperty("battery"); - BATTERY_EV3 = ev3DevProperties.getProperty("ev3.battery"); - BATTERY_PISTORMS = ev3DevProperties.getProperty("pistorms.battery"); - BATTERY_BRICKPI = ev3DevProperties.getProperty("brickpi.battery"); - BATTERY_BRICKPI3 = ev3DevProperties.getProperty("brickpi3.battery"); + String battery = ev3DevProperties.getProperty("battery"); + String batteryEv3 = ev3DevProperties.getProperty("ev3.battery"); + String batteryPistorms = ev3DevProperties.getProperty("pistorms.battery"); + String batteryBrickpi = ev3DevProperties.getProperty("brickpi.battery"); + String batteryBrickpi3 = ev3DevProperties.getProperty("brickpi3.battery"); //TODO Create separator variable for the whole project - BATTERY_PATH = EV3DevFileSystem.getRootPath() + "/" + BATTERY; + String batteryPath = EV3DevFileSystem.getRootPath() + "/" + battery; + String batteryPathLocal = ""; if (CURRENT_PLATFORM.equals(EV3DevPlatform.EV3BRICK)) { - BATTERY_PATH_LOCAL += BATTERY_PATH + "/" + BATTERY_EV3; + batteryPathLocal += batteryPath + "/" + batteryEv3; } else if (CURRENT_PLATFORM.equals(EV3DevPlatform.PISTORMS)) { - BATTERY_PATH_LOCAL += BATTERY_PATH + "/" + BATTERY_PISTORMS; + batteryPathLocal += batteryPath + "/" + batteryPistorms; } else if (CURRENT_PLATFORM.equals(EV3DevPlatform.BRICKPI)) { - BATTERY_PATH_LOCAL += BATTERY_PATH + "/" + BATTERY_BRICKPI; + batteryPathLocal += batteryPath + "/" + batteryBrickpi; } else if (CURRENT_PLATFORM.equals(EV3DevPlatform.BRICKPI3)) { - BATTERY_PATH_LOCAL += BATTERY_PATH + "/" + BATTERY_BRICKPI3; + batteryPathLocal += batteryPath + "/" + batteryBrickpi3; } + String voltage = "voltage_now"; + voltageRereader = new DataChannelRereader(batteryPathLocal + "/" + voltage); + String current = "current_now"; + currentRereader = new DataChannelRereader(batteryPath + "/" + batteryEv3 + "/" + current); + } + + public int getVoltageMicroVolt() { + return Integer.parseInt(voltageRereader.readString()); } + /** + * @return voltage of the battery in millivolts. + */ @Override public int getVoltageMilliVolt() { - return (int) Sysfs.readFloat(BATTERY_PATH_LOCAL + "/" + VOLTAGE) / 1000; + return getVoltageMicroVolt() / 1000; } /** - * Returns voltage of the battery in microvolts. - * - * @return voltage + * @return voltage of the battery in microvolts. */ public float getVoltage() { - return Sysfs.readFloat(BATTERY_PATH_LOCAL + "/" + VOLTAGE) / 1000000; + return getVoltageMicroVolt() / 1000000f; } //TODO Review output //TODO Review units /** - * Returns the current of the battery in amps. - * - * @return current + * @return current from the battery in amps, or Float.NaN if run on something other than EV3BRICK */ public float getBatteryCurrent() { if (CURRENT_PLATFORM.equals(EV3DevPlatform.EV3BRICK)) { - return Sysfs.readFloat(BATTERY_PATH + "/" + BATTERY_EV3 + "/" + CURRENT); + return Float.parseFloat(currentRereader.readString()); } else { LOGGER.warn("This method is not available for {} & {}", EV3DevPlatform.PISTORMS, EV3DevPlatform.BRICKPI); - return -1f; + return Float.NaN; } } @@ -107,4 +107,9 @@ public float getMotorCurrent() { throw new UnsupportedOperationException("This feature is not implemented"); } + @Override + public void close() throws IOException { + voltageRereader.close(); + currentRereader.close(); + } } diff --git a/src/main/java/ev3dev/utils/DataChannelRereader.java b/src/main/java/ev3dev/utils/DataChannelRereader.java index 2ba369f6..36066091 100644 --- a/src/main/java/ev3dev/utils/DataChannelRereader.java +++ b/src/main/java/ev3dev/utils/DataChannelRereader.java @@ -25,38 +25,38 @@ public class DataChannelRereader implements Closeable { * * @param path path to the file to reread * @param bufferLength length of the buffer to hold the structure - * @throws IOException when things go wrong */ - public DataChannelRereader(Path path, int bufferLength) throws IOException { + public DataChannelRereader(Path path, int bufferLength) { this.path = path; this.byteBuffer = ByteBuffer.allocate(bufferLength); - this.channel = FileChannel.open(path); + try { + this.channel = FileChannel.open(path); + } catch (IOException e) { + throw new RuntimeException("Problem opening path: " + path, e); + } } /** * Create a DataChannelRereader for pathString with the default 32-byte buffer. * * @param pathString Path to the file to reread - * @throws IOException when things go wrong */ - public DataChannelRereader(String pathString) throws IOException { + public DataChannelRereader(String pathString) { this(Paths.get(pathString),32); } /** * @return a string made from the bytes in the file; */ - public String readString() { + public synchronized String readString() { try { - int n; - do { - byteBuffer.clear(); - channel.position(0); - n = channel.read(byteBuffer); - if (n == -1) { - throw new IOException("Premature end of file "); - } - } while (n <= 0); + byteBuffer.clear(); + int n = channel.read(byteBuffer,0); + if ((n == -1) || (n == 0)) { + return ""; + } else if (n < -1) { + throw new RuntimeException("Unexpected read byte count of " + n + " while reading " + path); + } byte[] bytes = byteBuffer.array(); if (bytes[n - 1] == '\n') { @@ -69,8 +69,12 @@ public String readString() { } } + public Path getPath() { + return path; + } + @Override - public void close() throws IOException { + public synchronized void close() throws IOException { channel.close(); } } diff --git a/src/main/java/ev3dev/utils/DataChannelRewriter.java b/src/main/java/ev3dev/utils/DataChannelRewriter.java new file mode 100644 index 00000000..3a5a1f09 --- /dev/null +++ b/src/main/java/ev3dev/utils/DataChannelRewriter.java @@ -0,0 +1,75 @@ +package ev3dev.utils; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +/** + * Writer of streams that can rewrite the same channel for structured data of + * known length. The focus of this class is on performance. + * + * @author David Walend + */ +public class DataChannelRewriter implements Closeable { + + private final Path path; + private final ByteBuffer byteBuffer; + private final FileChannel channel; + + /** + * Create a DataChannelRewriter for path with a bufferLength byte buffer + * + * @param path path to the file to reread + * @param bufferLength length of the buffer to hold the structure + */ + public DataChannelRewriter(Path path, int bufferLength) { + this.path = path; + this.byteBuffer = ByteBuffer.allocate(bufferLength); + try { + this.channel = FileChannel.open(path, StandardOpenOption.WRITE); + } catch (IOException e) { + throw new RuntimeException("While opening " + path,e); + } + } + + /** + * Create a DataChannelRewriter for pathString with the default 32-byte buffer. + * + * @param pathString Path to the file to reread + */ + public DataChannelRewriter(String pathString) { + this(Paths.get(pathString),32); + } + + /** + * @param string to write. A new line character + */ + public synchronized void writeString(String string) { + try { + byteBuffer.clear(); + byteBuffer.put(string.getBytes(StandardCharsets.UTF_8)); + byteBuffer.put(((byte)'\n')); + byteBuffer.flip(); + channel.truncate(0); + channel.write(byteBuffer,0); + channel.force(false); + } catch (IOException e) { + throw new RuntimeException("Problem writing path: " + path, e); + } + } + + public Path getPath() { + return path; + } + + @Override + public synchronized void close() throws IOException { + channel.close(); + } +} + diff --git a/src/main/resources/jessie.properties b/src/main/resources/jessie.properties index f6044b38..4024e53c 100644 --- a/src/main/resources/jessie.properties +++ b/src/main/resources/jessie.properties @@ -43,10 +43,10 @@ pistorms.sensor.port.3=pistorms:BAS1 pistorms.sensor.port.4=pistorms:BAS2 #LED -ev3.led.left.red=/sys/class/leds/ev3:left:red:ev3dev/brightness -ev3.led.left.green=/sys/class/leds/ev3:left:green:ev3dev/brightness -ev3.led.right.red=/sys/class/leds/ev3:right:red:ev3dev/brightness -ev3.led.right.green=/sys/class/leds/ev3:right:green:ev3dev/brightness +ev3.led.left.red=/leds/ev3:left:red:ev3dev/brightness +ev3.led.left.green=/leds/ev3:left:green:ev3dev/brightness +ev3.led.right.red=/leds/ev3:right:red:ev3dev/brightness +ev3.led.right.green=/leds/ev3:right:green:ev3dev/brightness #KEY ev3.key=/dev/input/by-path/platform-gpio-keys.0-event diff --git a/src/main/resources/stretch.properties b/src/main/resources/stretch.properties index fc54fe91..801dd7e6 100644 --- a/src/main/resources/stretch.properties +++ b/src/main/resources/stretch.properties @@ -43,10 +43,10 @@ pistorms.sensor.port.3=pistorms:BAS1 pistorms.sensor.port.4=pistorms:BAS2 #LED -ev3.led.left.red=/sys/class/leds/led0:red:brick-status/brightness -ev3.led.left.green=/sys/class/leds/led0:green:brick-status/brightness -ev3.led.right.red=/sys/class/leds/led1:red:brick-status/brightness -ev3.led.right.green=/sys/class/leds/led1:green:brick-status/brightness +ev3.led.left.red=/leds/led0:red:brick-status/brightness +ev3.led.left.green=/leds/led0:green:brick-status/brightness +ev3.led.right.red=/leds/led1:red:brick-status/brightness +ev3.led.right.green=/leds/led1:green:brick-status/brightness #KEY ev3.key=/dev/input/by-path/platform-gpio_keys-event diff --git a/src/test/java/ev3dev/actuators/ev3/EV3LedTest.java b/src/test/java/ev3dev/actuators/ev3/EV3LedTest.java index b83351f2..0e8358cb 100644 --- a/src/test/java/ev3dev/actuators/ev3/EV3LedTest.java +++ b/src/test/java/ev3dev/actuators/ev3/EV3LedTest.java @@ -2,90 +2,95 @@ import ev3dev.hardware.EV3DevFileSystem; import ev3dev.hardware.EV3DevPlatform; -import fake_ev3dev.ev3dev.actuators.FakeLed; +import fake_ev3dev.ev3dev.actuators.ev3.FakeLed; import fake_ev3dev.ev3dev.sensors.FakeBattery; import lejos.hardware.LED; -import org.junit.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.IOException; +import static org.assertj.core.api.BDDAssertions.then; + public class EV3LedTest { + //TODO Refactor exception cases with JUnit 5 @Rule public ExpectedException thrown = ExpectedException.none(); @Before - public void resetTest() throws IOException, NoSuchFieldException, IllegalAccessException { - - //Reset the singleton - //https://stackoverflow.com/questions/8256989/singleton-and-unit-testing - //Field instance = Sound.class.getDeclaredField("instance"); - //instance.setAccessible(true); - //instance.set(null, null); - - FakeBattery.resetEV3DevInfrastructure(); + public void resetTest() throws IOException { System.setProperty(EV3DevFileSystem.EV3DEV_TESTING_KEY, FakeBattery.EV3DEV_FAKE_SYSTEM_PATH); + FakeBattery.resetEV3DevInfrastructure(); + new FakeBattery(EV3DevPlatform.EV3BRICK); } @Test - public void constructorLeftTest() throws Exception { - - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.EV3BRICK); - - LED led = new EV3Led(EV3Led.LEFT); - led = new EV3Led(EV3Led.Direction.LEFT); - } + public void given_actuator_when_useConstructorLeft_then_Ok() throws Exception { - @Test - public void constructorRightTest() throws Exception { + //Given + FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.EV3BRICK); + //When + @SuppressWarnings("deprecation") LED led = new EV3Led(EV3Led.LEFT); + LED led2 = new EV3Led(EV3Led.Direction.LEFT); - LED led = new EV3Led(EV3Led.RIGHT); - led = new EV3Led(EV3Led.Direction.RIGHT); + //Then + then(led).isNotNull(); + then(led2).isNotNull(); } - @Ignore("Review how to reset a Static classic in JUnit") @Test - public void usingLedOnEV3BrickPlatformTest() throws Exception { + public void given_actuator_when_useConstructorRight_then_Ok() throws Exception { - thrown.expect(RuntimeException.class); + //Given + FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.BRICKPI); + //When + @SuppressWarnings("deprecation") LED led = new EV3Led(EV3Led.RIGHT); + LED led2 = new EV3Led(EV3Led.Direction.RIGHT); - LED led = new EV3Led(EV3Led.LEFT); + //Then + then(led).isNotNull(); + then(led2).isNotNull(); } @Test - public void badButtonTest() throws Exception { - - thrown.expect(RuntimeException.class); + public void given_actuator_when_useConstructorWithBadParameter_then_Ko() throws Exception { - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.EV3BRICK); + //Given + FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); - LED led = new EV3Led(2); + //When + //Then + thrown.expect(ArrayIndexOutOfBoundsException.class); + @SuppressWarnings("deprecation") LED led = new EV3Led(4); } @Test - public void badDirectionTest() throws Exception { - - thrown.expect(IllegalArgumentException.class); + public void given_actuator_when_useConstructorWithNull_then_Ko() throws Exception { - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.EV3BRICK); + //Given + FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); + //When + //Then + thrown.expect(IllegalArgumentException.class); LED led = new EV3Led(null); } @Test - public void leftLedPatternsTest() throws Exception { + public void given_actuator_when_leftPatterns_then_Ok() throws Exception { - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.EV3BRICK); - final FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); + //Given + FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); - LED led = new EV3Led(EV3Led.LEFT); + //When + LED led = new EV3Led(EV3Led.Direction.LEFT); led.setPattern(1); led.setPattern(2); led.setPattern(3); @@ -96,15 +101,19 @@ public void leftLedPatternsTest() throws Exception { led.setPattern(2); led.setPattern(3); led.setPattern(4); + + //Then + //TODO Currently, it is not possible to execute a verify or something similar } @Test - public void rightLedPatternsTest() throws Exception { + public void given_actuator_when_rightPatterns_then_Ok() throws Exception { - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.EV3BRICK); - final FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); + //Given + FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); - LED led = new EV3Led(EV3Led.RIGHT); + //When + @SuppressWarnings("deprecation") LED led = new EV3Led(EV3Led.RIGHT); led.setPattern(1); led.setPattern(2); led.setPattern(3); @@ -115,19 +124,25 @@ public void rightLedPatternsTest() throws Exception { led.setPattern(2); led.setPattern(3); led.setPattern(4); + + //Then + //TODO Currently, it is not possible to execute a verify or something similar } @Test - public void getDirectionTest() throws Exception { + public void given_actuator_when_getDirection_then_Ok() throws Exception { - final FakeBattery fakeBattery = new FakeBattery(EV3DevPlatform.EV3BRICK); - final FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); + //Given + FakeLed fakeLed = new FakeLed(EV3DevPlatform.EV3BRICK); - EV3Led led = new EV3Led(EV3Led.RIGHT); - Assert.assertEquals(EV3Led.Direction.RIGHT, led.getDirection()); + //When + @SuppressWarnings("deprecation") EV3Led led = new EV3Led(EV3Led.RIGHT); + EV3Led led2 = new EV3Led(EV3Led.Direction.RIGHT); - led = new EV3Led(EV3Led.Direction.RIGHT); - Assert.assertEquals(EV3Led.Direction.RIGHT, led.getDirection()); + //Then + EV3Led.Direction expectdedDirection = EV3Led.Direction.RIGHT; + then(led.getDirection()).isEqualTo(expectdedDirection); + then(led2.getDirection()).isEqualTo(expectdedDirection); } } diff --git a/src/test/java/ev3dev/utils/DataChannelRereaderConcurrencyTest.java b/src/test/java/ev3dev/utils/DataChannelRereaderConcurrencyTest.java new file mode 100644 index 00000000..34106caa --- /dev/null +++ b/src/test/java/ev3dev/utils/DataChannelRereaderConcurrencyTest.java @@ -0,0 +1,279 @@ +package ev3dev.utils; + +import java.io.File; +import java.util.concurrent.CompletableFuture; +import java.util.stream.IntStream; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import static org.assertj.core.api.BDDAssertions.then; + +@Slf4j +public class DataChannelRereaderConcurrencyTest { + + final String fileName = "./pairs.txt"; + final String fileName2 = "./odds.txt"; + final Integer limit = 1000; + + @Before + @SneakyThrows + public void createFiles() { + new File(fileName).createNewFile(); + new File(fileName2).createNewFile(); + } + + /** + * Writer1 -> pairs.txt + * Reader1 <- pairs.txt + * Reader2 <- pairs.txt + * + * Writer1 -> odds.txt + * Reader1 <- odds.txt + * Reader2 <- odds.txt + */ + @Ignore + @Test + public void given_multiple_DataChannelRereader_when_execute_concurrently_then_Ok() { + + CompletableFuture request1 = asyncWriteFile(true); + CompletableFuture request2 = asyncWriteFile(false); + CompletableFuture request3 = asyncReadFile(true); + CompletableFuture request4 = asyncReadFile(false); + CompletableFuture request5 = asyncReadFile2(true); + CompletableFuture request6 = asyncReadFile2(false); + + CompletableFuture combinedFuture = CompletableFuture.allOf( + request1, + request2, + request3, + request4, + request5, + request6); + + combinedFuture.join(); + + then(request1.isDone()).isTrue(); + then(request2.isDone()).isTrue(); + then(request3.isDone()).isTrue(); + then(request4.isDone()).isTrue(); + then(request5.isDone()).isTrue(); + then(request6.isDone()).isTrue(); + + then(request1.join()).isEqualTo("Ok asyncWriteFile"); + then(request2.join()).isEqualTo("Ok asyncWriteFile"); + then(request3.join()).isEqualTo("Ok asyncReadFile"); + then(request4.join()).isEqualTo("Ok asyncReadFile"); + then(request5.join()).isEqualTo("Ok asyncReadFile2"); + then(request6.join()).isEqualTo("Ok asyncReadFile2"); + + System.out.println("End"); + } + + private void readFile(String file, Boolean flag) { + Integer value = Sysfs.readInteger(file); + if (flag) { + then(value % 2 == 0).isTrue(); + } else { + then(value % 2 != 0).isTrue(); + } + } + + private void readFile2(String file, Boolean flag) { + DataChannelRereader dataChannelRereader = new DataChannelRereader(file); + Integer value = Integer.parseInt(dataChannelRereader.readString()); + if (flag) { + then(value % 2 == 0).isTrue(); + } else { + then(value % 2 != 0).isTrue(); + } + } + + private CompletableFuture asyncReadFile(boolean flag) { + CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> { + IntStream + .rangeClosed(1, limit) + .forEach(i -> { + if (flag) { + readFile(fileName, flag); + } else { + readFile(fileName2, flag); + } + }); + return "Ok asyncReadFile"; + }) + .handle((input, exception) -> { + if (exception != null) { + LOGGER.warn(exception.getLocalizedMessage(), exception); + return "Ko asyncReadFile"; + } + return input; + }); + return cf1; + } + + private CompletableFuture asyncReadFile2(boolean flag) { + CompletableFuture cf = CompletableFuture.supplyAsync(() -> { + IntStream + .rangeClosed(1, limit) + .forEach(i -> { + if (flag) { + readFile2(fileName, flag); + } else { + readFile2(fileName2, flag); + } + }); + return "Ok asyncReadFile2"; + }) + .handle((input, exception) -> { + if (exception != null) { + LOGGER.warn(exception.getLocalizedMessage(), exception); + return "Ko asyncReadFile2"; + } + return input; + }); + return cf; + } + + private void writeFile(String file, String value) { + Sysfs.writeString(file, value); + } + + private CompletableFuture asyncWriteFile(boolean flag) { + CompletableFuture cf = CompletableFuture.supplyAsync(() -> { + IntStream + .rangeClosed(1, limit) + .filter(i -> { + if (flag) { + return i % 2 == 0; + } else { + return i % 2 != 0; + } + }) + .forEach(i -> { + if (flag) { + writeFile(fileName, String.valueOf(i)); + } else { + writeFile(fileName2, String.valueOf(i)); + } + }); + return "Ok asyncWriteFile"; + }) + .handle((input, exception) -> { + if (exception != null) { + LOGGER.warn(exception.getLocalizedMessage(), exception); + return "Ko asyncWriteFile"; + } + return input; + }); + + return cf; + } + + /** + * Writer1 -> pairs.txt + * Reader1 <- pairs.txt + * + * Writer1 -> odds.txt + * Reader1 <- odds.txt + */ + @Ignore + @Test + public void given_multiple_DataChannelRereader_when_execute_concurrently_then_Ok2() { + + CompletableFuture request1 = asyncWriteFile(true); + CompletableFuture request2 = asyncWriteFile(false); + CompletableFuture request3 = asyncReadFile(true); + CompletableFuture request4 = asyncReadFile(false); + + CompletableFuture combinedFuture = CompletableFuture.allOf( + request1, + request2, + request3, + request4); + + combinedFuture.join(); + + then(request1.isDone()).isTrue(); + then(request2.isDone()).isTrue(); + then(request3.isDone()).isTrue(); + then(request4.isDone()).isTrue(); + + then(request1.join()).isEqualTo("Ok asyncWriteFile"); + then(request2.join()).isEqualTo("Ok asyncWriteFile"); + then(request3.join()).isEqualTo("Ok asyncReadFile"); + then(request4.join()).isEqualTo("Ok asyncReadFile"); + + System.out.println("End"); + } + + /** + * Reader1 <- pairs.txt + * Reader1 <- odds.txt + */ + @Test + public void given_multiple_DataChannelRereader_when_execute_concurrently_then_Ok3() { + + writeFile(fileName, "2"); + writeFile(fileName2, "1"); + + CompletableFuture request1 = asyncReadFile(true); + CompletableFuture request2 = asyncReadFile(false); + + CompletableFuture combinedFuture = CompletableFuture.allOf( + request1, + request2); + + combinedFuture.join(); + + then(request1.isDone()).isTrue(); + then(request2.isDone()).isTrue(); + + then(request1.join()).isEqualTo("Ok asyncReadFile"); + then(request2.join()).isEqualTo("Ok asyncReadFile"); + + System.out.println("End"); + } + + /** + * Writer1 -> pairs.txt Once + * Writer1 -> odds.txt Once + * Reader1 <- pairs.txt + * Reader1 <- odds.txt + * Reader2 <- pairs.txt + * Reader2 <- odds.txt + */ + @Test + public void given_multiple_DataChannelRereader_when_execute_concurrently_then_Ok4() { + + writeFile(fileName, "2"); + writeFile(fileName2, "1"); + + CompletableFuture request1 = asyncReadFile(true); + CompletableFuture request2 = asyncReadFile(false); + CompletableFuture request3 = asyncReadFile2(true); + CompletableFuture request4 = asyncReadFile2(false); + + CompletableFuture combinedFuture = CompletableFuture.allOf( + request1, + request2, + request3, + request4); + + combinedFuture.join(); + + then(request1.isDone()).isTrue(); + then(request2.isDone()).isTrue(); + then(request3.isDone()).isTrue(); + then(request4.isDone()).isTrue(); + + then(request1.join()).isEqualTo("Ok asyncReadFile"); + then(request2.join()).isEqualTo("Ok asyncReadFile"); + then(request3.join()).isEqualTo("Ok asyncReadFile2"); + then(request4.join()).isEqualTo("Ok asyncReadFile2"); + + System.out.println("End"); + } +} diff --git a/src/test/java/ev3dev/utils/DataChannelRereaderRaceTest.java b/src/test/java/ev3dev/utils/DataChannelRereaderRaceTest.java new file mode 100644 index 00000000..1df8adcd --- /dev/null +++ b/src/test/java/ev3dev/utils/DataChannelRereaderRaceTest.java @@ -0,0 +1,78 @@ +package ev3dev.utils; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.stream.IntStream; + +import static org.assertj.core.api.BDDAssertions.then; + +@Slf4j +public class DataChannelRereaderRaceTest { + + final String fileName = "./race.txt"; + final Integer limit = 10000; + + @Before + @SneakyThrows + public void createFiles() throws IOException { + Files.writeString(Path.of(fileName), "test1234"); + } + + /** + * Reader1 <- race.txt + * Reader2 <- race.txt + * Reader3 <- race.txt + * Reader4 <- race.txt + */ + @Test + public void given_multiple_DataChannelRereader_when_execute_concurrently_then_Ok() { + DataChannelRereader reader = new DataChannelRereader(fileName); + + CompletableFuture rq1 = asyncReadFile(reader); + CompletableFuture rq2 = asyncReadFile(reader); + CompletableFuture rq3 = asyncReadFile(reader); + CompletableFuture rq4 = asyncReadFile(reader); + + CompletableFuture combinedFuture = CompletableFuture.allOf(rq1, rq2, rq3, rq4); + combinedFuture.join(); + + then(rq1.isDone()).isTrue(); + then(rq2.isDone()).isTrue(); + then(rq3.isDone()).isTrue(); + then(rq4.isDone()).isTrue(); + + then(rq1.join()).isEqualTo("Ok asyncReadFile"); + then(rq2.join()).isEqualTo("Ok asyncReadFile"); + then(rq3.join()).isEqualTo("Ok asyncReadFile"); + then(rq4.join()).isEqualTo("Ok asyncReadFile"); + System.out.println("End"); + } + + private void readFile(DataChannelRereader reader) { + then(reader.readString()).isEqualTo("test1234"); + } + + private CompletableFuture asyncReadFile(DataChannelRereader reader) { + CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> { + IntStream + .rangeClosed(1, limit) + .forEach(i -> readFile(reader)); + return "Ok asyncReadFile"; + }) + .handle((input, exception) -> { + if (exception != null) { + LOGGER.warn(exception.getLocalizedMessage(), exception); + return "Ko asyncReadFile"; + } + return input; + }); + return cf1; + } +} diff --git a/src/test/java/ev3dev/utils/DataChannelRereaderTest.java b/src/test/java/ev3dev/utils/DataChannelRereaderTest.java new file mode 100644 index 00000000..86a8352b --- /dev/null +++ b/src/test/java/ev3dev/utils/DataChannelRereaderTest.java @@ -0,0 +1,89 @@ +package ev3dev.utils; + +import java.io.IOException; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; + +/** + * Some tests of DataChannelRereader. + * + * @author David Walend + */ +public class DataChannelRereaderTest { + + static Path tempDirectory; + static Path testPath; + static final String testString = "Written String"; + static final String differentTestString = "Different String"; + + @BeforeClass + public static void createFiles() throws IOException { + tempDirectory = Files.createTempDirectory("DataChannelRereaderTest"); + testPath = Files.createFile(Path.of(tempDirectory.toString(),"testFile")); + Files.write(testPath, testString.getBytes()); + } + + @Before + public void writeFile() throws IOException { + Files.write(testPath, testString.getBytes()); + } + + @AfterClass + public static void cleanupFiles() throws IOException { + Files.delete(testPath); + Files.delete(tempDirectory); + } + + + @Test + public void testOpenClose() throws IOException { + DataChannelRereader rereader = new DataChannelRereader(testPath,32); + rereader.close(); + } + + @Test + public void testOpenReadClose() throws IOException { + DataChannelRereader rereader = new DataChannelRereader(testPath,32); + String readString = rereader.readString(); + rereader.close(); + + assertEquals(testString,readString); + } + + @Test + public void testClosable() throws IOException { + try(DataChannelRereader rereader = new DataChannelRereader(testPath,32)){ + String readString = rereader.readString(); + assertEquals(testString,readString); + } + } + + @Test + public void testOpenReadTwoThingsClose() throws IOException { + DataChannelRereader rereader = new DataChannelRereader(testPath,32); + String readString = rereader.readString(); + assertEquals(testString,readString); + + Files.write(testPath, differentTestString.getBytes()); + + String readString2 = rereader.readString(); + assertEquals(differentTestString,readString2); + + rereader.close(); + } + + @Test(expected = RuntimeException.class) + public void testOpenNonexistentFile() throws IOException { + Path badPath = Path.of("/does/not/exist"); + + DataChannelRereader rereader = new DataChannelRereader(badPath,32); + rereader.close(); + } +} diff --git a/src/test/java/ev3dev/utils/DataChannelRewriterTest.java b/src/test/java/ev3dev/utils/DataChannelRewriterTest.java new file mode 100644 index 00000000..dcc534b1 --- /dev/null +++ b/src/test/java/ev3dev/utils/DataChannelRewriterTest.java @@ -0,0 +1,92 @@ +package ev3dev.utils; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; + +/** + * Some tests of DataChannelRewriter. + * + * @author David Walend + */ +public class DataChannelRewriterTest { + + static Path tempDirectory; + static Path testPath; + static final String startString = "Original String"; + static final String differentString = "Written String"; + + @BeforeClass + public static void createFiles() throws IOException { + tempDirectory = Files.createTempDirectory("DataChannelRewriterTest"); + testPath = Files.createFile(Path.of(tempDirectory.toString(),"testFile")); + Files.write(testPath, startString.getBytes()); + } + + @Before + public void writeFile() throws IOException { + Files.write(testPath, startString.getBytes()); + } + + @AfterClass + public static void cleanupFiles() throws IOException { + Files.delete(testPath); + Files.delete(tempDirectory); + } + + + @Test + public void testOpenClose() throws IOException { + DataChannelRewriter rewriter = new DataChannelRewriter(testPath,32); + rewriter.close(); + + assertEquals(startString,Files.readString(testPath)); + } + + @Test + public void testOpenReadClose() throws IOException { + DataChannelRewriter rewriter = new DataChannelRewriter(testPath,32); + rewriter.writeString(differentString); + rewriter.close(); + + assertEquals(differentString+"\n",Files.readString(testPath)); + } + + @Test + public void testClosable() throws IOException { + try(DataChannelRewriter rewriter = new DataChannelRewriter(testPath,32)){ + rewriter.writeString(differentString); + } + assertEquals(differentString+"\n",Files.readString(testPath)); + } + + @Test + public void testOpenWriteTwoThingsClose() throws IOException { + DataChannelRewriter rewriter = new DataChannelRewriter(testPath,32); + rewriter.writeString(differentString); + assertEquals(differentString+"\n",Files.readString(testPath)); + + String anotherString = "Another String"; + rewriter.writeString(anotherString); + + assertEquals(anotherString+"\n",Files.readString(testPath)); + + rewriter.close(); + } + + @Test(expected = RuntimeException.class) + public void testOpenNonexistentFile() throws IOException { + Path badPath = Path.of("/does/not/exist"); + + DataChannelRewriter rewriter = new DataChannelRewriter(badPath,32); + rewriter.writeString(differentString); + rewriter.close(); + } +} diff --git a/src/test/java/fake_ev3dev/ev3dev/actuators/FakeLed.java b/src/test/java/fake_ev3dev/ev3dev/actuators/ev3/FakeLed.java similarity index 82% rename from src/test/java/fake_ev3dev/ev3dev/actuators/FakeLed.java rename to src/test/java/fake_ev3dev/ev3dev/actuators/ev3/FakeLed.java index 23063021..c2478571 100644 --- a/src/test/java/fake_ev3dev/ev3dev/actuators/FakeLed.java +++ b/src/test/java/fake_ev3dev/ev3dev/actuators/ev3/FakeLed.java @@ -1,18 +1,21 @@ -package fake_ev3dev.ev3dev.actuators; +package fake_ev3dev.ev3dev.actuators.ev3; import ev3dev.hardware.EV3DevPlatform; +import ev3dev.utils.Shell; import fake_ev3dev.BaseElement; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class FakeLed extends BaseElement{ - public static final String LEFT_LED = "leds/ev3:left"; - public static final String RIGHT_LED = "leds/ev3:right"; - public static final String RED_LED = ":red:ev3dev"; - public static final String GREEN_LED = ":green:ev3dev"; + public static final String LEFT_LED = "leds/led0"; + public static final String RIGHT_LED = "leds/led1"; + public static final String RED_LED = ":red:brick-status"; + public static final String GREEN_LED = ":green:brick-status"; public static final String BRIGHTNESS = "brightness"; public FakeLed(final EV3DevPlatform ev3DevPlatform) throws IOException { @@ -71,6 +74,8 @@ public FakeLed(final EV3DevPlatform ev3DevPlatform) throws IOException { BRIGHTNESS); createFile(ledLeftRedBrightness); + var result = Shell.execute("tree " + EV3DEV_FAKE_SYSTEM_PATH); + LOGGER.info(result); } } diff --git a/src/test/resources/simplelogger.properties b/src/test/resources/simplelogger.properties new file mode 100644 index 00000000..a1143482 --- /dev/null +++ b/src/test/resources/simplelogger.properties @@ -0,0 +1,18 @@ +# SLF4J's SimpleLogger configuration file +# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. + +org.slf4j.simpleLogger.defaultLogLevel=info + +#org.slf4j.simpleLogger.log.ev3dev.hardware=trace +#org.slf4j.simpleLogger.log.ev3dev.utils=trace + + + +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss +org.slf4j.simpleLogger.showThreadName=true +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false + +org.slf4j.simpleLogger.logFile=System.err +#org.slf4j.simpleLogger.logFile=/home/robot/java/programs/logs.log pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy