All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.unlaxer.ParserTestBase Maven / Gradle / Ivy

package org.unlaxer;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import org.junit.AfterClass;
import org.unlaxer.context.CombinedDebugSpecifier;
import org.unlaxer.context.CreateMetaTokenSprcifier;
import org.unlaxer.context.ParseContext;
import org.unlaxer.listener.CombinedDebugListener;
import org.unlaxer.listener.OutputLevel;
import org.unlaxer.listener.DebugParserListener;
import org.unlaxer.listener.DebugTransactionListener;
import org.unlaxer.listener.LogOutputCountListener;
import org.unlaxer.parser.Parser;
import org.unlaxer.util.BlackHole;
import org.unlaxer.util.MultipleOutputStream;

import net.arnx.jsonic.JSON;

public class ParserTestBase {
	
	public static String testFolderName ="parserTest";
	
	public static void setLevel(OutputLevel outputLevel) {
		ParserTestBase.outputLevel.set(outputLevel);
	}
	
	public final LogListenerContainer transactionLogger = new LogListenerContainer();
	public final LogListenerContainer parseLogger = new LogListenerContainer();
	public final LogListenerContainer combinedLogger = new LogListenerContainer();
	
	
	public static void setLogOutputCountListener(LogOutputCountListener listener){
		ParserTestBase.logOutputCountListener.set(listener);
	}
	
	public static void clearLogOutputCountListener(){
		ParserTestBase.logOutputCountListener.set(LogOutputCountListener.BlackHole);
	}
	
	public TestResult testPartialMatch(Parser parser, String sourceString, String matchedString,
			boolean createMeta)  {
		return testMatch(parser, sourceString, matchedString, createMeta);
	}
	public TestResult testPartialMatch(Parser parser, String sourceString, String matchedString) {
		return testMatch(parser, sourceString, matchedString, true);
	}

	TestResult testMatch(Parser parser, String sourceString, String matchedString, boolean createMeta){
		
		return test(parser, sourceString, createMeta,
			(parseContext , parsed)->{
				resultParsed.set(parsed);
				Optional lastToken = parseContext.getCurrent().getTokenString();
				resultTokenString.set(lastToken);
				assertEquals(true, parsed.isSucceeded());
				if (matchedString == null) {
					assertEquals(false, lastToken.isPresent());
				} else {
					if("".equals(sourceString)){
						
						assertFalse(parsed.getConsumed().tokenString.isPresent());
					}else{
						assertEquals(matchedString, parsed.getConsumed().tokenString.get());
					}
				}
				return new TestResult(parsed, parseContext, lastToken);
			}
		);
	}

	private ParseContext createParseContext(
			boolean createMeta, StringSource source, 
			PrintStream transactionOut, PrintStream parseOut) {
		
		return new ParseContext(
			source, //
			
			new CombinedDebugSpecifier(
				new CombinedDebugListener(
					new DebugParserListener(
							parseOut, //
							outputLevel.get(),//
							logOutputCountListener.get(),//
							parseLogger.breakPoints.get()
					),
					new DebugTransactionListener(
							transactionOut , //
							outputLevel.get(),//
							logOutputCountListener.get(),//
							transactionLogger.breakPoints.get()//
					),
					combinedLogger.breakPoints.get()
				)
					
			),
			CreateMetaTokenSprcifier.of(createMeta)
		);
	}

	public TestResult testAllMatch(Parser parser, String sourceString) {

		return testPartialMatch(parser, sourceString, sourceString);
	}

	public TestResult testAllMatch(Parser parser, String sourceString, boolean createMeta) {

		return testMatch(parser, sourceString, sourceString, createMeta);
	}

	public TestResult testUnMatch(Parser parser, String sourceString) {
		return testUnMatch(parser, sourceString, true);
	}

	public TestResult testUnMatch(Parser parser, String sourceString, boolean createMeta) {

		return test(parser, sourceString, createMeta,
			(parseContext , parsed)->{
				resultParsed.set(parsed);
				assertEquals(false, parsed.isSucceeded());
				Optional lastToken = parseContext.getCurrent().getTokenString();
				return new TestResult(parsed, parseContext, lastToken);
			}
		);
	}
	
	public TestResult testSucceededOnly(Parser parser, String sourceString) {
		return testSucceededOnly(parser, sourceString, false);
	}
	
