Skip to content

Commit

Permalink
GP-146 Upgrade HeterogeneousMaxHolder exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
tboychuk committed Jun 23, 2023
1 parent 1444c25 commit b9a7749
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
* key/value map, where the key is a type and the value is the maximum among all values of this type that were put.
* <p>
* It's based on the {@link Map} and provides an API that allows to put a value by type, and get a max value by type.
* <p>
* <p>
* <strong>TODO: to get the most out of your learning, <a href="https://www.bobocode.com/learn">visit our website</a></strong>
* <p>
*
* @author Taras Boychuk
*/
public class HeterogeneousMaxHolder {

Expand All @@ -17,7 +23,7 @@ public class HeterogeneousMaxHolder {
* If the current max value is less than a provided one, or if it's null, then a provided value gets stored and the old
* max is returned. Otherwise, nothing new is added, and the provided value is returned.
* <p>
* So technically, this method always stored the greater value and returns the smaller one.
* So technically, this method always stores the greater value and returns the smaller one.
*
* @param key a provided value type
* @param value a value to put
Expand All @@ -27,9 +33,9 @@ public class HeterogeneousMaxHolder {
// todo: implement a method according to javadoc

/**
* An overloaded method put implements the same logic using a custom comparator. A given comparator is wrapped with
* a null-safe comparator, considering null smaller than any non-null object.
*
* An overloaded method put implements the same logic using a custom comparator. A given comparator is wrapped with
* a null-safe comparator, considering null smaller than any non-null object.
* <p>
* All arguments must not be null.
*
* @param key a provided value type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.mockito.Mockito;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Comparator;
Expand All @@ -23,6 +24,7 @@
@TestMethodOrder(OrderAnnotation.class)
class HeterogeneousMaxHolderTest {
private HeterogeneousMaxHolder heterogeneousMaxHolder = new HeterogeneousMaxHolder();
private HeterogeneousMaxHolder heterogeneousMaxHolderMock = Mockito.spy(HeterogeneousMaxHolder.class);

@Test
@Order(1)
Expand Down Expand Up @@ -71,58 +73,53 @@ void putDeclaresOneTypeParam() {
var putMethod = getPutMethod();

var methodTypeParameters = putMethod.getTypeParameters();

assertThat(methodTypeParameters).hasSize(1);

var typeParam = putMethod.getTypeParameters()[0];
assertThat(typeParam.getName()).isEqualTo("T");
}

@Test
@Order(6)
@DisplayName("put method type parameter is called 'T'")
void putTypeParamIsCalledT() {
@DisplayName("put method accepts type-safe key")
void putMethodAcceptsTypeSafeKeyParameter() {
var putMethod = getPutMethod();

var typeParam = putMethod.getTypeParameters()[0];
var typeParam = (ParameterizedType) putMethod.getGenericParameterTypes()[0];
var typeArgument = typeParam.getActualTypeArguments()[0];

assertThat(typeParam.getName()).isEqualTo("T");
assertThat(typeParam.getRawType()).isEqualTo(Class.class);
assertThat(typeArgument.getTypeName()).isEqualTo("T");
}

@Test
@Order(7)
@DisplayName("type parameter 'T' is declared as Comparable")
void typeParamIsComparable() {
@DisplayName("put method accepts comparable value")
void putMethodAcceptsComparableValueParameter() {
var putMethod = getPutMethod();

var typeParam = putMethod.getTypeParameters()[0];
var bound = typeParam.getBounds()[0];
var boundType = (ParameterizedType) typeParam.getBounds()[0];

assertThat(bound.getTypeName()).isEqualTo(Comparable.class.getTypeName() + "<? super T>");
assertThat(boundType.getRawType()).isEqualTo(Comparable.class);
}

@Test
@Order(8)
@SneakyThrows
@DisplayName("put method accepts type (class) and value (object) parameters")
void putHasKeyValueParameters() {
HeterogeneousMaxHolder.class.getMethod("put", Class.class, Comparable.class);
}
@DisplayName("put method supports value that has comparable super class")
void putMethodAcceptsValueParameterWithComparableSuperClass() {
var putMethod = getPutMethod();

@Test
@Order(9)
@SneakyThrows
@DisplayName("put method params specify type arguments")
void putParametersSpecifyTypeArguments() {
var putMethod = HeterogeneousMaxHolder.class.getMethod("put", Class.class, Comparable.class);
var genericParamTypeNames = Arrays.stream(putMethod.getGenericParameterTypes())
.map(Type::getTypeName)
.toList();
var typeParam = putMethod.getTypeParameters()[0];
var boundType = (ParameterizedType) typeParam.getBounds()[0];
var typeArgument = boundType.getActualTypeArguments()[0].getTypeName();

assertThat(genericParamTypeNames)
.contains(Class.class.getTypeName() + "<T>")
.contains("T");
assertThat(boundType.getRawType()).isEqualTo(Comparable.class);
assertThat(typeArgument).isEqualTo("? super T");
}

@Test
@Order(10)
@Order(9)
@SneakyThrows
@DisplayName("put stores provided value when current max is null")
void putStoresValueWhenCurrentMaxIsNull() {
Expand All @@ -133,7 +130,7 @@ void putStoresValueWhenCurrentMaxIsNull() {
}

@Test
@Order(11)
@Order(10)
@SneakyThrows
@DisplayName("put returns null when current max is null")
void putReturnsNullWhenCurrentMaxIsNull() {
Expand All @@ -143,7 +140,7 @@ void putReturnsNullWhenCurrentMaxIsNull() {
}

@Test
@Order(12)
@Order(11)
@SneakyThrows
@DisplayName("put stores provided value when current max is smaller than it")
void putStoresValueWhenCurrentMaxIsSmaller() {
Expand All @@ -156,7 +153,7 @@ void putStoresValueWhenCurrentMaxIsSmaller() {
}

@Test
@Order(13)
@Order(12)
@SneakyThrows
@DisplayName("put returns old max value when the provided value is greater than it")
void putReturnsOldMaxValue() {
Expand All @@ -168,7 +165,7 @@ void putReturnsOldMaxValue() {
}

@Test
@Order(14)
@Order(13)
@SneakyThrows
@DisplayName("put ignores provided value when the current max is greater than it")
void putIgnoresNewValueWhenCurrentMaxIsGreater() {
Expand All @@ -181,7 +178,7 @@ void putIgnoresNewValueWhenCurrentMaxIsGreater() {
}

@Test
@Order(15)
@Order(14)
@SneakyThrows
@DisplayName("put returns provided value when the current max is greater than it")
void putReturnsProvidedValueWhenCurrentMaxIsGreater() {
Expand All @@ -193,45 +190,67 @@ void putReturnsProvidedValueWhenCurrentMaxIsGreater() {
}

@Test
@Order(16)
@Order(15)
@SneakyThrows
@DisplayName("put method is overloaded with additional Comparator param")
@DisplayName("put method is overloaded with additional Comparator parameter")
void putIsOverloadedWithAdditionalComparatorParam() {
var overloadedPutMethod = getOverloadedPutMethod();
var params = overloadedPutMethod.getParameters();
HeterogeneousMaxHolder.class.getMethod("put", Class.class, Object.class, Comparator.class);
}

@Test
@Order(16)
@DisplayName("Overloaded put method declares one type parameter T")
void overloadedPutDeclaresOneTypeParam() {
var putMethod = getOverloadedPutMethod();

assertThat(params[2].getType()).isEqualTo(Comparator.class);
var methodTypeParameters = putMethod.getTypeParameters();
assertThat(methodTypeParameters).hasSize(1);

var typeParam = putMethod.getTypeParameters()[0];
assertThat(typeParam.getName()).isEqualTo("T");
}

@Test
@Order(17)
@DisplayName("Overloaded put has simple type param 'T'")
void overloadedPutHasSimpleTypeParameterT() {
var overloadedPutMethod = getOverloadedPutMethod();
@DisplayName("Overloaded put method accepts type-safe key")
void overloadedPutMethodAcceptsTypeSafeKeyParameter() {
var putMethod = getOverloadedPutMethod();

var typeParam = (ParameterizedType) putMethod.getGenericParameterTypes()[0];
var typeArgument = typeParam.getActualTypeArguments()[0];

assertThat(overloadedPutMethod.getTypeParameters()).hasSize(1);
assertThat(overloadedPutMethod.getTypeParameters()[0].getTypeName()).isEqualTo("T");
assertThat(typeParam.getRawType()).isEqualTo(Class.class);
assertThat(typeArgument.getTypeName()).isEqualTo("T");
}

@Test
@Order(18)
@DisplayName("Overloaded put method accepts value of arbitrary type T")
void overloadedPutMethodAcceptsAnyValue() {
var putMethod = getOverloadedPutMethod();

var genericValueTypeParam = putMethod.getGenericParameterTypes()[1];
var actualValueTypeParm = putMethod.getParameterTypes()[1];

assertThat(genericValueTypeParam.getTypeName()).isEqualTo("T");
assertThat(actualValueTypeParm).isEqualTo(Object.class);
}

@Test
@Order(19)
@SneakyThrows
@DisplayName("Overloaded put method params specify type arguments")
void overloadedPutParametersSpecifyTypeArguments() {
@DisplayName("Overloaded put method supports comparator of a super type")
void overloadedPutAcceptsComparatorOfSuperTypes() {
var putMethod = HeterogeneousMaxHolder.class.getMethod("put", Class.class, Object.class, Comparator.class);
var genericParamTypeNames = Arrays.stream(putMethod.getGenericParameterTypes())
.map(Type::getTypeName)
.toList();

assertThat(genericParamTypeNames)
.contains(Class.class.getTypeName() + "<T>")
.contains("T")
.contains(Comparator.class.getTypeName() + "<? super T>");
var comparatorParam = (ParameterizedType) putMethod.getGenericParameterTypes()[2];
var comparatorTypeArgument = comparatorParam.getActualTypeArguments()[0];

assertThat(comparatorTypeArgument.getTypeName()).isEqualTo("? super T");
}

@Test
@Order(19)
@Order(20)
@SneakyThrows
@DisplayName("Overloaded put stores provided value when current max is null")
void overloadedPutStoresValueWhenCurrentMaxIsNull() {
Expand All @@ -243,7 +262,7 @@ void overloadedPutStoresValueWhenCurrentMaxIsNull() {
}

@Test
@Order(20)
@Order(21)
@SneakyThrows
@DisplayName("Overloaded put returns null when current max is null")
void overloadedPutReturnsNullWhenCurrentMaxIsNull() {
Expand All @@ -253,7 +272,7 @@ void overloadedPutReturnsNullWhenCurrentMaxIsNull() {
}

@Test
@Order(21)
@Order(22)
@SneakyThrows
@DisplayName("Overloaded put stores provided value when current max is smaller than it")
void overloadedPutStoresValueWhenCurrentMaxIsSmaller() {
Expand All @@ -269,7 +288,7 @@ void overloadedPutStoresValueWhenCurrentMaxIsSmaller() {
}

@Test
@Order(22)
@Order(23)
@SneakyThrows
@DisplayName("Overloaded put returns old max value when the provided value is greater than it")
void overloadedPutReturnsOldMaxValue() {
Expand All @@ -284,7 +303,7 @@ void overloadedPutReturnsOldMaxValue() {
}

@Test
@Order(23)
@Order(24)
@SneakyThrows
@DisplayName("Overloaded put ignores provided value when the current max is greater than it")
void overloadedPutIgnoresNewValueWhenCurrentMaxIsGreater() {
Expand All @@ -300,7 +319,7 @@ void overloadedPutIgnoresNewValueWhenCurrentMaxIsGreater() {
}

@Test
@Order(24)
@Order(25)
@SneakyThrows
@DisplayName("Overloaded put returns provided value when the current max is greater")
void overloadedPutReturnsProvidedValueWhenCurrentMaxIsGreater() {
Expand All @@ -315,7 +334,7 @@ void overloadedPutReturnsProvidedValueWhenCurrentMaxIsGreater() {
}

@Test
@Order(25)
@Order(26)
@DisplayName("getMax method exists")
void getMaxExists() {
var getMaxMethodExists = Arrays.stream(HeterogeneousMaxHolder.class.getDeclaredMethods())
Expand All @@ -325,7 +344,7 @@ void getMaxExists() {
}

@Test
@Order(26)
@Order(27)
@DisplayName("getMax declares one simple type param 'T'")
void getMaxDeclaresOneTypeParam() {
var getMaxMethod = getGetMaxMethod();
Expand All @@ -337,7 +356,7 @@ void getMaxDeclaresOneTypeParam() {
}

@Test
@Order(27)
@Order(28)
@DisplayName("getMax has one parameter")
void getMaxHasOneParameter() {
var getMaxMethod = getGetMaxMethod();
Expand All @@ -349,7 +368,7 @@ void getMaxHasOneParameter() {
}

@Test
@Order(28)
@Order(29)
@DisplayName("getMax param specifies type arguments")
void getMaxParamSpecifyTypeArguments() {
var getMaxMethod = getGetMaxMethod();
Expand All @@ -360,7 +379,7 @@ void getMaxParamSpecifyTypeArguments() {
}

@Test
@Order(29)
@Order(30)
@DisplayName("getMax returns value when it exists")
void getMaxReturnsValueWhenItExists() {
givenMaxHolderWithData(String.class, "I am maximum");
Expand All @@ -371,7 +390,7 @@ void getMaxReturnsValueWhenItExists() {
}

@Test
@Order(30)
@Order(31)
@DisplayName("getMax returns value when it exists")
void getMaxReturnsNullWhenNoValueByGivenTypeExists() {
var returnedValue = callGetMax(String.class);
Expand All @@ -380,7 +399,7 @@ void getMaxReturnsNullWhenNoValueByGivenTypeExists() {
}

@Test
@Order(31)
@Order(32)
@DisplayName("HeterogeneousMaxHolder keeps track of value one per each type")
void maxHolderKeepsTrackOfMultipleValuesPerType() {
callPut(String.class, "A");
Expand Down

0 comments on commit b9a7749

Please sign in to comment.