package org.fandev.lang; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementVisitor; import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import static junit.framework.Assert.assertEquals; import org.fandev.lang.fan.FanHighlightingLexer; import org.fandev.lang.fan.FanTokenTypes; import org.fandev.util.TestUtils; import org.junit.Assert; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * @author Dror Bereznitsky Date: Jan 13, 2009 Time: 10:53:36 AM */ public abstract class BaseFanTest { protected Project myProject; protected Module myModule; protected IdeaProjectTestFixture myFixture; private static final Logger LOG = Logger.getInstance(BaseFanTest.class.getName()); private static final byte[] BUFF = new byte[8000]; protected ExecutorService executorService; protected void setupProject() { executorService = Executors.newSingleThreadExecutor(); myFixture = createFixture(); try { myFixture.setUp(); } catch (Exception e) { LOG.error(e); } myModule = myFixture.getModule(); myProject = myModule.getProject(); } protected IdeaProjectTestFixture createFixture() { final TestFixtureBuilder<IdeaProjectTestFixture> fixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createLightFixtureBuilder(); return fixtureBuilder.getFixture(); } public static String readFile(final File myTestFile) throws IOException { String content = new String(FileUtil.loadFileText(myTestFile)); Assert.assertNotNull(content); content = StringUtil.replace(content, "\r", ""); // for MACs Assert.assertTrue("No data found in source file", content.length() > 0); return content; } protected void lexerAllInDir(final File dir, final List<LexerResult> results) throws Throwable { final File[] files = dir.listFiles(); for (final File file : files) { if (file.isDirectory()) { lexerAllInDir(file, results); } else { final String fileName = file.getName(); if (fileName.endsWith(".fan") && !fileName.equals("gamma.fan")) { try { final List<ParserBlock> list = lex(BaseFanTest.readFile(file)); checkNoBadCharacters(list); final LexerResult result = new LexerResult(file.getPath(), list.size()); for (final ParserBlock block : list) { final Integer t = result.totals.get(block.type); if (t == null) { result.totals.put(block.type, 1); } else { result.totals.put(block.type, t + 1); } } results.add(result); } catch (Throwable throwable) { System.err.println("Error parsing " + file.getAbsolutePath()); results.add(new LexerResult(throwable.getMessage(), ResultStatusCode.TIMEOUT, file.getPath())); } } } } } protected List<ParserBlock> lex(final CharSequence buff) throws Throwable { final FanHighlightingLexer lexer = new FanHighlightingLexer(); lexer.start(buff); final List<ParserBlock> blocks = new ArrayList<ParserBlock>(); ParserBlock block; while (lexer.getState() == 0) { block = new ParserBlock(); block.type = lexer.getTokenType(); if (block.type == null) { break; } block.start = lexer.getTokenStart(); block.end = lexer.getTokenEnd(); blocks.add(block); try { lexer.advance(); } catch (Throwable e) { for (int i = Math.max(blocks.size() - 30, 0); i < blocks.size(); i++) { final ParserBlock rBlock = blocks.get(i); System.out.println(rBlock.toString() + " " + getTextBlock(buff, rBlock)); } e.printStackTrace(System.out); junit.framework.Assert.assertTrue("Got Exception parsing " + e.getMessage(), false); } } return blocks; } protected CharSequence getTextBlock(final CharSequence buff, final ParserBlock rBlock) { return buff.subSequence(rBlock.start, rBlock.end); } public String getResourceCharArray(final String resourceName) throws IOException { final InputStream is = getClass().getResourceAsStream(resourceName); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); int nbRead; while ((nbRead = is.read(BUFF)) > 0) { bos.write(BUFF, 0, nbRead); } return bos.toString(); } protected void checkNoBadCharacters(final List<ParserBlock> list) { for (final ParserBlock block : list) { junit.framework.Assert.assertNotSame(block.type, FanTokenTypes.BAD_CHARACTER); } } protected void parseAllInDir(final File dir, FileFilter fileFilter, final List<ParsingResult> results) throws Throwable { if (fileFilter == null) { fileFilter = new FileFilter() { public boolean accept(final File pathname) { return true; } }; } final File[] files = dir.listFiles(fileFilter); for (final File file : files) { if (file.isDirectory()) { parseAllInDir(file, fileFilter, results); } else { final String fileName = file.getName(); if (fileName.endsWith(".fan") && !fileName.equals("gamma.fan")) { try { System.out.println("Parsing file: " + file.getAbsolutePath()); final PsiFile psiFile = parse(readFile(file)); if (psiFile == null) { System.out.println("[Error] Failed parsing file (timeout): " + file.getAbsolutePath()); results.add(new ParsingResult(ResultStatusCode.TIMEOUT, file.getPath(), "timeout", null)); } else { final int errors = visitErrorElements(psiFile); System.out.println("Parsed file: " + file.getAbsolutePath()); if (errors == 0) { results.add(new ParsingResult(ResultStatusCode.OK, file.getPath(), "", psiFile)); } else { results.add( new ParsingResult(ResultStatusCode.PARSING_ERROR, file.getPath(), "", psiFile)); } } } catch (Throwable throwable) { String msg = throwable.getMessage(); System.out.println("[Error] Error parsing " + file.getAbsolutePath() + ": " + msg); int firstReturn = msg.indexOf("\n"); if (firstReturn != -1) { msg = msg.substring(0, firstReturn); } results.add(new ParsingResult(ResultStatusCode.EXCEPTION, file.getPath(), msg, null)); } } } } } protected int visitErrorElements(final PsiFile psiFile) { final PsiRecursiveErrorElementVisitor recursiveErrorElementVisitor = new PsiRecursiveErrorElementVisitor(); psiFile.accept(recursiveErrorElementVisitor); return recursiveErrorElementVisitor.getErrors(); } public PsiFile parse(final String text) throws Throwable { final Callable<PsiFile> testBlock = new Callable<PsiFile>() { public PsiFile call() throws Exception { try { return TestUtils.createPseudoPhysicalFanFile(myProject, text); } catch (Throwable throwable) { throw new Exception(throwable); } } }; final Future<PsiFile> fileFuture = executorService.submit(testBlock); try { return fileFuture.get(getTimeoutSecs(), TimeUnit.SECONDS); } catch (TimeoutException e) { return null; } finally { if (fileFuture != null && !fileFuture.isDone()) { fileFuture.cancel(true); } } } protected int getTimeoutSecs() { return 20; } public PsiFile parseWithNoTimeout(final String text) throws Throwable { final PsiFile psiFile = TestUtils.createPseudoPhysicalFanFile(myProject, text); assertEquals(0, visitErrorElements(psiFile)); return psiFile; } private class PsiRecursiveErrorElementVisitor extends PsiRecursiveElementVisitor { private int errors = 0; @Override public void visitErrorElement(final PsiErrorElement element) { errors++; final String textFile = element.getContainingFile().getText(); final int offset = element.getTextOffset(); System.out.println("\tError element: " + element.getErrorDescription() + " for pos " + offset + " around '" + textFile.substring(Math.max(0, offset - 20), Math.min(textFile.length(), offset + 20)) + "'"); } public int getErrors() { return errors; } } }