	public TestResult testSucceededOnly(Parser parser, String sourceString, boolean createMeta) {

		return test(parser, sourceString, createMeta, 
			(parseContext , parsed)->{
				resultParsed.set(parsed);
				assertEquals(true, parsed.isSucceeded());
				Optional lastToken = parseContext.getCurrent().getTokenString();
				return new TestResult(parsed, parseContext, lastToken);
			}
		);
	}
	
	public TestResult test(Parser parser, String sourceString, boolean createMeta , ParseFunction parseFunction) {

		int count = counts.get();
		counts.set(count+1);

		StringSource source = new StringSource(sourceString);
		try (OutputStream transactionFile = createFileOutputSream("transaction" , count);
			OutputStream parseFile = createFileOutputSream("parse" , count);
			OutputStream bothFile = createFileOutputSream("combined" , count);
				
			PrintStream transactionPrintStream = 
					new PrintStream(new MultipleOutputStream(transactionFile,bothFile),false,"UTF-8");
				
			PrintStream parsePrintStream = 
					new PrintStream(new MultipleOutputStream(parseFile,bothFile),false,"UTF-8");
			PrintStream tokenPrintStream = new PrintStream(createFileOutputSream("token" , count),false,"UTF-8");
				
			ParseContext parseContext = 
				createParseContext(createMeta, source, transactionPrintStream , parsePrintStream);
			){
			Parsed parsed = parser.parse(parseContext);
			TestResult testResult = parseFunction.apply(parseContext, parsed);
			
			tokenPrintStream.println(
				TokenPrinter.get(
					testResult.parsed.getRootToken(false == createMeta), OutputLevel.detail)
			);
			
			return testResult;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	
	interface ParseFunction extends BiFunction{}
	
	OutputStream createFileOutputSream(String logName , int count) {
		
		StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
		LastAndFirst callerIndex = getCallerIndex(getTestClass(),stackTraces);
		try {
			String kind = stackTraces[callerIndex.first-1].getMethodName();
			String callerMethod = stackTraces[callerIndex.last].getMethodName();
			String callerClass = stackTraces[callerIndex.last].getClassName();
			
			callerMethodName.set(callerMethod);
			callerClassName.set(callerClass);
			int callerLine = stackTraces[callerIndex.last].getLineNumber();
			OutputStream out;
			try {
				out = outputLevel.get().isNone() ? 
						BlackHole.getOutputStream(): 
							createOutputStream(logName, kind, callerMethod, callerLine, count);
						return out;
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			}
			
		}catch (IndexOutOfBoundsException e) {
			System.err.println(JSON.encode(callerIndex));
			String thisClassName = getClass().getName();
			System.err.format("this class name %s\n",thisClassName);

			for(int i = 0 ; i < stackTraces.length ; i++){
				StackTraceElement stackTraceElement = stackTraces[i];
				String currentClassName = stackTraceElement.getClassName();
				System.err.format("\tstackTrace element class name %s\n",currentClassName);
			}
			throw e;
		}
	}

	private OutputStream createOutputStream(String logName, String kind, String callerMethod, int callerLine,
			int count) throws FileNotFoundException{
		File root = new File("build/"+testFolderName);
		root.mkdirs();
		File testClassFolder = new File(root,getClass().getName());
		testClassFolder.delete();
		testClassFolder.mkdirs();
		return new FileOutputStream(
			new File(testClassFolder, 
				callerMethod+//
				"_"+kind+//
				"_("+count+",L"+callerLine+")."+//
				logName+//
				".log"
			)
		);
	}
	
	public static LastAndFirst getCallerIndex(Class thisClass , StackTraceElement[] stackTraces){
		String thisClassName = thisClass.getName();
		LastAndFirst lastAndFirst = new LastAndFirst();
		System.out.format("this class name %s\n",thisClassName);

		for(int i = 0 ; i < stackTraces.length ; i++){
			StackTraceElement stackTraceElement = stackTraces[i];
			String currentClassName = stackTraceElement.getClassName();
			System.out.format("\tstackTrace element class name %s\n",currentClassName);
			if(currentClassName.equals(thisClassName)){
				lastAndFirst.apply(i);
			}
		}
		return lastAndFirst;
	}
	
	static class LastAndFirst{
		public int last=0;
		public int first=Integer.MAX_VALUE;
		public void apply(int value){
			first = Math.min(first, value);
			last = Math.max(last, value);
		}
	}
	
	
	ThreadLocal counts = new ThreadLocal(){

		@Override
		protected Integer initialValue() {
			return 1;
		}
	};
	
	@AfterClass
	public static void checkTokenAndTransaction(){
		
		if(isCI()) {
			return;
		}
		
		try {
			List results = 
				Files.walk(Paths.get("build/"+testFolderName , callerClassName.get()))
					.filter(path->path.toFile().isFile())
					.map(actualLog->{
						ResultKind compareKind = compare(actualLog);
						String name = actualLog.toString();
						String message;
						switch (compareKind) {
						case ioError:
							message = String.format("read error : %s\n" , name);break;
						case notMatch:
							message = String.format("not match : %s\n" , name);break;
						case noExpected:
							message = String.format("expected file not exists. see test/resources/parserTest : %s\n" , name);
							break;
						case match:
						default:
							message = String.format("match : %s\n" , name);break;
						}
						return new CompareResult(compareKind, actualLog, message);
					})
					.filter(result->result.compareKind == ResultKind.notMatch)
					.collect(Collectors.toList());
			if(results.size()>0){
				System.err.println("build/reports/tests/classes/"+
					callerClassName.get()+".html");
				fail(JSON.encode(results));
			}
		} catch (NoSuchFileException e){
			System.err.format(
					"%s#%s() is not execute setDebugLevel(DebugLevel) , then do not execute check Token and Transaction",
					callerClassName.get(),callerMethodName.get());
		
		} catch (IOException e) {
			e.printStackTrace();
		}			

	}
	
	
	public static class CompareResult{
		public ResultKind compareKind;
		public Path path;
		public String message;
		public CompareResult(ResultKind compareKind, Path path, String message) {
			super();
			this.compareKind = compareKind;
			this.path = path;
			this.message = message;
		}
	}
	
	
	public enum ResultKind{
		match,//
		notMatch,//
		noExpected,//
		ioError,//
	}
	
	static ResultKind compare(Path actualLog){
		Path expectedFile = getExpectedFile(actualLog);
		if(false == expectedFile.toFile().exists()){
			return ResultKind.noExpected;
		}
		try(BufferedReader actualReader = Files.newBufferedReader(actualLog, StandardCharsets.UTF_8);
			BufferedReader expectedReader = Files.newBufferedReader(expectedFile, StandardCharsets.UTF_8)){
			while(true){
				String actual = actualReader.readLine();
				String expected = expectedReader.readLine();
				if(actual == null && expected == null){
					return ResultKind.match;
				}
				if(actual == null || expected == null || false == actual.trim().equals(expected.trim())){
					print("expected", expected);
					print("actual", actual);
					return ResultKind.notMatch;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
			return ResultKind.ioError;
		}
	}
	
	static void print(String header,String data) {
		System.err.print(header+":");
		if(data == null) {
			System.err.println("null");
			return;
		}
		byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
		for (byte b : bytes) {
			System.err.format("%x ",b);
		}
		
	}
	
	static Path getExpectedFile(Path actual){
		Path path = Paths.get(actual.toFile().getAbsolutePath());
		int nameCount = path.getNameCount();
		Path name = path.subpath(nameCount-2,nameCount);

		Path expectedFile = Paths.get("src/test/resources/",
		testFolderName ,name.toString());
		return expectedFile;
	}
	
	static ThreadLocal callerClassName = new ThreadLocal<>();
	static ThreadLocal callerMethodName = new ThreadLocal<>();
	
	ThreadLocal> resultTokenString = new ThreadLocal<>();
	ThreadLocal resultParsed = new ThreadLocal<>();

	static ThreadLocal outputLevel = new ThreadLocal() {

		@Override
		protected OutputLevel initialValue() {
			return OutputLevel.none;
		}
	};
	
	static ThreadLocal logOutputCountListener =
		new ThreadLocal(){

		@Override
		protected LogOutputCountListener initialValue() {
			return LogOutputCountListener.BlackHole;
		}
	};
	public static boolean isCI(){
		return "true".equals(System.getenv("CI"));
	}
	
	public static boolean isNotCI(){
		return false == isCI();
	}
	
	public Class getTestClass(){
		return getClass();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy