diff --git a/pom.xml b/pom.xml index bf40001..d15407a 100644 --- a/pom.xml +++ b/pom.xml @@ -95,6 +95,20 @@ ${assertj.version} test + + + org.mockito + mockito-core + 5.6.0 + test + + + + org.mockito + mockito-junit-jupiter + 5.6.0 + test + @@ -106,6 +120,10 @@ org.apache.maven.plugins maven-surefire-plugin + + -Dnet.bytebuddy.experimental=true + true + diff --git a/src/main/java/edu/hw2/task1/Expr.java b/src/main/java/edu/hw2/task1/Expr.java new file mode 100644 index 0000000..bd7cef3 --- /dev/null +++ b/src/main/java/edu/hw2/task1/Expr.java @@ -0,0 +1,40 @@ +package edu.hw2.task1; + +public sealed interface Expr { + double evaluate(); + + record Constant(double value) implements Expr { + @Override + public double evaluate() { + return value; + } + } + + public record Negate(Expr value) implements Expr { + @Override + public double evaluate() { + return value.evaluate() * -1; + } + } + + record Exponent(Expr value, double exponent) implements Expr { + @Override + public double evaluate() { + return Math.pow(value.evaluate(), exponent); + } + } + + record Addition(Expr value1, Expr value2) implements Expr { + @Override + public double evaluate() { + return value1.evaluate() + value2.evaluate(); + } + } + + record Multiplication(Expr value1, Expr value2) implements Expr { + @Override + public double evaluate() { + return value1.evaluate() * value2.evaluate(); + } + } +} diff --git a/src/main/java/edu/hw2/task2/Rectangle.java b/src/main/java/edu/hw2/task2/Rectangle.java new file mode 100644 index 0000000..ccdfac5 --- /dev/null +++ b/src/main/java/edu/hw2/task2/Rectangle.java @@ -0,0 +1,23 @@ +package edu.hw2.task2; + +public class Rectangle { + private final int width; + private final int height; + + public Rectangle(int width, int height) { + this.width = width; + this.height = height; + } + + public Rectangle setWidth(int width) { + return new Rectangle(width, height); + } + + public Rectangle setHeight(int height) { + return new Rectangle(width, height); + } + + public double area() { + return width * height; + } +} diff --git a/src/main/java/edu/hw2/task2/Square.java b/src/main/java/edu/hw2/task2/Square.java new file mode 100644 index 0000000..40e619e --- /dev/null +++ b/src/main/java/edu/hw2/task2/Square.java @@ -0,0 +1,9 @@ +package edu.hw2.task2; + +public class Square extends Rectangle { + + public Square(int sideLength) { + super(sideLength, sideLength); + } + +} diff --git a/src/main/java/edu/hw2/task3/ConnectionException.java b/src/main/java/edu/hw2/task3/ConnectionException.java new file mode 100644 index 0000000..749df4e --- /dev/null +++ b/src/main/java/edu/hw2/task3/ConnectionException.java @@ -0,0 +1,11 @@ +package edu.hw2.task3; + +public class ConnectionException extends RuntimeException { + public ConnectionException(String message) { + super(message); + } + + public ConnectionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/edu/hw2/task3/PopularCommandExecutor.java b/src/main/java/edu/hw2/task3/PopularCommandExecutor.java new file mode 100644 index 0000000..3fc5066 --- /dev/null +++ b/src/main/java/edu/hw2/task3/PopularCommandExecutor.java @@ -0,0 +1,43 @@ +package edu.hw2.task3; + +import edu.hw2.task3.connection.Connection; +import edu.hw2.task3.connectionmanager.ConnectionManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public final class PopularCommandExecutor { + private final ConnectionManager manager; + private final int maxAttemps; + private final static Logger LOGGER = LogManager.getLogger(); + + public PopularCommandExecutor(ConnectionManager manager, int maxAttemps) { + this.manager = manager; + this.maxAttemps = maxAttemps; + } + + public void updatePackages() { + tryExecute("apt update && apt upgrade -y"); + } + + private void tryExecute(String command) { + int attemps = 0; + while (attemps < maxAttemps) { + Connection connection = manager.getConnection(); + try { + connection.execute(command); + return; + } catch (ConnectionException e) { + if (attemps == maxAttemps - 1) { + throw new ConnectionException(e.getMessage(), e.getCause()); + } + attemps++; + } finally { + try { + connection.close(); + } catch (Exception closeException) { + LOGGER.warn(connection.toString() + " didnt close!"); + } + } + } + } +} diff --git a/src/main/java/edu/hw2/task3/connection/Connection.java b/src/main/java/edu/hw2/task3/connection/Connection.java new file mode 100644 index 0000000..08d8603 --- /dev/null +++ b/src/main/java/edu/hw2/task3/connection/Connection.java @@ -0,0 +1,7 @@ +package edu.hw2.task3.connection; + +public interface Connection extends AutoCloseable { + + void execute(String command); + +} diff --git a/src/main/java/edu/hw2/task3/connection/FaultyConnection.java b/src/main/java/edu/hw2/task3/connection/FaultyConnection.java new file mode 100644 index 0000000..3443204 --- /dev/null +++ b/src/main/java/edu/hw2/task3/connection/FaultyConnection.java @@ -0,0 +1,27 @@ +package edu.hw2.task3.connection; + +import edu.hw2.task3.ConnectionException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class FaultyConnection implements Connection { + private final static Logger LOGGER = LogManager.getLogger(); + private final static double PROBABILITY_BOUND = 0.8; + + @Override + public void execute(String command) { + if (isConnectionFailed()) { + throw new ConnectionException("Connection cannot be established"); + } + LOGGER.info(command); + } + + @Override + public void close() { + LOGGER.info("Connection closed"); + } + + private boolean isConnectionFailed() { + return Math.random() < PROBABILITY_BOUND; + } +} diff --git a/src/main/java/edu/hw2/task3/connection/StableConnection.java b/src/main/java/edu/hw2/task3/connection/StableConnection.java new file mode 100644 index 0000000..26af2be --- /dev/null +++ b/src/main/java/edu/hw2/task3/connection/StableConnection.java @@ -0,0 +1,18 @@ +package edu.hw2.task3.connection; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class StableConnection implements Connection { + private final static Logger LOGGER = LogManager.getLogger(); + + @Override + public void execute(String command) { + LOGGER.info(command); + } + + @Override + public void close() { + LOGGER.info("Connection closed!"); + } +} diff --git a/src/main/java/edu/hw2/task3/connectionmanager/ConnectionManager.java b/src/main/java/edu/hw2/task3/connectionmanager/ConnectionManager.java new file mode 100644 index 0000000..a16efed --- /dev/null +++ b/src/main/java/edu/hw2/task3/connectionmanager/ConnectionManager.java @@ -0,0 +1,9 @@ +package edu.hw2.task3.connectionmanager; + +import edu.hw2.task3.connection.Connection; + +public interface ConnectionManager { + + Connection getConnection(); + +} diff --git a/src/main/java/edu/hw2/task3/connectionmanager/DefaultConnectionManager.java b/src/main/java/edu/hw2/task3/connectionmanager/DefaultConnectionManager.java new file mode 100644 index 0000000..8552031 --- /dev/null +++ b/src/main/java/edu/hw2/task3/connectionmanager/DefaultConnectionManager.java @@ -0,0 +1,21 @@ +package edu.hw2.task3.connectionmanager; + +import edu.hw2.task3.connection.Connection; +import edu.hw2.task3.connection.FaultyConnection; +import edu.hw2.task3.connection.StableConnection; + +public class DefaultConnectionManager implements ConnectionManager { + private final static double PROBABLITY_BOUND = 0.5; + + @Override + public Connection getConnection() { + if (isFaultyConnection()) { + return new FaultyConnection(); + } + return new StableConnection(); + } + + private boolean isFaultyConnection() { + return Math.random() < PROBABLITY_BOUND; + } +} diff --git a/src/main/java/edu/hw2/task3/connectionmanager/FaultyConnectionManager.java b/src/main/java/edu/hw2/task3/connectionmanager/FaultyConnectionManager.java new file mode 100644 index 0000000..44d951d --- /dev/null +++ b/src/main/java/edu/hw2/task3/connectionmanager/FaultyConnectionManager.java @@ -0,0 +1,11 @@ +package edu.hw2.task3.connectionmanager; + +import edu.hw2.task3.connection.Connection; +import edu.hw2.task3.connection.FaultyConnection; + +public class FaultyConnectionManager implements ConnectionManager { + @Override + public Connection getConnection() { + return new FaultyConnection(); + } +} diff --git a/src/main/java/edu/hw2/task4/Call.java b/src/main/java/edu/hw2/task4/Call.java new file mode 100644 index 0000000..af0170d --- /dev/null +++ b/src/main/java/edu/hw2/task4/Call.java @@ -0,0 +1,13 @@ +package edu.hw2.task4; + +public class Call { + private Call() { + } + + public static CallingInfo callingInfo() { + var stackTrace = new Throwable().getStackTrace(); + String callerMethodName = stackTrace[1].getMethodName(); + String callerClassName = stackTrace[1].getClassName(); + return new CallingInfo(callerClassName, callerMethodName); + } +} diff --git a/src/main/java/edu/hw2/task4/CallingInfo.java b/src/main/java/edu/hw2/task4/CallingInfo.java new file mode 100644 index 0000000..6fae1b0 --- /dev/null +++ b/src/main/java/edu/hw2/task4/CallingInfo.java @@ -0,0 +1,4 @@ +package edu.hw2.task4; + +public record CallingInfo(String className, String methodName) { +} diff --git a/src/test/java/edu/hw2/task1/ExprTest.java b/src/test/java/edu/hw2/task1/ExprTest.java new file mode 100644 index 0000000..6266d9d --- /dev/null +++ b/src/test/java/edu/hw2/task1/ExprTest.java @@ -0,0 +1,37 @@ +package edu.hw2.task1; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class ExprTest { + @Test + @DisplayName("Базовый тест") + void expr_shouldCorrectlyEvaluate_basicTest() { + var two = new Expr.Constant(2); + var four = new Expr.Constant(4); + var negOne = new Expr.Negate(new Expr.Constant(1)); + var sumTwoFour = new Expr.Addition(two, four); + var mult = new Expr.Multiplication(sumTwoFour, negOne); + var exp = new Expr.Exponent(mult, 2); + var res = new Expr.Addition(exp, new Expr.Constant(1)); + + assertThat(res.evaluate()).isEqualTo(37.0); + } + + @Test + @DisplayName("Отрицательное число в Negate") + void expr_shouldEvaluateMinusOne_whenValueAlreadyNegative() { + var negOne = new Expr.Negate(new Expr.Constant(-1)); + + assertThat(negOne.evaluate()).isEqualTo(1); + } + + @Test + @DisplayName("Отрицательные числа в Exponent") + void expr_shouldExponentCorrectly_whenValueAndExponentNegative() { + var negOne = new Expr.Exponent(new Expr.Constant(-2), -5); + + assertThat(negOne.evaluate()).isEqualTo(-0.03125); + } +} diff --git a/src/test/java/edu/hw2/task2/RectangleTest.java b/src/test/java/edu/hw2/task2/RectangleTest.java new file mode 100644 index 0000000..5bc0c71 --- /dev/null +++ b/src/test/java/edu/hw2/task2/RectangleTest.java @@ -0,0 +1,41 @@ +package edu.hw2.task2; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class RectangleTest { + @Test + @DisplayName("Проверка LSP") + public void squareArea_shouldReturnCorrectSquareArea() { + Rectangle square = new Square(30); + double area = square.area(); + assertThat(area).isEqualTo(900); + } + @Test + @DisplayName("Проверка LSP2") + public void squareArea2_shouldReturnCorrectSquareArea() { + Rectangle square = new Square(30); + double area = square.setHeight(30).setWidth(40).area(); + assertThat(area).isEqualTo(1200); + } + + @Test + @DisplayName("Квадрату устанавливается ширина и высота") + public void squareArea_shouldReturnCorrectSquareArea_whenSquareHasWidthAndHeight() { + Rectangle square = new Square(30); + square = square.setHeight(99); + square = square.setWidth(20); + double area = square.area(); + assertThat(area).isEqualTo(1980); + } + + @Test + @DisplayName("Квадрату устанавливается ширина и высота, но объект не изменяется") + public void squareArea_shouldReturnInitialSquareArea() { + Rectangle square = new Square(30).setHeight(99); + double area = square.area(); + assertThat(area).isEqualTo(2970); + } + +} diff --git a/src/test/java/edu/hw2/task3/PopularCommandExecutorTest.java b/src/test/java/edu/hw2/task3/PopularCommandExecutorTest.java new file mode 100644 index 0000000..bb98bfb --- /dev/null +++ b/src/test/java/edu/hw2/task3/PopularCommandExecutorTest.java @@ -0,0 +1,47 @@ +package edu.hw2.task3; + +import edu.hw2.task3.connection.Connection; +import edu.hw2.task3.connection.FaultyConnection; +import edu.hw2.task3.connection.StableConnection; +import edu.hw2.task3.connectionmanager.ConnectionManager; +import edu.hw2.task3.connectionmanager.DefaultConnectionManager; +import edu.hw2.task3.connectionmanager.FaultyConnectionManager; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class PopularCommandExecutorTest { + private static final int MAX_ATTEMPS = 3; + + private ConnectionManager connectionManager = mock(DefaultConnectionManager.class); + + private PopularCommandExecutor executor = new PopularCommandExecutor(connectionManager, MAX_ATTEMPS); + + @Test + @DisplayName("Проверка на выполнение команды и закрытие соединения") + void updatePackages_shouldExecuteCommandAndCloseConnection() throws Exception { + String command = "apt update && apt upgrade -y"; + Connection connection = mock(StableConnection.class); + when(connectionManager.getConnection()).thenReturn(connection); + + executor.updatePackages(); + + verify(connection, times(1)).execute(command); + verify(connection, times(1)).close(); + } + @Test + @DisplayName("Проверка на невыполнение команды и выброс ошибки") + void updatePackages_shouldNotExecuteCommandAndThrowConnectionException() throws Exception { + String command = "apt update && apt upgrade -y"; + Connection connection = mock(FaultyConnection.class); + when(connectionManager.getConnection()).thenReturn(connection); + + executor.updatePackages(); + + verify(connection, times(1)).execute(command); + verify(connection, times(1)).close(); + } +} diff --git a/src/test/java/edu/hw2/task4/CallingInfoTest.java b/src/test/java/edu/hw2/task4/CallingInfoTest.java new file mode 100644 index 0000000..f597e68 --- /dev/null +++ b/src/test/java/edu/hw2/task4/CallingInfoTest.java @@ -0,0 +1,22 @@ +package edu.hw2.task4; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class CallingInfoTest { + @Test + @DisplayName("Вызов метода из CallingInfoTest") + public void callingInfo_ShouldReturnThisClassAndMethodName() { + var info = Call.callingInfo(); + + String className = this.getClass().getName(); + String methodName = new Object() {} + .getClass() + .getEnclosingMethod() + .getName(); + + assertThat(info.className()).isEqualTo(className); + assertThat(info.methodName()).isEqualTo(methodName); + } +}