/*
* Copyright (c) 2014 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.werval.devshell;
import io.werval.spi.dev.DevShellSPI.SourceChangeListener;
import io.werval.spi.dev.DevShellSPI.SourceWatch;
import io.werval.util.DeltreeFileVisitor;
import io.werval.test.util.Slf4jRule;
import java.nio.file.Path;
import java.util.concurrent.Callable;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static com.jayway.awaitility.Awaitility.await;
import static io.werval.util.Strings.indentTwoSpaces;
import static java.nio.file.Files.createDirectories;
import static java.nio.file.Files.createFile;
import static java.nio.file.Files.delete;
import static java.nio.file.Files.walkFileTree;
import static java.nio.file.Files.write;
import static java.nio.file.StandardOpenOption.SYNC;
import static java.nio.file.StandardOpenOption.WRITE;
import static java.util.Collections.singleton;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* JavaWatcher Test.
*/
@Ignore( "unstable" )
public class JavaWatcherTest
{
@BeforeClass
public static void assumeNotTravis()
{
// This test is disabled on Travis as it is too fragile (Thread.sleeps ...)
Assume.assumeTrue( System.getenv("TRAVIS") == null );
}
private static final long SLEEP_TIME = 2000;
@Rule
public final TemporaryFolder tmp = new TemporaryFolder();
@Rule
public final Slf4jRule slf4j = new Slf4jRule()
{
{
record( Level.TRACE );
recordForType( JavaWatcher.class );
}
};
private volatile boolean changed;
private final SourceChangeListener changed_l = new SourceChangeListener()
{
@Override
public void onChange()
{
changed = true;
}
};
private final Callable<Boolean> changed_c = new Callable<Boolean>()
{
@Override
public Boolean call()
throws Exception
{
return changed;
}
};
@Before
public void notChanged()
{
changed = false;
}
private class Check
implements AutoCloseable
{
private final Path path;
public Check( Path path )
{
this.path = path;
slf4j.clear();
}
@Override
public void close()
{
System.out.println( "Logs of CheckBlock for " + path );
for( String log : slf4j.allStatements() )
{
System.out.println( indentTwoSpaces( log, 2 ) );
}
assertThat( slf4j.contains( path.toAbsolutePath().toString() ), is( true ) );
changed = false;
}
}
@Test
public void watchExistingDirectory()
throws Exception
{
Path root = tmp.getRoot().toPath().resolve( "root" );
createDirectories( root );
SourceWatch watch = new JavaWatcher().watch( singleton( root.toFile() ), changed_l );
try
{
assertThat( changed, is( false ) );
Path file = root.resolve( "file" );
Path subdir = root.resolve( "subdir" );
Path subfile = subdir.resolve( "subfile" );
try( Check block = new Check( file ) )
{
createFile( file );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( subdir ) )
{
createDirectories( subdir );
await().until( changed_c, is( true ) );
assertTrue( slf4j.contains( "newly created directory " + subdir.toAbsolutePath() ) );
}
try( Check block = new Check( subfile ) )
{
createFile( subfile );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( subfile ) )
{
delete( subfile );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( subdir ) )
{
walkFileTree( subdir, new DeltreeFileVisitor() );
await().until( changed_c, is( true ) );
// assertTrue( slf4j.contains( "delete" ) ); // Fail on Linux
}
try( Check block = new Check( file ) )
{
delete( file );
await().until( changed_c, is( true ) );
}
}
finally
{
watch.unwatch();
}
}
@Test
public void watchExistingSingleFile()
throws Exception
{
Path root = tmp.getRoot().toPath().resolve( "root" );
createDirectories( root );
Path singleFile = root.resolve( "single-file" );
Path otherFile = root.resolve( "other-file" );
createFile( singleFile );
SourceWatch watch = new JavaWatcher().watch( singleton( singleFile.toFile() ), changed_l );
Thread.sleep( SLEEP_TIME );
try
{
assertThat( changed, is( false ) );
try( Check block = new Check( singleFile ) )
{
write( singleFile, "CHANGED".getBytes(), WRITE, SYNC );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( singleFile ) )
{
delete( singleFile );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( singleFile ) )
{
createFile( singleFile );
await().until( changed_c, is( true ) );
}
createFile( otherFile );
Thread.sleep( SLEEP_TIME );
assertFalse( changed );
}
finally
{
watch.unwatch();
}
}
@Test
public void watchAbsentDirectory()
throws Exception
{
Path root = tmp.getRoot().toPath().resolve( "root" );
createDirectories( root );
Path dir = root.resolve( "dir" );
Path file = dir.resolve( "file" );
SourceWatch watch = new JavaWatcher().watch( singleton( dir.toFile() ), changed_l );
Thread.sleep( SLEEP_TIME );
try
{
assertThat( changed, is( false ) );
try( Check block = new Check( dir ) )
{
createDirectories( dir );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( dir ) )
{
createFile( file );
await().until( changed_c, is( true ) );
}
}
finally
{
watch.unwatch();
}
}
@Test
public void watchAbsentSingleFile()
throws Exception
{
Path root = tmp.getRoot().toPath().resolve( "root" );
createDirectories( root );
Path singleFile = root.resolve( "single-file" );
SourceWatch watch = new JavaWatcher().watch( singleton( singleFile.toFile() ), changed_l );
Thread.sleep( SLEEP_TIME );
try
{
assertThat( changed, is( false ) );
try( Check block = new Check( singleFile ) )
{
createFile( singleFile );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( singleFile ) )
{
write( singleFile, "CHANGED".getBytes() );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( singleFile ) )
{
delete( singleFile );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( singleFile ) )
{
createFile( singleFile );
await().until( changed_c, is( true ) );
}
}
finally
{
watch.unwatch();
}
}
@Test
public void watchAbsentDeepPath()
throws Exception
{
Path root = tmp.getRoot().toPath().resolve( "root" );
createDirectories( root );
Path dir = root.resolve( "dir" );
Path subdir = root.resolve( "subdir" );
SourceWatch watch = new JavaWatcher().watch( singleton( subdir.toFile() ), changed_l );
Thread.sleep( SLEEP_TIME );
try
{
assertThat( changed, is( false ) );
createDirectories( dir );
Thread.sleep( SLEEP_TIME );
assertFalse( changed );
try( Check block = new Check( subdir ) )
{
createDirectories( subdir );
await().until( changed_c, is( true ) );
}
}
finally
{
watch.unwatch();
}
}
@Test
public void watchPresentAbsentPresentSingleFile()
throws Exception
{
Path root = tmp.getRoot().toPath().resolve( "root" );
createDirectories( root );
SourceWatch watch = new JavaWatcher().watch( singleton( root.toFile() ), changed_l );
try
{
assertThat( changed, is( false ) );
Path file = root.resolve( "file" );
try( Check block = new Check( root ) )
{
createFile( file );
await().until( changed_c, is( true ) );
}
try( Check block = new Check( root ) )
{
walkFileTree( root, new DeltreeFileVisitor() );
await().until( changed_c, is( true ) );
// assertTrue( slf4j.contains( "delete" ) ); // Fail on Linux
}
try( Check block = new Check( root ) )
{
createDirectories( root );
createFile( file );
await().until( changed_c, is( true ) );
}
}
finally
{
watch.unwatch();
}
}
}