/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.netbeans.modules.ruby;
import java.util.List;
import org.netbeans.api.ruby.platform.RubyInstallation;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Utilities;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
/**
*
* @todo Test partial reformatting (indentation in the middle of a file, newline computation in the middle of a file)
* @todo Partial test: Make sure I don't reformat the line AFTER the newline insertion when I hit newline!
*
* @author Tor Norbye
*/
public class RubyFormatterTest extends RubyTestBase {
public RubyFormatterTest(String testName) {
super(testName);
}
private void reformatFileContents(String file) throws Exception {
reformatFileContents(file, new IndentPrefs(2, 2));
}
// Used to test arbitrary source trees
//public void testReformatSourceTree() {
// List<FileObject> files = new ArrayList<FileObject>();
//
// // Used to test random source trees
// File f = new File("/Users/tor/Desktop/facets-1.8.54"); // NOI18N
// FileObject root = FileUtil.toFileObject(f);
// addAllRubyFiles(root, files);
// reformatAll(files);
//}
private void addAllRubyFiles(FileObject file, List<FileObject> files) {
if (file.isFolder()) {
for (FileObject c : file.getChildren()) {
addAllRubyFiles(c, files);
}
} else if (file.getMIMEType().equals(RubyInstallation.RUBY_MIME_TYPE)) {
files.add(file);
}
}
public void testReformatAll() {
// Find ruby files
List<FileObject> files = findJRubyRubyFiles();
assertTrue(files.size() > 0);
reformatAll(files);
}
private boolean skip(FileObject fo, String name, String parentName) {
if (fo.getName().equals(name) && (parentName == null || fo.getParent().getNameExt().equals(parentName))) {
System.err.println("SKIPPING known bad file " + fo.getNameExt());
return true;
}
return false;
}
private void reformatAll(List<FileObject> files) {
IndentPrefs preferences = new IndentPrefs(2, 2);
RubyFormatter formatter = getFormatter(preferences);
int fileCount = files.size();
int count = 0;
// indent each one
for (FileObject fo : files) {
count++;
// This file is okay and shouldn't end in column 0
if (fo.getNameExt().equals("mvm_subvm.rb")) continue;
if (fo.getName().equals("sample_02") || fo.getName().equals("sample_03")) {
System.err.println("Can't properly format sample_02.rb yet - it's unusual" + fo.getNameExt());
continue;
}
if (skip(fo, "deprecation", "active_support")) continue; // triggers #134931, in rails 2.3.2 the file has a 'def class' method
if (skip(fo, "registry", null)) continue;
if (skip(fo, "delegating_attributes", null)) continue;
if (skip(fo, "indexer", "rubygems")) continue; // bug #182494
if (skip(fo, "date", "1.9")) continue; // Unknown
if (skip(fo, "include", "rdoc")) continue; // Unknown
if (skip(fo, "mathn", "1.9")) continue; // bug #182761
if (skip(fo, "rational", "1.9")) continue; // bug #182761
if (skip(fo, "action_controller_dispatcher", "dispatcher")) continue; // bug #108889
if (skip(fo, "parse_f95", "parsers")) continue; // bug #108889
if (skip(fo, "httputils", "webrick")) continue; // Tested by RubyLexerTest#testDefRegexp
// When erubis is installed:
if (fo.getNameExt().equals("test-enhancers.rb") || fo.getNameExt().equals("test-erubis.rb")) {
System.err.println("SKIPPING " + fo.getNameExt() + " - the lexing of data after __END__ doesn't seem to work");
continue;
}
System.err.println("Formatting file " + count + "/" + files.size() + " : " + FileUtil.getFileDisplayName(fo));
// check that we end up at indentation level 0
BaseDocument doc = getDocument(fo);
try {
format(doc, formatter, 0, doc.getLength(), true);
} catch (Exception ex) {
System.err.println("Exception processing " + FileUtil.getFileDisplayName(fo));
fail(ex.toString());
throw new RuntimeException(ex);
}
// Make sure that we end up on column 0, as all balanced Ruby files typically do
// (check for special exceptions, e.g. formatted strings and whatnot)
try {
int offset = doc.getLength();
while (offset > 0) {
offset = Utilities.getRowStart(doc, offset);
if (Utilities.isRowEmpty(doc, offset) || Utilities.isRowWhite(doc, offset)) {
offset = offset - 1;
continue;
}
int indentation = Utilities.getRowFirstNonWhite(doc, offset) - offset;
if (indentation != 0) {
// Make sure the file actually compiles - we might be picking up some testfiles in the
// JRuby tree which don't actually pass
if (AstUtilities.getRoot(fo) == null) {
System.err.println("WARNING - invalid formatting for " + FileUtil.getFileDisplayName(fo) + " " +
"but it also doesn't parse with JRuby so ignoring");
break;
}
}
assertEquals("Failed formatting file " + count + "/" + fileCount + " \n" + fo.getNameExt() + "\n: Last line not at 0 indentation in " + FileUtil.getFileDisplayName(fo) + " indent=" + indentation + " line=" +
doc.getText(offset, Utilities.getRowEnd(doc, offset) - offset), getExpectedIndentation(fo), indentation);
break;
}
} catch (Exception ex) {
fail(ex.toString());
}
// Also try re-lexing buffer incrementally and make sure it makes sense! (and handle bracket completion stuff)
}
}
private static int getExpectedIndentation(FileObject fo) {
String path = fo.getPath();
if (path.endsWith("actionpack-2.3.2/test/abstract_unit.rb") || // last uses a ternary operator
path.endsWith("1.8/rbconfig/datadir.rb") || // last line isliteral string via '\' on previous line
path.endsWith("linecache-0.46-java/test/data/class1.rb")) {
return 2;
}
return 0;
}
public void testFormatApe() throws Exception {
// Check that the given source files reformat EXACTLY as specified
reformatFileContents("testfiles/ape.rb");
}
public void testFormatMephisto1() throws Exception {
reformatFileContents("testfiles/mephisto-site.rb");
}
public void testFormatMephisto2() throws Exception {
reformatFileContents("testfiles/mephisto_controller.rb");
}
public void testFormatMephisto3() throws Exception {
reformatFileContents("testfiles/mephisto-articles-controller.rb");
}
public void testFormat110332() throws Exception {
// Check that the given source files reformat EXACTLY as specified
reformatFileContents("testfiles/percent-expressions.rb");
}
public void testFormatDate() throws Exception {
// Check that the given source files reformat EXACTLY as specified
reformatFileContents("testfiles/date.rb");
}
public void testFormatResolv() throws Exception {
// Check that the given source files reformat EXACTLY as specified
reformatFileContents("testfiles/resolv.rb");
}
public void testFormatBegin() throws Exception {
// Test for http://scripting.netbeans.org/issues/show_bug.cgi?id=112259
// Check that the given source files reformat EXACTLY as specified
reformatFileContents("testfiles/begin.rb");
}
public void testFormatPostgres() throws Exception {
// Check that the given source files reformat EXACTLY as specified
reformatFileContents("testfiles/postgresql_adapter.rb");
}
public void testLineContinuationAsgn() throws Exception {
format("x =\n1",
"x =\n 1", null);
}
// Separate setting for line continuations not yet supported
//public void testLineContinuation2() throws Exception {
// format("x =\n1",
// "x =\n 1", new IndentPrefs(2,4));
//}
//
//public void testLineContinuation3() throws Exception {
// format("x =\n1\ny = 5",
// "x =\n 1\ny = 5", new IndentPrefs(2,4));
//}
public void testLineContinuation4() throws Exception {
format("def foo\nfoo\nif true\nx\nend\nend",
"def foo\n foo\n if true\n x\n end\nend", null);
}
public void testLineContinuation5() throws Exception {
format("def foo\nfoo\nif true\nx\nend\nend",
"def foo\n foo\n if true\n x\n end\nend", new IndentPrefs(4, 4));
}
// Trigger lexer bug!
//public void testLineContinuationBackslash() throws Exception {
// format("x\\\n= 1",
// "x\\\n = 1", new IndentPrefs(2,4));
//}
public void testLineContinuationComma() throws Exception {
format("render foo,\nbar\nbaz",
"render foo,\n bar\nbaz", null);
}
public void testQuestionmarkIndent1() throws Exception {
format("j = t ?\n1 : 0\nx = 1",
"j = t ?\n 1 : 0\nx = 1", null);
}
public void testQuestionmarkIndent2() throws Exception {
format("j = t ?\n1 :\n0\nx = 1",
"j = t ?\n 1 :\n 0\nx = 1", null);
}
public void testCommaIndent() throws Exception {
insertNewline("puts foo,^", "puts foo,\n ^", null);
}
public void testQuestionmarkIndent3() throws Exception {
insertNewline("j = t ?^",
"j = t ?\n ^", null);
}
public void testBackslashIndent() throws Exception {
insertNewline("puts foo\\^", "puts foo\\\n ^", null);
}
public void testDotIndent() throws Exception {
insertNewline("puts foo.^", "puts foo.\n ^", null);
}
public void testLineContinuationParens() throws Exception {
format("foo(1,2\n3,4)\nx",
"foo(1,2\n 3,4)\nx", null);
}
public void testLiterals() throws Exception {
format("def foo\n x = %q-foo\nbar-",
"def foo\n x = %q-foo\nbar-", null);
}
public void testLiterals2() throws Exception {
insertNewline("def foo\n=begin\nfoo^\n=end\nend",
"def foo\n=begin\nfoo\n^\n=end\nend", null);
}
public void testLiterals3() throws Exception {
insertNewline("def foo\nx = '\nfoo^\n'\nend",
"def foo\nx = '\nfoo\n^\n'\nend", null);
}
public void testLineContinuationAlias() throws Exception {
format("foo ==\ntrue",
"foo ==\n true", null);
format("alias foo ==\ntrue",
"alias foo ==\ntrue", null);
// Different hangingindent not yet supported in the UI
//format("def ==\ntrue",
// "def ==\n true", new IndentPrefs(2,4));
}
public void testBrackets() throws Exception {
format("x = [[5]\n]\ny",
"x = [[5]\n]\ny", null);
}
public void testBrackets2() throws Exception {
format("x = [\n[5]\n]\ny",
"x = [\n [5]\n]\ny", null);
// Different hangingindent not yet supported in the UI
//format("x = [\n[5]\n]\ny",
// "x = [\n [5]\n]\ny", new IndentPrefs(2,4));
}
public void testIndent1() throws Exception {
insertNewline("x = [^[5]\n]\ny", "x = [\n ^[5]\n]\ny", null);
}
public void testIndent2() throws Exception {
insertNewline("x = ^", "x = \n ^", null);
//insertNewline("x = ^", "x = \n ^", new IndentPrefs(2,4));
insertNewline("x = ^ ", "x = \n ^", null);
}
public void testIndent3() throws Exception {
insertNewline(" def foo^", " def foo\n ^\n end", null);
}
public void testIndentCurlyBraces() throws Exception {
insertNewline("{^}", "{\n ^\n}", null);
}
public void testIndentCurlyBraces2() throws Exception {
insertNewline("{ ^}", "{ \n ^\n}", null);
}
public void testHeredoc1() throws Exception {
format("def foo\n s = <<EOS\n stuff\nEOS\nend",
"def foo\n s = <<EOS\n stuff\nEOS\nend", null);
}
public void testHeredoc2() throws Exception {
format("def foo\n s = <<-EOS\n stuff\nEOS\nend",
"def foo\n s = <<-EOS\n stuff\n EOS\nend", null);
}
public void testHeredoc3() throws Exception {
format("def foo\n s = <<EOS\nstuff\n foo\nbar\nEOS\n end",
"def foo\n s = <<EOS\nstuff\n foo\nbar\nEOS\nend", null);
}
public void testHeredoc4() throws Exception {
format("def foo\n s = <<-EOS\nstuff\n foo\nbar\nEOS\n end",
"def foo\n s = <<-EOS\nstuff\n foo\nbar\n EOS\nend", null);
}
public void testArrayDecl() throws Exception {
format("@foo = [\n'bar',\n'bar2',\n'bar3'\n]",
"@foo = [\n 'bar',\n 'bar2',\n 'bar3'\n]", null);
}
public void testHashDecl() throws Exception {
String unformatted = "@foo = {\n" +
"'bar' => :foo,\n" +
"'bar2' => :bar,\n" +
"'bar3' => :baz\n" +
"}";
String formatted = "@foo = {\n" +
" 'bar' => :foo,\n" +
" 'bar2' => :bar,\n" +
" 'bar3' => :baz\n" +
"}";
format(unformatted, formatted, null);
}
public void testParenCommaList() throws Exception {
String unformatted = "foo(\nx,\ny,\nz\n)";
String formatted = "foo(\n" +
" x,\n" +
" y,\n" +
" z\n" +
")";
format(unformatted, formatted, null);
}
public void testDocumentRange1() throws Exception {
format(" def foo\n%<%foo%>%\n end\n",
" def foo\n foo\n end\n", null);
format("def foo\nfoo\nend\n",
"def foo\n foo\nend\n", null);
}
public void testDocumentRange2() throws Exception {
format("def foo\n if true\n %<%xxx%>%\n end\nend\n",
"def foo\n if true\n xxx\n end\nend\n", null);
}
public void testDocumentRange3() throws Exception {
format("class Foo\n def bar\n end\n\n\n%<%def test\nhello\nend%>%\nend\n",
"class Foo\n def bar\n end\n\n\n def test\n hello\n end\nend\n", null);
}
public void testPercentWIndent110983a() throws Exception {
insertNewline(
"class Apple\n def foo\n snark %w[a b c]^\n blah",
"class Apple\n def foo\n snark %w[a b c]\n ^\n blah", null);
}
public void testPercentWIndent110983b() throws Exception {
insertNewline(
"class Apple\n def foo\n snark %w,a b c,^\n blah",
"class Apple\n def foo\n snark %w,a b c,\n ^\n blah", null);
}
public void testPercentWIndent110983c() throws Exception {
insertNewline(
"class Apple\n def foo\n snark %w/a/^\n blah",
"class Apple\n def foo\n snark %w/a/\n ^\n blah", null);
}
public void testPercentWIndent110983d() throws Exception {
insertNewline(
"class Apple\n def foo\n snark %W[a b c]^\n blah",
"class Apple\n def foo\n snark %W[a b c]\n ^\n blah", null);
}
public void testPercentWIndent110983e() throws Exception {
insertNewline(
"class Apple\n def foo\n snark %Q[a b c]^\n blah",
"class Apple\n def foo\n snark %Q[a b c]\n ^\n blah", null);
}
public void testEof() throws Exception {
format("def foo\n if true\n %<%xxx%>%\n end\nend\n",
"def foo\n if true\n xxx\n end\nend\n", null);
format("x\n",
"x\n", null);
}
}