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);
+ }
+}