### Question:
GBPTree implements Closeable { @Override public void close() throws IOException { if ( readOnly ) { doClose(); return; } lock.writerLock(); try { if ( closed ) { return; } maybeForceCleanState(); doClose(); } catch ( IOException ioe ) { try { pagedFile.flushAndForce(); maybeForceCleanState(); doClose(); } catch ( IOException e ) { ioe.addSuppressed( e ); throw ioe; } } finally { lock.writerUnlock(); } } GBPTree( PageCache pageCache, File indexFile, Layout<KEY,VALUE> layout, int tentativePageSize,
            Monitor monitor, Header.Reader headerReader, Consumer<PageCursor> headerWriter,
            RecoveryCleanupWorkCollector recoveryCleanupWorkCollector, boolean readOnly ); static void readHeader( PageCache pageCache, File indexFile, Header.Reader headerReader ); RawCursor<Hit<KEY,VALUE>,IOException> seek( KEY fromInclusive, KEY toExclusive ); void checkpoint( IOLimiter ioLimiter, Consumer<PageCursor> headerWriter ); void checkpoint( IOLimiter ioLimiter ); @Override void close(); Writer<KEY,VALUE> writer(); Writer<KEY,VALUE> writer( double ratioToKeepInLeftOnSplit ); VISITOR visit( VISITOR visitor ); @SuppressWarnings( "unused" ) void printTree(); void printState(); boolean consistencyCheck(); boolean consistencyCheck( boolean reportCrashPointers ); boolean consistencyCheck( GBPTreeConsistencyCheckVisitor<KEY> visitor ); boolean consistencyCheck( GBPTreeConsistencyCheckVisitor<KEY> visitor, boolean reportCrashPointers ); @VisibleForTesting void unsafe( GBPTreeUnsafe<KEY,VALUE> unsafe ); @Override String toString(); boolean wasDirtyOnStartup(); int keyValueSizeCap(); static final Monitor NO_MONITOR; static final Header.Reader NO_HEADER_READER; static final Consumer<PageCursor> NO_HEADER_WRITER; }### Answer:
@Test public void shouldAllowClosingTreeMultipleTimes() throws Exception { GBPTree<MutableLong,MutableLong> index = index().build(); index.close(); index.close(); }
@Test public void shouldCheckpointAfterInitialCreation() throws Exception { CheckpointCounter checkpointCounter = new CheckpointCounter(); GBPTree<MutableLong,MutableLong> index = index().with( checkpointCounter ).build(); assertEquals( 1, checkpointCounter.count() ); index.close(); }
@Test public void indexMustBeDirtyWhenCrashedWithChangesSinceLastCheckpoint() throws Exception { try ( EphemeralFileSystemAbstraction ephemeralFs = new EphemeralFileSystemAbstraction() ) { ephemeralFs.mkdirs( indexFile.getParentFile() ); PageCache pageCache = pageCacheRule.getPageCache( ephemeralFs ); EphemeralFileSystemAbstraction snapshot; try ( GBPTree<MutableLong, MutableLong> index = index( pageCache ).build() ) { insert( index, 0, 1 ); snapshot = ephemeralFs.snapshot(); } pageCache.close(); MonitorDirty monitorDirty = new MonitorDirty(); pageCache = pageCacheRule.getPageCache( snapshot ); try ( GBPTree<MutableLong, MutableLong> ignored = index( pageCache ).with( monitorDirty ).build() ) { } assertFalse( "Expected to be dirty on start after crash", monitorDirty.cleanOnStart() ); } }