CC-Tweaked/src/test/java/dan200/computercraft/core/LuaCoverage.java

157 lines
5.0 KiB
Java

/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core;
import com.google.common.base.Strings;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntMaps;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.squiddev.cobalt.Prototype;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LuaC;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Map;
import java.util.Queue;
class LuaCoverage
{
private static final Logger LOG = LoggerFactory.getLogger( LuaCoverage.class );
private static final Path ROOT = new File( "src/main/resources/data/computercraft/lua" ).toPath();
private final Map<String, Int2IntMap> coverage;
private final String blank;
private final String zero;
private final String countFormat;
LuaCoverage( Map<String, Int2IntMap> coverage )
{
this.coverage = coverage;
int max = coverage.values().stream()
.flatMapToInt( x -> x.values().stream().mapToInt( y -> y ) )
.max().orElse( 0 );
int maxLen = Math.max( 1, (int) Math.ceil( Math.log10( max ) ) );
blank = Strings.repeat( " ", maxLen + 1 );
zero = Strings.repeat( "*", maxLen ) + "0";
countFormat = "%" + (maxLen + 1) + "d";
}
void write( Writer out ) throws IOException
{
Files.find( ROOT, Integer.MAX_VALUE, ( path, attr ) -> attr.isRegularFile() ).forEach( path -> {
Path relative = ROOT.relativize( path );
String full = relative.toString().replace( '\\', '/' );
if( !full.endsWith( ".lua" ) ) return;
Int2IntMap possiblePaths = coverage.remove( "/" + full );
if( possiblePaths == null ) possiblePaths = coverage.remove( full );
if( possiblePaths == null )
{
possiblePaths = Int2IntMaps.EMPTY_MAP;
LOG.warn( "{} has no coverage data", full );
}
try
{
writeCoverageFor( out, path, possiblePaths );
}
catch( IOException e )
{
throw new UncheckedIOException( e );
}
} );
for( String filename : coverage.keySet() )
{
if( filename.startsWith( "/test-rom/" ) ) continue;
LOG.warn( "Unknown file {}", filename );
}
}
private void writeCoverageFor( Writer out, Path fullName, Int2IntMap visitedLines ) throws IOException
{
if( !Files.exists( fullName ) )
{
LOG.error( "Cannot locate file {}", fullName );
return;
}
IntSet activeLines = getActiveLines( fullName.toFile() );
out.write( "==============================================================================\n" );
out.write( fullName.toString().replace( '\\', '/' ) );
out.write( "\n" );
out.write( "==============================================================================\n" );
try( BufferedReader reader = Files.newBufferedReader( fullName ) )
{
String line;
int lineNo = 0;
while( (line = reader.readLine()) != null )
{
lineNo++;
int count = visitedLines.getOrDefault( lineNo, -1 );
if( count >= 0 )
{
out.write( String.format( countFormat, count ) );
}
else if( activeLines.contains( lineNo ) )
{
out.write( zero );
}
else
{
out.write( blank );
}
out.write( ' ' );
out.write( line );
out.write( "\n" );
}
}
}
private static IntSet getActiveLines( File file ) throws IOException
{
IntSet activeLines = new IntOpenHashSet();
Queue<Prototype> queue = new ArrayDeque<>();
try( InputStream stream = Files.newInputStream( file.toPath() ) )
{
Prototype proto = LuaC.compile( stream, "@" + file.getPath() );
queue.add( proto );
}
catch( CompileException e )
{
throw new IllegalStateException( "Cannot compile", e );
}
Prototype proto;
while( (proto = queue.poll()) != null )
{
int[] lines = proto.lineInfo;
if( lines != null )
{
for( int line : lines )
{
activeLines.add( line );
}
}
if( proto.children != null ) Collections.addAll( queue, proto.children );
}
return activeLines;
}
}