Adds saveObjectAsync with a CompletableFuture return for databases (#1308)

Deprecates the saveObject() method
This commit is contained in:
tastybento 2020-04-25 16:00:49 -07:00 committed by GitHub
parent 97341ce657
commit fe58159db3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 170 additions and 102 deletions

View File

@ -4,6 +4,7 @@ import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.bukkit.Bukkit;
@ -143,7 +144,7 @@ public abstract class AbstractDatabaseHandler<T> {
*
* @param instance that should be inserted into the database
*/
public abstract void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException ;
public abstract CompletableFuture<Boolean> saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException ;
/**
* Deletes the object with the unique id from the database. If the object does not exist, it will fail silently.

View File

@ -4,6 +4,7 @@ import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
import org.eclipse.jdt.annotation.NonNull;
@ -82,18 +83,32 @@ public class Database<T> {
}
/**
* Save config object. Saving may be done async.
* Save object async. Saving may be done sync, depending on the underlying database.
* @param instance to save
* @return true if no immediate errors. If async, errors may occur later.
* @since 1.13.0
*/
public boolean saveObject(T instance) {
public CompletableFuture<Boolean> saveObjectAsync(T instance) {
try {
handler.saveObject(instance);
return handler.saveObject(instance);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException
| IntrospectionException e) {
logger.severe(() -> "Could not save object to database! Error: " + e.getMessage());
return false;
return new CompletableFuture<>();
}
}
/**
* Save object. Saving may be done async or sync, depending on the underlying database.
* @param instance to save
* @return true - always.
* @deprecated As of 1.13.0. Use {@link #saveObjectAsync(Object)}.
*/
@Deprecated
public boolean saveObject(T instance) {
saveObjectAsync(instance).thenAccept(r -> {
if (!r) logger.severe(() -> "Could not save object to database!");
});
return true;
}
@ -137,4 +152,4 @@ public class Database<T> {
}
}

View File

@ -15,6 +15,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.annotation.NonNull;
@ -97,15 +98,18 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
}
@Override
public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
public CompletableFuture<Boolean> saveObject(T instance) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
// Null check
if (instance == null) {
plugin.logError("JSON database request to store a null. ");
return;
completableFuture.complete(false);
return completableFuture;
}
if (!(instance instanceof DataObject)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
return;
completableFuture.complete(false);
return completableFuture;
}
String path = DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName();
@ -123,14 +127,15 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
String toStore = getGson().toJson(instance);
if (plugin.isEnabled()) {
// Async
processQueue.add(() -> store(toStore, file, tableFolder, fileName));
processQueue.add(() -> store(completableFuture, toStore, file, tableFolder, fileName));
} else {
// Sync
store(toStore, file, tableFolder, fileName);
store(completableFuture, toStore, file, tableFolder, fileName);
}
return completableFuture;
}
private void store(String toStore, File file, File tableFolder, String fileName) {
private void store(CompletableFuture<Boolean> completableFuture, String toStore, File file, File tableFolder, String fileName) {
try (FileWriter fileWriter = new FileWriter(file)) {
File tmpFile = new File(tableFolder, fileName + ".bak");
if (file.exists()) {
@ -139,8 +144,10 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
}
fileWriter.write(toStore);
Files.deleteIfExists(tmpFile.toPath());
completableFuture.complete(true);
} catch (IOException e) {
plugin.logError("Could not save JSON file: " + tableFolder.getName() + " " + fileName + " " + e.getMessage());
completableFuture.complete(false);
}
}

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.database.mongodb;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.bson.Document;
import org.bson.conversions.Bson;
@ -106,15 +107,18 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
}
@Override
public void saveObject(T instance) {
public CompletableFuture<Boolean> saveObject(T instance) {
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
// Null check
if (instance == null) {
plugin.logError("MongoDB database request to store a null. ");
return;
completableFuture.complete(false);
return completableFuture;
}
if (!(instance instanceof DataObject)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
return;
completableFuture.complete(false);
return completableFuture;
}
DataObject dataObj = (DataObject)instance;
try {
@ -129,10 +133,12 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
// Set the options to upsert (update or insert if doc is not there)
FindOneAndReplaceOptions options = new FindOneAndReplaceOptions().upsert(true);
// Do the deed
collection.findOneAndReplace(filter, document, options);
completableFuture.complete(collection.findOneAndReplace(filter, document, options) != null);
} catch (Exception e) {
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
completableFuture.complete(false);
}
return completableFuture;
}
@Override

View File

@ -9,6 +9,7 @@ import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.eclipse.jdt.annotation.NonNull;
@ -144,29 +145,34 @@ public class SQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
}
@Override
public void saveObject(T instance) {
public CompletableFuture<Boolean> saveObject(T instance) {
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
// Null check
if (instance == null) {
plugin.logError("SQL database request to store a null. ");
return;
completableFuture.complete(false);
return completableFuture;
}
if (!(instance instanceof DataObject)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
return;
completableFuture.complete(false);
return completableFuture;
}
// This has to be on the main thread to avoid concurrent modification errors
String toStore = getGson().toJson(instance);
// Async
processQueue.add(() -> store(instance.getClass().getName(), toStore, sqlConfig.getSaveObjectSQL()));
processQueue.add(() -> store(completableFuture, instance.getClass().getName(), toStore, sqlConfig.getSaveObjectSQL()));
return completableFuture;
}
private void store(String name, String toStore, String sb) {
private void store(CompletableFuture<Boolean> completableFuture, String name, String toStore, String sb) {
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
preparedStatement.setString(1, toStore);
preparedStatement.setString(2, toStore);
preparedStatement.execute();
completableFuture.complete(preparedStatement.execute());
} catch (SQLException e) {
plugin.logError("Could not save object " + name + " " + e.getMessage());
completableFuture.complete(false);
}
}

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.database.sql.postgresql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.CompletableFuture;
import com.google.gson.Gson;
@ -51,15 +52,18 @@ public class PostgreSQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
* @see world.bentobox.bentobox.database.sql.SQLDatabaseHandler#saveObject(java.lang.Object)
*/
@Override
public void saveObject(T instance) {
public CompletableFuture<Boolean> saveObject(T instance) {
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
// Null check
if (instance == null) {
plugin.logError("Postgres database request to store a null. ");
return;
plugin.logError("PostgreSQL database request to store a null. ");
completableFuture.complete(false);
return completableFuture;
}
if (!(instance instanceof DataObject)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
return;
completableFuture.complete(false);
return completableFuture;
}
Gson gson = getGson();
String toStore = gson.toJson(instance);
@ -69,10 +73,12 @@ public class PostgreSQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
preparedStatement.setString(1, uniqueId); // INSERT
preparedStatement.setString(2, toStore); // INSERT
preparedStatement.setString(3, toStore); // ON CONFLICT
preparedStatement.execute();
completableFuture.complete(preparedStatement.execute());
} catch (SQLException e) {
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
completableFuture.complete(false);
}
});
return completableFuture;
}
}

