Made MySQL tests 3 times faster (3.5 minutes vs 1 minute)

This commit is contained in:
Aurora Lahtela 2022-04-18 15:10:33 +03:00
parent 3ab98a220b
commit e9dcb591c8
11 changed files with 114 additions and 189 deletions

View File

@ -48,13 +48,13 @@ class BungeeSystemTest {
private DBPreparer dbPreparer; private DBPreparer dbPreparer;
@BeforeEach @BeforeEach
void prepareSystem(@TempDir Path temp) throws Exception { void prepareSystem(@TempDir Path temp) {
component = new BungeeMockComponent(temp); component = new BungeeMockComponent(temp);
dbPreparer = new DBPreparer(component.getPlanSystem(), TEST_PORT_NUMBER); dbPreparer = new DBPreparer(new BungeeSystemTestDependencies(component.getPlanSystem()), TEST_PORT_NUMBER);
} }
@Test @Test
void bungeeEnables() throws Exception { void bungeeEnables() {
PlanSystem bungeeSystem = component.getPlanSystem(); PlanSystem bungeeSystem = component.getPlanSystem();
try { try {
PlanConfig config = bungeeSystem.getConfigSystem().getConfig(); PlanConfig config = bungeeSystem.getConfigSystem().getConfig();
@ -75,45 +75,41 @@ class BungeeSystemTest {
@Test @Test
void bungeeDoesNotEnableWithDefaultIP() { void bungeeDoesNotEnableWithDefaultIP() {
EnableException thrown = assertThrows(EnableException.class, () -> { PlanSystem bungeeSystem = component.getPlanSystem();
PlanSystem bungeeSystem = component.getPlanSystem(); try {
try { PlanConfig config = bungeeSystem.getConfigSystem().getConfig();
PlanConfig config = bungeeSystem.getConfigSystem().getConfig(); config.set(WebserverSettings.PORT, TEST_PORT_NUMBER);
config.set(WebserverSettings.PORT, TEST_PORT_NUMBER); config.set(ProxySettings.IP, "0.0.0.0");
config.set(ProxySettings.IP, "0.0.0.0");
DBSystem dbSystem = bungeeSystem.getDatabaseSystem(); DBSystem dbSystem = bungeeSystem.getDatabaseSystem();
SQLiteDB db = dbSystem.getSqLiteFactory().usingDefaultFile(); SQLiteDB db = dbSystem.getSqLiteFactory().usingDefaultFile();
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService); db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
dbSystem.setActiveDatabase(db); dbSystem.setActiveDatabase(db);
bungeeSystem.enable(); // Throws EnableException EnableException thrown = assertThrows(EnableException.class, bungeeSystem::enable);
} finally { assertEquals("IP setting still 0.0.0.0 - Configure Alternative_IP/IP that connects to the Proxy server.", thrown.getMessage());
bungeeSystem.disable(); } finally {
} bungeeSystem.disable();
}); }
assertEquals("IP setting still 0.0.0.0 - Configure Alternative_IP/IP that connects to the Proxy server.", thrown.getMessage());
} }
@Test @Test
void testEnableNoMySQL() { void testEnableNoMySQL() {
assertThrows(EnableException.class, () -> { PlanSystem bungeeSystem = component.getPlanSystem();
PlanSystem bungeeSystem = component.getPlanSystem(); try {
try { PlanConfig config = bungeeSystem.getConfigSystem().getConfig();
PlanConfig config = bungeeSystem.getConfigSystem().getConfig(); config.set(WebserverSettings.PORT, TEST_PORT_NUMBER);
config.set(WebserverSettings.PORT, TEST_PORT_NUMBER); config.set(ProxySettings.IP, "8.8.8.8");
config.set(ProxySettings.IP, "8.8.8.8");
bungeeSystem.enable(); // Throws EnableException assertThrows(EnableException.class, bungeeSystem::enable);
} finally { } finally {
bungeeSystem.disable(); bungeeSystem.disable();
} }
});
} }
@Test @Test
void testEnableWithMySQL() throws Exception { void testEnableWithMySQL() {
PlanSystem bungeeSystem = component.getPlanSystem(); PlanSystem bungeeSystem = component.getPlanSystem();
try { try {
PlanConfig config = bungeeSystem.getConfigSystem().getConfig(); PlanConfig config = bungeeSystem.getConfigSystem().getConfig();

View File

@ -0,0 +1,48 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.storage.database.DBSystem;
import utilities.DBPreparer;
public class BungeeSystemTestDependencies implements DBPreparer.Dependencies {
private final PlanSystem system;
public BungeeSystemTestDependencies(PlanSystem system) {
this.system = system;
}
@Override
public PlanConfig config() {
return system.getConfigSystem().getConfig();
}
@Override
public DBSystem dbSystem() {
return system.getDatabaseSystem();
}
@Override
public void enable() {
}
@Override
public void disable() {
}
}

View File

@ -41,7 +41,7 @@ public class BungeeMockComponent {
SQLDB.setDownloadDriver(false); SQLDB.setDownloadDriver(false);
} }
public PlanBungee getPlanMock() throws Exception { public PlanBungee getPlanMock() {
if (planMock == null) { if (planMock == null) {
planMock = PlanBungeeMocker.setUp() planMock = PlanBungeeMocker.setUp()
.withDataFolder(tempDir.toFile()) .withDataFolder(tempDir.toFile())
@ -53,7 +53,7 @@ public class BungeeMockComponent {
return planMock; return planMock;
} }
public PlanSystem getPlanSystem() throws Exception { public PlanSystem getPlanSystem() {
if (component == null) { if (component == null) {
PlanBungee planMock = getPlanMock(); PlanBungee planMock = getPlanMock();
component = DaggerPlanBungeeComponent.builder() component = DaggerPlanBungeeComponent.builder()

View File

@ -67,7 +67,7 @@ public class PlanBungeeMocker extends Mocker {
return this; return this;
} }
PlanBungeeMocker withResourceFetchingFromJar() throws Exception { PlanBungeeMocker withResourceFetchingFromJar() {
return this; return this;
} }

View File

@ -1,83 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.sql.tables;
import com.djrapitops.plan.storage.database.DBType;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.Executable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
public class MetadataTable {
public static final String TABLE_NAME = "plan_database_metadata";
public static final String KEY = "id";
public static final String VALUE = "uuid";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" +
KEY + ',' +
VALUE +
") VALUES (?, ?)";
public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET " + VALUE + "=?" +
WHERE + KEY + "=?";
public static final String SELECT_VALUE_OF_KEY = SELECT + VALUE + FROM + TABLE_NAME + WHERE + KEY + "=?";
private MetadataTable() {
/* Static information class */
}
public static Executable insertValue(String key, String value) {
return new ExecStatement(MetadataTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, key);
statement.setString(2, value);
}
};
}
public static Query<String> getValueOrNull(String key) {
return new QueryStatement<String>(MetadataTable.SELECT_VALUE_OF_KEY) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, key);
}
@Override
public String processResults(ResultSet set) throws SQLException {
return set.next() ? set.getString(MetadataTable.VALUE) : null;
}
};
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(KEY, Sql.varchar(36)).notNull()
.column(VALUE, Sql.varchar(75)).notNull()
.toString();
}
}

View File

@ -17,21 +17,22 @@
package com.djrapitops.plan.storage.database.transactions.commands; package com.djrapitops.plan.storage.database.transactions.commands;
import com.djrapitops.plan.storage.database.sql.tables.*; import com.djrapitops.plan.storage.database.sql.tables.*;
import com.djrapitops.plan.storage.database.transactions.ThrowawayTransaction; import com.djrapitops.plan.storage.database.transactions.patches.Patch;
import static com.djrapitops.plan.storage.database.sql.building.Sql.DELETE_FROM;
/** /**
* Transaction that removes everything from the database. * Transaction that removes everything from the database.
* *
* @author AuroraLS3 * @author AuroraLS3
*/ */
public class RemoveEverythingTransaction extends ThrowawayTransaction { public class RemoveEverythingTransaction extends Patch {
@Override @Override
protected void performOperations() { public boolean hasBeenApplied() {
// Delete statements are run in a specific order as some tables have foreign keys, return false;
// or had at some point in the past. }
@Override
protected void applyPatch() {
clearTable(SettingsTable.TABLE_NAME); clearTable(SettingsTable.TABLE_NAME);
clearTable(GeoInfoTable.TABLE_NAME); clearTable(GeoInfoTable.TABLE_NAME);
clearTable(NicknamesTable.TABLE_NAME); clearTable(NicknamesTable.TABLE_NAME);
@ -59,6 +60,6 @@ public class RemoveEverythingTransaction extends ThrowawayTransaction {
} }
private void clearTable(String tableName) { private void clearTable(String tableName) {
execute(DELETE_FROM + tableName); execute("DELETE FROM " + tableName);
} }
} }

View File

@ -16,7 +16,6 @@
*/ */
package com.djrapitops.plan.storage.database; package com.djrapitops.plan.storage.database;
import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.delivery.domain.container.PlayerContainer; import com.djrapitops.plan.delivery.domain.container.PlayerContainer;
import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; import com.djrapitops.plan.delivery.domain.keys.PlayerKeys;
import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries; import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries;
@ -64,16 +63,19 @@ class DBPatchMySQLRegressionTest extends DBPatchRegressionTest {
private final String transferTable = "CREATE TABLE IF NOT EXISTS plan_transfer (sender_server_id integer NOT NULL, expiry_date bigint NOT NULL DEFAULT 0, type varchar(100) NOT NULL, extra_variables varchar(255) DEFAULT '', content_64 MEDIUMTEXT, part bigint NOT NULL DEFAULT 0, FOREIGN KEY(sender_server_id) REFERENCES plan_servers(id))"; private final String transferTable = "CREATE TABLE IF NOT EXISTS plan_transfer (sender_server_id integer NOT NULL, expiry_date bigint NOT NULL DEFAULT 0, type varchar(100) NOT NULL, extra_variables varchar(255) DEFAULT '', content_64 MEDIUMTEXT, part bigint NOT NULL DEFAULT 0, FOREIGN KEY(sender_server_id) REFERENCES plan_servers(id))";
private MySQLDB underTest; private MySQLDB underTest;
private static DBPreparer dbPreparer;
@BeforeAll @BeforeAll
static void ensureMySQLAvailable(@TempDir Path tempDir) { static void ensureMySQLAvailable(@TempDir Path tempDir) {
assumeTrue(System.getenv(CIProperties.MYSQL_DATABASE) != null); assumeTrue(System.getenv(CIProperties.MYSQL_DATABASE) != null);
component = new PluginMockComponent(tempDir); dbPreparer = new DBPreparer(DaggerDatabaseTestComponent.builder()
.bindTemporaryDirectory(tempDir)
.build(), TEST_PORT_NUMBER);
} }
@AfterAll @AfterAll
static void closeSystem() throws Exception { static void closeSystem() {
if (component != null) component.getPlanSystem().disable(); if (dbPreparer != null) dbPreparer.tearDown();
} }
private void dropAllTables() { private void dropAllTables() {
@ -89,9 +91,8 @@ class DBPatchMySQLRegressionTest extends DBPatchRegressionTest {
} }
@BeforeEach @BeforeEach
void setUpDBWithOldSchema() throws Exception { void setUpDBWithOldSchema() {
PlanSystem system = component.getPlanSystem(); Optional<Database> db = dbPreparer.prepareMySQL();
Optional<Database> db = new DBPreparer(system, TEST_PORT_NUMBER).prepareMySQL();
assumeTrue(db.isPresent()); assumeTrue(db.isPresent());
underTest = (MySQLDB) db.get(); underTest = (MySQLDB) db.get();

View File

@ -37,6 +37,7 @@ import com.djrapitops.plan.storage.database.queries.objects.playertable.NetworkT
import com.djrapitops.plan.storage.database.queries.objects.playertable.ServerTablePlayersQuery; import com.djrapitops.plan.storage.database.queries.objects.playertable.ServerTablePlayersQuery;
import com.djrapitops.plan.storage.database.sql.building.Sql; import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable;
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
import com.djrapitops.plan.storage.database.transactions.StoreConfigTransaction; import com.djrapitops.plan.storage.database.transactions.StoreConfigTransaction;
import com.djrapitops.plan.storage.database.transactions.Transaction; import com.djrapitops.plan.storage.database.transactions.Transaction;
import com.djrapitops.plan.storage.database.transactions.commands.RemovePlayerTransaction; import com.djrapitops.plan.storage.database.transactions.commands.RemovePlayerTransaction;
@ -56,8 +57,7 @@ import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.djrapitops.plan.storage.database.sql.building.Sql.SELECT; import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
import static com.djrapitops.plan.storage.database.sql.building.Sql.WHERE;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
/** /**
@ -255,7 +255,7 @@ public interface DatabaseTest extends DatabaseTestPreparer {
} }
@Test @Test
@Disabled @Disabled("Flaky test, fails every evening.")
default void sqlDateParsingSanitySQLDoesNotApplyTimezone() { default void sqlDateParsingSanitySQLDoesNotApplyTimezone() {
Database db = db(); Database db = db();
@ -292,7 +292,8 @@ public interface DatabaseTest extends DatabaseTestPreparer {
, new Transaction() { , new Transaction() {
@Override @Override
protected void performOperations() { protected void performOperations() {
execute("UPDATE " + UserInfoTable.TABLE_NAME + " SET " + UserInfoTable.REGISTERED + "=0" + WHERE + UserInfoTable.USER_ID + "=1"); execute("UPDATE " + UserInfoTable.TABLE_NAME + " SET " + UserInfoTable.REGISTERED + "=0" +
WHERE + UserInfoTable.USER_ID + "=(" + SELECT + "MAX(" + UsersTable.ID + ")" + FROM + UsersTable.TABLE_NAME + ")");
} }
} }
); );

View File

@ -29,7 +29,6 @@ import com.djrapitops.plan.storage.database.queries.filter.QueryFilters;
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction; import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction; import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
import com.djrapitops.plan.storage.database.transactions.init.CreateTablesTransaction; import com.djrapitops.plan.storage.database.transactions.init.CreateTablesTransaction;
import com.djrapitops.plan.storage.database.transactions.patches.Patch;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
@ -78,27 +77,12 @@ class MySQLTest implements DatabaseTest, QueriesTestAggregate {
Optional<Database> mysql = preparer.prepareMySQL(); Optional<Database> mysql = preparer.prepareMySQL();
Assumptions.assumeTrue(mysql.isPresent()); Assumptions.assumeTrue(mysql.isPresent());
database = mysql.get(); database = mysql.get();
database.executeTransaction(new CreateTablesTransaction());
} }
@BeforeEach @BeforeEach
void setUp() { void setUp() {
TestErrorLogger.throwErrors(true); TestErrorLogger.throwErrors(true);
db().executeTransaction(new Patch() {
@Override
public boolean hasBeenApplied() {
return false;
}
@Override
public void applyPatch() {
dropTable("plan_world_times");
dropTable("plan_kills");
dropTable("plan_sessions");
dropTable("plan_worlds");
dropTable("plan_users");
}
});
db().executeTransaction(new CreateTablesTransaction());
db().executeTransaction(new RemoveEverythingTransaction()); db().executeTransaction(new RemoveEverythingTransaction());
db().executeTransaction(new StoreServerInformationTransaction(new Server(serverUUID(), TestConstants.SERVER_NAME, "", TestConstants.VERSION))); db().executeTransaction(new StoreServerInformationTransaction(new Server(serverUUID(), TestConstants.SERVER_NAME, "", TestConstants.VERSION)));
@ -107,6 +91,7 @@ class MySQLTest implements DatabaseTest, QueriesTestAggregate {
@AfterAll @AfterAll
static void disableSystem() { static void disableSystem() {
preparer.prepareMySQL().ifPresent(Database::close);
if (database != null) database.close(); if (database != null) database.close();
preparer.tearDown(); preparer.tearDown();
} }

View File

@ -16,7 +16,6 @@
*/ */
package utilities; package utilities;
import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.SubSystem; import com.djrapitops.plan.SubSystem;
import com.djrapitops.plan.settings.config.PlanConfig; import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DatabaseSettings; import com.djrapitops.plan.settings.config.paths.DatabaseSettings;
@ -35,10 +34,6 @@ public class DBPreparer {
private final Dependencies dependencies; private final Dependencies dependencies;
private final int testPortNumber; private final int testPortNumber;
public DBPreparer(PlanSystem system, int testPortNumber) {
this(new PlanSystemAsDependencies(system), testPortNumber);
}
public DBPreparer(Dependencies dependencies, int testPortNumber) { public DBPreparer(Dependencies dependencies, int testPortNumber) {
this.dependencies = dependencies; this.dependencies = dependencies;
this.testPortNumber = testPortNumber; this.testPortNumber = testPortNumber;
@ -91,6 +86,9 @@ public class DBPreparer {
mysql.executeTransaction(new Transaction() { mysql.executeTransaction(new Transaction() {
@Override @Override
protected void performOperations() { protected void performOperations() {
execute("SET GLOBAL innodb_file_per_table=0");
execute("SET GLOBAL innodb_fast_shutdown=2");
execute("DROP DATABASE " + formattedDatabase); execute("DROP DATABASE " + formattedDatabase);
execute("CREATE DATABASE " + formattedDatabase); execute("CREATE DATABASE " + formattedDatabase);
execute("USE " + formattedDatabase); execute("USE " + formattedDatabase);
@ -110,33 +108,4 @@ public class DBPreparer {
DBSystem dbSystem(); DBSystem dbSystem();
} }
@Deprecated
static class PlanSystemAsDependencies implements Dependencies {
private final PlanSystem system;
PlanSystemAsDependencies(PlanSystem system) {
this.system = system;
}
@Override
public void enable() {
system.enable();
}
@Override
public void disable() {
system.disable();
}
@Override
public PlanConfig config() {
return system.getConfigSystem().getConfig();
}
@Override
public DBSystem dbSystem() {
return system.getDatabaseSystem();
}
}
} }

View File

@ -10,6 +10,13 @@
<property name="checks" value="RegexpHeader"/> <property name="checks" value="RegexpHeader"/>
<property name="files" value="package-info.java"/> <property name="files" value="package-info.java"/>
</module> </module>
<module name="SuppressionSingleFilter"> <!-- Skip class fan out complexity for SQLDB.java -->
<property name="checks" value="ClassFanOutComplexity"/>
<!-- These files need refactoring. -->
<property name="files"
value="SQLDB.java|ExtensionRegister.java|DataValueGatherer.java|PlayerOnlineListener.java|.*Test.java"/>
</module>
<module name="RegexpHeader"> <!-- License check --> <module name="RegexpHeader"> <!-- License check -->
<property name="headerFile" value="${config_loc}/java.header"/> <property name="headerFile" value="${config_loc}/java.header"/>
</module> </module>
@ -69,8 +76,8 @@
<!-- Metrics --> <!-- Metrics -->
<module name="ClassFanOutComplexity"> <module name="ClassFanOutComplexity">
<!-- This value is high. Notable: SQLDB: 66 --> <!-- This value is ok with manual exceptions. -->
<property name="max" value="70"/> <property name="max" value="35"/>
</module> </module>
<module name="CyclomaticComplexity"> <module name="CyclomaticComplexity">
<!-- This value is high. Notable: ThemeConfig: 16 --> <!-- This value is high. Notable: ThemeConfig: 16 -->