diff --git a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java index f6458b25..8473dcaa 100644 --- a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java +++ b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java @@ -97,20 +97,22 @@ import org.springframework.util.StringUtils; *
* CREATE TABLE SPRING_SESSION (
* PRIMARY_ID CHAR(36) NOT NULL,
- * SESSION_ID CHAR(36),
+ * SESSION_ID CHAR(36) NOT NULL,
* CREATION_TIME BIGINT NOT NULL,
* LAST_ACCESS_TIME BIGINT NOT NULL,
* MAX_INACTIVE_INTERVAL INT NOT NULL,
+ * EXPIRY_TIME BIGINT NOT NULL,
* PRINCIPAL_NAME VARCHAR(100),
* CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
* );
*
- * CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
+ * CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
+ * CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (EXPIRY_TIME);
*
* CREATE TABLE SPRING_SESSION_ATTRIBUTES (
* SESSION_PRIMARY_ID CHAR(36) NOT NULL,
- * ATTRIBUTE_NAME VARCHAR(200),
- * ATTRIBUTE_BYTES BYTEA,
+ * ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
+ * ATTRIBUTE_BYTES BYTEA NOT NULL,
* CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
* CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
* );
@@ -138,8 +140,8 @@ public class JdbcOperationsSessionRepository implements
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final String CREATE_SESSION_QUERY =
- "INSERT INTO %TABLE_NAME%(PRIMARY_ID, SESSION_ID, CREATION_TIME, LAST_ACCESS_TIME, MAX_INACTIVE_INTERVAL, PRINCIPAL_NAME) " +
- "VALUES (?, ?, ?, ?, ?, ?)";
+ "INSERT INTO %TABLE_NAME%(PRIMARY_ID, SESSION_ID, CREATION_TIME, LAST_ACCESS_TIME, MAX_INACTIVE_INTERVAL, EXPIRY_TIME, PRINCIPAL_NAME) " +
+ "VALUES (?, ?, ?, ?, ?, ?, ?)";
private static final String CREATE_SESSION_ATTRIBUTE_QUERY =
"INSERT INTO %TABLE_NAME%_ATTRIBUTES(SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) " +
@@ -152,7 +154,7 @@ public class JdbcOperationsSessionRepository implements
"WHERE S.SESSION_ID = ?";
private static final String UPDATE_SESSION_QUERY =
- "UPDATE %TABLE_NAME% SET SESSION_ID = ?, LAST_ACCESS_TIME = ?, MAX_INACTIVE_INTERVAL = ?, PRINCIPAL_NAME = ? " +
+ "UPDATE %TABLE_NAME% SET SESSION_ID = ?, LAST_ACCESS_TIME = ?, MAX_INACTIVE_INTERVAL = ?, EXPIRY_TIME = ?, PRINCIPAL_NAME = ? " +
"WHERE PRIMARY_ID = ?";
private static final String UPDATE_SESSION_ATTRIBUTE_QUERY =
@@ -175,9 +177,9 @@ public class JdbcOperationsSessionRepository implements
"LEFT OUTER JOIN %TABLE_NAME%_ATTRIBUTES SA ON S.PRIMARY_ID = SA.SESSION_PRIMARY_ID " +
"WHERE S.PRINCIPAL_NAME = ?";
- private static final String DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY =
+ private static final String DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY =
"DELETE FROM %TABLE_NAME% " +
- "WHERE MAX_INACTIVE_INTERVAL < (? - LAST_ACCESS_TIME) / 1000";
+ "WHERE EXPIRY_TIME < ?";
private static final Log logger = LogFactory
.getLog(JdbcOperationsSessionRepository.class);
@@ -211,7 +213,7 @@ public class JdbcOperationsSessionRepository implements
private String listSessionsByPrincipalNameQuery;
- private String deleteSessionsByLastAccessTimeQuery;
+ private String deleteSessionsByExpiryTimeQuery;
/**
* If non-null, this value is used to override the default value for
@@ -333,11 +335,11 @@ public class JdbcOperationsSessionRepository implements
/**
* Set the custom SQL query used to delete the sessions by last access time.
- * @param deleteSessionsByLastAccessTimeQuery the SQL query string
+ * @param deleteSessionsByExpiryTimeQuery the SQL query string
*/
- public void setDeleteSessionsByLastAccessTimeQuery(String deleteSessionsByLastAccessTimeQuery) {
- Assert.hasText(deleteSessionsByLastAccessTimeQuery, "Query must not be empty");
- this.deleteSessionsByLastAccessTimeQuery = deleteSessionsByLastAccessTimeQuery;
+ public void setDeleteSessionsByExpiryTimeQuery(String deleteSessionsByExpiryTimeQuery) {
+ Assert.hasText(deleteSessionsByExpiryTimeQuery, "Query must not be empty");
+ this.deleteSessionsByExpiryTimeQuery = deleteSessionsByExpiryTimeQuery;
}
/**
@@ -385,7 +387,8 @@ public class JdbcOperationsSessionRepository implements
ps.setLong(3, session.getCreationTime().toEpochMilli());
ps.setLong(4, session.getLastAccessedTime().toEpochMilli());
ps.setInt(5, (int) session.getMaxInactiveInterval().getSeconds());
- ps.setString(6, session.getPrincipalName());
+ ps.setLong(6, session.getExpiryTime().toEpochMilli());
+ ps.setString(7, session.getPrincipalName());
});
if (!session.getAttributeNames().isEmpty()) {
final List attributeNames = new ArrayList<>(session.getAttributeNames());
@@ -421,8 +424,9 @@ public class JdbcOperationsSessionRepository implements
ps.setString(1, session.getId());
ps.setLong(2, session.getLastAccessedTime().toEpochMilli());
ps.setInt(3, (int) session.getMaxInactiveInterval().getSeconds());
- ps.setString(4, session.getPrincipalName());
- ps.setString(5, session.primaryKey);
+ ps.setLong(4, session.getExpiryTime().toEpochMilli());
+ ps.setString(5, session.getPrincipalName());
+ ps.setString(6, session.primaryKey);
});
}
Map delta = session.getDelta();
@@ -524,7 +528,7 @@ public class JdbcOperationsSessionRepository implements
public void cleanUpExpiredSessions() {
int deletedCount = this.transactionOperations.execute(transactionStatus ->
JdbcOperationsSessionRepository.this.jdbcOperations.update(
- JdbcOperationsSessionRepository.this.deleteSessionsByLastAccessTimeQuery,
+ JdbcOperationsSessionRepository.this.deleteSessionsByExpiryTimeQuery,
System.currentTimeMillis()));
if (logger.isDebugEnabled()) {
@@ -571,8 +575,8 @@ public class JdbcOperationsSessionRepository implements
this.deleteSessionQuery = getQuery(DELETE_SESSION_QUERY);
this.listSessionsByPrincipalNameQuery =
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY);
- this.deleteSessionsByLastAccessTimeQuery =
- getQuery(DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY);
+ this.deleteSessionsByExpiryTimeQuery =
+ getQuery(DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY);
}
private void serialize(PreparedStatement ps, int paramIndex, Object attributeValue)
@@ -643,6 +647,10 @@ public class JdbcOperationsSessionRepository implements
return PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
}
+ Instant getExpiryTime() {
+ return getLastAccessedTime().plus(getMaxInactiveInterval());
+ }
+
public String getId() {
return this.delegate.getId();
}
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-db2.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-db2.sql
index c3111d16..b9408671 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-db2.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-db2.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-derby.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-derby.sql
index c3111d16..b9408671 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-derby.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-derby.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-h2.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-h2.sql
index c9076c4d..3df06c7b 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-h2.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-h2.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-hsqldb.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-hsqldb.sql
index c9076c4d..3df06c7b 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-hsqldb.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-hsqldb.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql
index 28bdf3c1..136b49e9 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=InnoDB;
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-oracle.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-oracle.sql
index 69d3892a..774e7043 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-oracle.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-oracle.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME NUMBER(19,0) NOT NULL,
LAST_ACCESS_TIME NUMBER(19,0) NOT NULL,
MAX_INACTIVE_INTERVAL NUMBER(10,0) NOT NULL,
+ EXPIRY_TIME NUMBER(19,0) NOT NULL,
PRINCIPAL_NAME VARCHAR2(100 CHAR),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-postgresql.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-postgresql.sql
index 570e2ed2..db508828 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-postgresql.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-postgresql.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlite.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlite.sql
index abfd8958..2b45733d 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlite.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlite.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME INTEGER NOT NULL,
LAST_ACCESS_TIME INTEGER NOT NULL,
MAX_INACTIVE_INTERVAL INTEGER NOT NULL,
+ EXPIRY_TIME INTEGER NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlserver.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlserver.sql
index c651dcda..c0ef6b47 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlserver.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlserver.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sybase.sql b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sybase.sql
index 1347a5b6..16f29ae8 100644
--- a/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sybase.sql
+++ b/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sybase.sql
@@ -4,12 +4,13 @@ CREATE TABLE SPRING_SESSION (
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
+ EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) LOCK DATAROWS;
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
-CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (LAST_ACCESS_TIME);
+CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
diff --git a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java
index 24b97d02..6d5c9978 100644
--- a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java
+++ b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java
@@ -274,7 +274,7 @@ public class JdbcOperationsSessionRepositoryTests {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");
- this.repository.setDeleteSessionsByLastAccessTimeQuery(null);
+ this.repository.setDeleteSessionsByExpiryTimeQuery(null);
}
@Test
@@ -282,7 +282,7 @@ public class JdbcOperationsSessionRepositoryTests {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");
- this.repository.setDeleteSessionsByLastAccessTimeQuery(" ");
+ this.repository.setDeleteSessionsByExpiryTimeQuery(" ");
}
@Test