Skip to content

added tests for size change HashTableTest, #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.bobocode.cs;

import lombok.ToString;

import static java.util.Objects.requireNonNull;

import lombok.ToString;

/**
* {@link HashTable} is a simple Hashtable-based implementation of {@link Map} interface with some additional methods.
* It is based on the array of {@link Node} objects. Both {@link HashTable} and {@link Node} have two type parameters:
@@ -198,12 +198,14 @@ public V remove(K key) {
if (current.key.equals(key)) {
var value = current.value;
table[index] = current.next;
size--;
return value;
}
while (current.next != null) {
if (current.next.key.equals(key)) {
var value = current.next.value;
current.next = current.next.next;
size--;
return value;
}
current = current.next;
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.bobocode.cs;

import lombok.SneakyThrows;
import org.junit.jupiter.api.ClassOrderer.OrderAnnotation;
import org.junit.jupiter.api.*;
import org.mockito.Mockito;
import static java.lang.reflect.Modifier.isStatic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.lang.reflect.Field;
import java.util.Arrays;
@@ -12,12 +15,15 @@
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.lang.reflect.Modifier.isStatic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import lombok.SneakyThrows;
import org.junit.jupiter.api.ClassOrderer.OrderAnnotation;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestClassOrder;
import org.junit.jupiter.api.TestMethodOrder;

/**
* A Reflection-based step by step test for a {@link HashTable} class. PLEASE NOTE that Reflection API should not be used
@@ -28,6 +34,7 @@
@TestClassOrder(OrderAnnotation.class)
@DisplayName("HashTable Test")
class HashTableTest {

private HashTable<String, Integer> hashTable = new HashTable<>();

@Nested
@@ -126,6 +133,7 @@ void nodeConstructorAcceptKeyValue() {
@DisplayName("2. HashTable fields Test")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class HashTableFieldsTest {

@Test
@Order(1)
@DisplayName("HastTable has a field 'table' which is an array of nodes")
@@ -155,6 +163,7 @@ void sizeFieldExists() {
@DisplayName("3. HashTable constructors Test")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class HashTableConstructorsTest {

@Test
@Order(1)
@SneakyThrows
@@ -198,6 +207,7 @@ void constructorWithTableCapacityWhenArgumentIsNegative() {
@DisplayName("4. Hash Function Test")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class HashFunctionTest {

@Test
@Order(1)
@DisplayName("calculateIndex returns the same value for the same key")
@@ -221,8 +231,8 @@ void calculateIndexReturnDifferentValuesWheKeysAreDifferent() {

assertThat(indexSet)
.hasSizeGreaterThan(1);
}
}

@Test
@Order(3)
@DisplayName("calculateIndex returns values in array bounds")
@@ -235,7 +245,7 @@ void calculateIndexReturnIndexInArrayBounds() {
var indexes = keys.stream()
.map(key -> HashTable.calculateIndex(key, arrayCapacity))
.toList();

assertThat(indexes)
.isNotEmpty()
.allMatch(i -> i >= 0 && i < arrayCapacity);
@@ -262,19 +272,20 @@ class HashTableMethodsTest {
@Test
@SneakyThrows
@Order(1)
@DisplayName("put creates new entry and returns null when the table is empty")
@DisplayName("put creates new entry and returns null when the table is empty, should increase the table size")
void putWhenTableIsEmpty() {
var previousValue = hashTable.put("madmax", 833);

var keyValueExists = checkKeyValueExists("madmax", 833);

assertNull(previousValue);
assertTrue(keyValueExists);
assertEquals(1, getSize());
}

@Test
@Order(2)
@DisplayName("put elements adds entry to to the same bucket when the hash code is the same")
@DisplayName("put elements adds entry to the same bucket and increases table size when the hash code is the same")
@SneakyThrows
void putTwoElementsWithTheSameHashCode() {
var table = getInternalTable(hashTable);
@@ -290,11 +301,13 @@ void putTwoElementsWithTheSameHashCode() {
assertTrue(containsKeyValueA);
assertTrue(containsKeyValueB);
assertThat(bucketIndexA).isEqualTo(bucketIndexB);
assertEquals(2, getSize());
}

@Test
@Order(3)
@DisplayName("put element updates the value and returns the previous one when key is the same")
@DisplayName(
"put element updates the value and returns the previous one when key is the same, should not increase table size")
void putElementWithTheSameKey() {
hashTable.put("madmax", 833);
System.out.println(hashTable);
@@ -305,6 +318,7 @@ void putElementWithTheSameKey() {

assertThat(previousValue).isEqualTo(833);
assertTrue(containsNewValueByKey);
assertEquals(1, getSize());
}

@Test
@@ -430,14 +444,15 @@ void isEmptyWhenThereIsNoElements() {

@Test
@Order(13)
@DisplayName("remove deletes the entry and returns a value")
@DisplayName("remove deletes the entry, decreases table size and returns a value")
void remove() {
addToTable("madmax", 833);

setSize(1);
var result = hashTable.remove("madmax");

assertThat(result).isEqualTo(833);
assertFalse(checkKeyValueExists("madmaxx", 833));
assertEquals(0, getSize());
}

@Test
@@ -451,34 +466,40 @@ void removeWhenKeyDoesNotExists() {

@Test
@Order(15)
@DisplayName("remove deletes the element when it's in the middle of the list")
@DisplayName("remove deletes the element when it's in the middle of the list and decreases the size of table")
void removeFromTheMiddleOfTheList() {
addToTable("AaAa", 843);
addToTable("BBBB", 434);
addToTable("AaBB", 587);

var size = 3;
setSize(size);
var removedValue = hashTable.remove("BBBB");

assertTrue(checkKeyValueExists("AaAa", 843));
assertFalse(checkKeyExists("BBBB"));
assertTrue(checkKeyValueExists("AaBB", 587));
assertThat(removedValue).isEqualTo(434);
}

assertEquals(size - 1, getSize());
}

@Test
@Order(16)
@DisplayName("remove deletes the element when it's in the end of the list")
@DisplayName("remove deletes the element when it's in the end of the list and decreases the size of table")
void removeFromTheEndOfTheList() {
addToTable("AaAa", 843);
addToTable("BBBB", 434);
addToTable("AaBB", 587);
var size = 3;
setSize(size);

var removedValue = hashTable.remove("AaBB");

assertTrue(checkKeyValueExists("AaAa", 843));
assertTrue(checkKeyValueExists("BBBB", 434));
assertFalse(checkKeyExists("AaBB"));
assertThat(removedValue).isEqualTo(587);
assertEquals(2, getSize());
}
}

@@ -585,6 +606,13 @@ private void setSize(int size) {
sizeField.set(hashTable, size);
}

@SneakyThrows
private int getSize() {
var sizeField = HashTable.class.getDeclaredField("size");
sizeField.setAccessible(true);
return sizeField.getInt(hashTable);
}

private String tableToString(Object[] table) {
StringBuilder result = new StringBuilder();
var n = table.length;