View File

@ -3,6 +3,7 @@ package world.bentobox.bentobox.database.sql.sqlite;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.annotation.NonNull;
@ -39,15 +40,18 @@ public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
}
@Override
public void saveObject(T instance) {
public CompletableFuture<Boolean> saveObject(T instance) {
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
// Null check
if (instance == null) {
plugin.logError("MySQL database request to store a null. ");
return;
plugin.logError("SQLite database request to store a null. ");
completableFuture.complete(false);
return completableFuture;
}
if (!(instance instanceof DataObject)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
return;
completableFuture.complete(false);
return completableFuture;
}
Gson gson = getGson();
String toStore = gson.toJson(instance);
@ -56,11 +60,13 @@ public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
preparedStatement.setString(1, toStore);
preparedStatement.setString(2, ((DataObject)instance).getUniqueId());
preparedStatement.setString(3, toStore);
preparedStatement.execute();
completableFuture.complete(preparedStatement.execute());
} catch (SQLException e) {
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
completableFuture.complete(false);
}
});
return completableFuture;
}
@Override

View File

@ -3,6 +3,7 @@ package world.bentobox.bentobox.database.transition;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.annotation.Nullable;
@ -83,9 +84,9 @@ public class TransitionDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#saveObject(java.lang.Object)
*/
@Override
public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
public CompletableFuture<Boolean> saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
// Save only in the destination database
toHandler.saveObject(instance);
return toHandler.saveObject(instance);
}
/* (non-Javadoc)

View File

@ -116,7 +116,7 @@ public class YamlDatabaseConnector implements DatabaseConnector {
}
}
public void saveYamlFile(String data, String tableName, String fileName, Map<String, String> commentMap) {
boolean saveYamlFile(String data, String tableName, String fileName, Map<String, String> commentMap) {
String name = fileName.endsWith(YML) ? fileName : fileName + YML;
File tableFolder = new File(plugin.getDataFolder(), tableName);
File file = new File(tableFolder, name);
@ -127,11 +127,12 @@ public class YamlDatabaseConnector implements DatabaseConnector {
writer.write(data);
} catch (IOException e) {
plugin.logError("Could not save yml file: " + tableName + " " + fileName + " " + e.getMessage());
return;
return false;
}
if (commentMap != null && !commentMap.isEmpty()) {
commentFile(new File(tableFolder, name), commentMap);
}
return true;
}
/**

View File

@ -22,6 +22,7 @@ import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -325,19 +326,22 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
* Inserts T into the corresponding database-table
*
* @param instance that should be inserted into the database
* @return
*/
@SuppressWarnings("unchecked")
@Override
public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
public CompletableFuture<Boolean> saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
// Null check
if (instance == null) {
plugin.logError("YAML database request to store a null.");
return;
completableFuture.complete(false);
return completableFuture;
}
// DataObject check
if (!(instance instanceof DataObject)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
return;
completableFuture.complete(false);
return completableFuture;
}
// This is the Yaml Configuration that will be used and saved at the end
YamlConfiguration config = new YamlConfiguration();
@ -414,16 +418,19 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
}
// Save
save(filename, config.saveToString(), path, yamlComments);
save(completableFuture, filename, config.saveToString(), path, yamlComments);
return completableFuture;
}
private void save(String name, String data, String path, Map<String, String> yamlComments) {
private void save(CompletableFuture<Boolean> completableFuture, String name, String data, String path, Map<String, String> yamlComments) {
if (plugin.isEnabled()) {
// Async
processQueue.add(() -> ((YamlDatabaseConnector)databaseConnector).saveYamlFile(data, path, name, yamlComments));
processQueue.add(() -> completableFuture.complete(
((YamlDatabaseConnector)databaseConnector).saveYamlFile(data, path, name, yamlComments)));
} else {
// Sync for shutdown
((YamlDatabaseConnector)databaseConnector).saveYamlFile(data, path, name, yamlComments);
completableFuture.complete(
((YamlDatabaseConnector)databaseConnector).saveYamlFile(data, path, name, yamlComments));
}
}

View File

@ -68,7 +68,7 @@ public class IslandDeletionManager implements Listener {
// Store location
inDeletion.add(e.getDeletedIslandInfo().getLocation());
// Save to database
handler.saveObject(e.getDeletedIslandInfo());
handler.saveObjectAsync(e.getDeletedIslandInfo());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)

View File

@ -340,7 +340,7 @@ public class IslandsManager {
// Set the delete flag which will prevent it from being loaded even if database deletion fails
island.setDeleted(true);
// Save the island
handler.saveObject(island);
handler.saveObjectAsync(island);
// Delete the island
handler.deleteObject(island);
// Remove players from island
@ -900,7 +900,7 @@ public class IslandsManager {
plugin.logError(toQuarantine.size() + " islands could not be loaded successfully; moving to trash bin.");
plugin.logError(unowned + " are unowned, " + owned + " are owned.");
toQuarantine.forEach(handler::saveObject);
toQuarantine.forEach(handler::saveObjectAsync);
// Check if there are any islands with duplicate islands
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
Set<UUID> duplicatedUUIDRemovedSet = new HashSet<>();
@ -1004,7 +1004,7 @@ public class IslandsManager {
public void removePlayer(World world, UUID uuid) {
Island island = islandCache.removePlayer(world, uuid);
if (island != null) {
handler.saveObject(island);
handler.saveObjectAsync(island);
}
}
@ -1037,7 +1037,7 @@ public class IslandsManager {
Collection<Island> collection = islandCache.getIslands();
for(Island island : collection){
try {
handler.saveObject(island);
handler.saveObjectAsync(island);
} catch (Exception e) {
plugin.logError("Could not save island to database when running sync! " + e.getMessage());
}
@ -1072,7 +1072,7 @@ public class IslandsManager {
teamIsland.addMember(playerUUID);
islandCache.addPlayer(playerUUID, teamIsland);
// Save the island
handler.saveObject(teamIsland);
handler.saveObjectAsync(teamIsland);
}
public void setLast(Location last) {
@ -1198,7 +1198,7 @@ public class IslandsManager {
* @param island - island
*/
public void save(Island island) {
handler.saveObject(island);
handler.saveObjectAsync(island);
}
/**
@ -1290,10 +1290,9 @@ public class IslandsManager {
// Put old island into trash
quarantineCache.computeIfAbsent(target, k -> new ArrayList<>()).add(oldIsland);
// Save old island
if (!handler.saveObject(oldIsland)) {
plugin.logError("Could not save trashed island in database");
return false;
}
handler.saveObjectAsync(oldIsland).thenAccept(result -> {
if (!result) plugin.logError("Could not save trashed island in database");
});
}
// Restore island from trash
island.setDoNotLoad(false);
@ -1303,10 +1302,9 @@ public class IslandsManager {
return false;
}
// Save new island
if (!handler.saveObject(island)) {
plugin.logError("Could not save recovered island to database");
return false;
}
handler.saveObjectAsync(island).thenAccept(result -> {
if (!result) plugin.logError("Could not save recovered island to database");
});
return true;
}

View File

@ -72,7 +72,7 @@ public class PlayersManager {
* Save all players
*/
public void saveAll(){
Collections.unmodifiableCollection(playerCache.values()).forEach(handler::saveObject);
Collections.unmodifiableCollection(playerCache.values()).forEach(handler::saveObjectAsync);
}
/**
@ -281,7 +281,7 @@ public class PlayersManager {
playerCache.get(user.getUniqueId()).setPlayerName(user.getName());
Names newName = new Names(user.getName(), user.getUniqueId());
// Add to names database
names.saveObject(newName);
names.saveObjectAsync(newName);
}
/**
@ -425,7 +425,7 @@ public class PlayersManager {
*/
public void save(UUID playerUUID) {
if (playerCache.containsKey(playerUUID)) {
handler.saveObject(playerCache.get(playerUUID));
handler.saveObjectAsync(playerCache.get(playerUUID));
}
}

View File

@ -3,7 +3,13 @@ package world.bentobox.bentobox.database;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.framework;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.beans.IntrospectionException;
@ -11,6 +17,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.logging.Logger;
@ -21,7 +28,6 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@ -66,17 +72,22 @@ public class DatabaseTest {
PowerMockito.mockStatic(DatabaseSetup.class);
dbSetup = mock(DatabaseSetup.class);
handler = mock(AbstractDatabaseHandler.class);
when(dbSetup.getHandler(Mockito.any())).thenReturn(handler);
when(dbSetup.getHandler(any())).thenReturn(handler);
// Set the internal state of the static database variable to null for each test
Whitebox.setInternalState(Database.class, "databaseSetup", dbSetup);
when(handler.loadObject(Mockito.anyString())).thenReturn(island);
when(handler.loadObject(anyString())).thenReturn(island);
objectList = new ArrayList<>();
objectList.add(mock(Island.class));
objectList.add(mock(Island.class));
objectList.add(mock(Island.class));
objectList.add(mock(Island.class));
when(handler.loadObjects()).thenReturn(objectList);
CompletableFuture<Boolean> completetableFuture = new CompletableFuture<>();
// Complete immediately in a positive way
completetableFuture.complete(true);
when(handler.saveObject(any())).thenReturn(completetableFuture);
}
/**
@ -85,7 +96,7 @@ public class DatabaseTest {
@After
public void tearDown() throws Exception {
dbSetup = null;
Mockito.framework().clearInlineMocks();
framework().clearInlineMocks();
}
/**
@ -94,7 +105,7 @@ public class DatabaseTest {
*/
private void checkSevereLog(String stringToCheck) {
// This magic obtains the lambda from an argument
Mockito.verify(logger).severe(registerMessageLambdaCaptor.capture());
verify(logger).severe(registerMessageLambdaCaptor.capture());
Supplier<String> lambda = registerMessageLambdaCaptor.getValue();
assertEquals(stringToCheck,lambda.get());
}
@ -104,8 +115,8 @@ public class DatabaseTest {
@Test
public void testDatabaseBentoBoxClassOfT() {
new Database<Island>(plugin, Island.class);
Mockito.verify(plugin).getLogger();
Mockito.verify(dbSetup).getHandler(Mockito.any());
verify(plugin).getLogger();
verify(dbSetup).getHandler(any());
}
/**
@ -114,8 +125,8 @@ public class DatabaseTest {
@Test
public void testDatabaseAddonClassOfT() {
new Database<Island>(addon, Island.class);
Mockito.verify(addon).getLogger();
Mockito.verify(dbSetup).getHandler(Mockito.any());
verify(addon).getLogger();
verify(dbSetup).getHandler(any());
}
/**
@ -131,7 +142,7 @@ public class DatabaseTest {
public void testLoadObjects() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException, IntrospectionException {
Database<Island> db = new Database<Island>(plugin, Island.class);
assertEquals(objectList, db.loadObjects());
Mockito.verify(handler).loadObjects();
verify(handler).loadObjects();
}
/**
@ -148,7 +159,7 @@ public class DatabaseTest {
when(handler.loadObjects()).thenThrow(new IllegalAccessException("No bad dog! No biscuit!"));
Database<Island> db = new Database<Island>(plugin, Island.class);
db.loadObjects();
Mockito.verify(handler).loadObjects();
verify(handler).loadObjects();
checkSevereLog("Could not load objects from database! Error: No bad dog! No biscuit!");
}
@ -167,7 +178,7 @@ public class DatabaseTest {
Database<Island> db = new Database<Island>(plugin, Island.class);
String uniqueId = UUID.randomUUID().toString();
assertEquals(island, db.loadObject(uniqueId));
Mockito.verify(handler).loadObject(Mockito.eq(uniqueId));
verify(handler).loadObject(eq(uniqueId));
}
/**
@ -179,8 +190,9 @@ public class DatabaseTest {
@Test
public void testSaveObject() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
Database<Island> db = new Database<Island>(plugin, Island.class);
assertTrue(db.saveObject(island));
Mockito.verify(handler).saveObject(Mockito.eq(island));
db.saveObjectAsync(island);
verify(handler).saveObject(eq(island));
}
/**
@ -190,10 +202,10 @@ public class DatabaseTest {
* @throws IllegalAccessException
*/
@Test
public void testSaveObjectFail() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
Mockito.doThrow(new IntrospectionException("No means no!")).when(handler).saveObject(Mockito.any(Island.class));
public void testSaveObjectException() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
doThrow(new IntrospectionException("No means no!")).when(handler).saveObject(any(Island.class));
Database<Island> db = new Database<Island>(plugin, Island.class);
assertFalse(db.saveObject(island));
db.saveObjectAsync(island);
checkSevereLog("Could not save object to database! Error: No means no!");
}
@ -202,8 +214,8 @@ public class DatabaseTest {
*/
@Test
public void testObjectExists() {
when(handler.objectExists(Mockito.eq("test"))).thenReturn(false);
when(handler.objectExists(Mockito.eq("exists"))).thenReturn(true);
when(handler.objectExists(eq("test"))).thenReturn(false);
when(handler.objectExists(eq("exists"))).thenReturn(true);
Database<Island> db = new Database<Island>(plugin, Island.class);
assertFalse(db.objectExists("test"));
assertTrue(db.objectExists("exists"));
@ -216,7 +228,7 @@ public class DatabaseTest {
public void testDeleteID() {
Database<Island> db = new Database<Island>(plugin, Island.class);
db.deleteID("test");
Mockito.verify(handler).deleteID(Mockito.eq("test"));
verify(handler).deleteID(eq("test"));
}
/**
@ -229,7 +241,7 @@ public class DatabaseTest {
public void testDeleteObject() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
Database<Island> db = new Database<Island>(plugin, Island.class);
db.deleteObject(island);
Mockito.verify(handler).deleteObject(Mockito.eq(island));
verify(handler).deleteObject(eq(island));
}
/**
@ -240,7 +252,7 @@ public class DatabaseTest {
*/
@Test
public void testDeleteObjectFail() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
Mockito.doThrow(new IllegalArgumentException("Wot?!")).when(handler).deleteObject(Mockito.any());
doThrow(new IllegalArgumentException("Wot?!")).when(handler).deleteObject(any());
Database<Island> db = new Database<Island>(plugin, Island.class);
db.deleteObject(island);
checkSevereLog("Could not delete object! Error: Wot?!");
@ -254,7 +266,7 @@ public class DatabaseTest {
public void testClose() {
Database<Island> db = new Database<Island>(plugin, Island.class);
db.close();
Mockito.verify(handler).close();
verify(handler).close();
}
}

View File

@ -1,12 +1,15 @@
/**
*
*/
package world.bentobox.bentobox.database.yaml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.framework;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.beans.IntrospectionException;
@ -35,7 +38,6 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@ -91,11 +93,11 @@ public class YamlDatabaseHandlerTest {
PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getScheduler()).thenReturn(scheduler);
when(scheduler.runTaskAsynchronously(Mockito.any(), Mockito.any(Runnable.class))).thenReturn(task);
when(scheduler.runTaskAsynchronously(any(), any(Runnable.class))).thenReturn(task);
Server server = mock(Server.class);
World world = mock(World.class);
when(world.getName()).thenReturn("cleanroom");
when(server.getWorld(Mockito.anyString())).thenReturn(world);
when(server.getWorld(anyString())).thenReturn(world);
when(Bukkit.getServer()).thenReturn(server);
// A YAML file representing island
@ -105,11 +107,11 @@ public class YamlDatabaseHandlerTest {
config.loadFromString(getYaml(uuid));
YamlConfiguration config2 = new YamlConfiguration();
config2.loadFromString(getYaml2(uuid2));
when(dbConnector.loadYamlFile(Mockito.anyString(), Mockito.anyString())).thenReturn(config, config2);
when(dbConnector.loadYamlFile(anyString(), anyString())).thenReturn(config, config2);
// Flags Manager
FlagsManager fm = mock(FlagsManager.class);
when(fm.getFlag(Mockito.anyString())).thenReturn(Optional.empty());
when(fm.getFlag(anyString())).thenReturn(Optional.empty());
when(plugin.getFlagsManager()).thenReturn(fm);
// Island
@ -138,7 +140,7 @@ public class YamlDatabaseHandlerTest {
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
Mockito.framework().clearInlineMocks();
framework().clearInlineMocks();
}
/**
@ -189,7 +191,7 @@ public class YamlDatabaseHandlerTest {
Location center = mock(Location.class);
is.setCenter(center);
handler.saveObject(is);
Mockito.verify(dbConnector).saveYamlFile(Mockito.anyString(), Mockito.eq("database/Island"), Mockito.eq("unique"), Mockito.isA(Map.class));
verify(dbConnector).saveYamlFile(anyString(), eq("database/Island"), eq("unique"), isA(Map.class));
}
/**
@ -201,7 +203,7 @@ public class YamlDatabaseHandlerTest {
@Test
public void testSaveObjectNull() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
handler.saveObject(null);
Mockito.verify(plugin).logError("YAML database request to store a null.");
verify(plugin).logError("YAML database request to store a null.");
}
/**
@ -215,7 +217,7 @@ public class YamlDatabaseHandlerTest {
YamlDatabaseHandler<String> h = new YamlDatabaseHandler<String>(plugin, String.class, dbConnector);
String test = "";
h.saveObject(test);
Mockito.verify(plugin).logError("This class is not a DataObject: java.lang.String");
verify(plugin).logError("This class is not a DataObject: java.lang.String");
}
/**
@ -238,7 +240,7 @@ public class YamlDatabaseHandlerTest {
@Test
public void testDeleteObjectNull() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
handler.deleteObject(null);
Mockito.verify(plugin).logError("YAML database request to delete a null.");
verify(plugin).logError("YAML database request to delete a null.");
}
/**
@ -252,7 +254,7 @@ public class YamlDatabaseHandlerTest {
YamlDatabaseHandler<String> h = new YamlDatabaseHandler<String>(plugin, String.class, dbConnector);
String test = "";
h.deleteObject(test);
Mockito.verify(plugin).logError("This class is not a DataObject: java.lang.String");
verify(plugin).logError("This class is not a DataObject: java.lang.String");
}
@ -261,7 +263,7 @@ public class YamlDatabaseHandlerTest {
*/
@Test
public void testObjectExists() {
when(dbConnector.uniqueIdExists(Mockito.eq(Island.class.getSimpleName()), Mockito.eq(uuid.toString()))).thenReturn(true);
when(dbConnector.uniqueIdExists(eq(Island.class.getSimpleName()), eq(uuid.toString()))).thenReturn(true);
assertTrue(handler.objectExists(uuid.toString()));
assertFalse(handler.objectExists("nope"));
}
@ -301,12 +303,12 @@ public class YamlDatabaseHandlerTest {
*/
@Test
public void testYamlDatabaseHandler() {
Mockito.verify(scheduler).runTaskAsynchronously(Mockito.eq(plugin), registerLambdaCaptor.capture());
verify(scheduler).runTaskAsynchronously(eq(plugin), registerLambdaCaptor.capture());
Runnable lamda = registerLambdaCaptor.getValue();
// Cannot run with true otherwise it'll infinite loop
when(plugin.isShutdown()).thenReturn(true);
lamda.run();
Mockito.verify(task).cancel();
verify(task).cancel();
}