ruby/test/unit/src/org/netbeans/modules/ruby/RubyTypeAnalyzerTest.java
author enebo@netbeans.org
Sun, 08 Dec 2013 11:39:16 -0600
changeset 4554 07958c1ff402
parent 4117 6e1f647524a5
permissions -rw-r--r--
Too much stuff in one commit. Rename blocks now follows language semantics. Removal of more ASTPath consumers
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2009 Sun
    31  * Microsystems, Inc. All Rights Reserved.
    32  *
    33  * If you wish your version of this file to be governed by only the CDDL
    34  * or only the GPL Version 2, indicate your decision by adding
    35  * "[Contributor] elects to include this software in this distribution
    36  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    37  * single choice of license, a recipient has the option to distribute
    38  * your version of this file under either the CDDL, the GPL Version 2 or
    39  * to extend the choice of license to its licensees as provided above.
    40  * However, if you add GPL Version 2 code and therefore, elected the GPL
    41  * Version 2 license, then the option applies only if the new code is
    42  * made subject to such option by the copyright holder.
    43  */
    44 package org.netbeans.modules.ruby;
    45 
    46 import java.util.Map;
    47 import org.jrubyparser.ast.MethodDefNode;
    48 import org.jrubyparser.ast.Node;
    49 import org.netbeans.api.java.classpath.ClassPath;
    50 import org.netbeans.editor.BaseDocument;
    51 import org.netbeans.modules.csl.spi.ParserResult;
    52 import org.netbeans.modules.parsing.api.Source;
    53 import org.netbeans.modules.ruby.options.TypeInferenceSettings;
    54 import org.openide.filesystems.FileObject;
    55 
    56 /**
    57  * @todo Test compound assignment:  x = File::Stat.new
    58  *
    59  * @author Tor Norbye
    60  */
    61 public class RubyTypeAnalyzerTest extends RubyTestBase {
    62 
    63     public RubyTypeAnalyzerTest(String testName) {
    64         super(testName);
    65         RubyIndexer.userSourcesTest = true;
    66         TypeInferenceSettings.getDefault().setMethodTypeInference(true);
    67         TypeInferenceSettings.getDefault().setRdocTypeInference(true);
    68     }
    69 
    70     @Override
    71     protected Map<String, ClassPath> createClassPathsForTest() {
    72         return rubyTestsClassPath();
    73     }
    74 
    75     private RubyTypeInferencer getInferencer(String file, String caretLine, boolean findMethod) throws Exception {
    76         FileObject fo = getTestFile("testfiles/" + file);
    77 
    78         int caretOffset = -1;
    79         if (caretLine != null) {
    80             Source source = Source.create(fo);
    81             caretOffset = getCaretOffset(source.createSnapshot().getText().toString(), caretLine);
    82             enforceCaretOffset(source, caretOffset);
    83         }
    84 
    85         ParserResult parserResult = getParserResult(fo);
    86         Node root = AstUtilities.getRoot(parserResult);
    87         initializeRegistry();
    88         RubyIndex index = RubyIndex.get(parserResult);
    89         Node node = root.getNodeAt(caretOffset);
    90 
    91         if (findMethod) {
    92             MethodDefNode method = AstUtilities.findMethodAtOffset(root, caretOffset);
    93             assertNotNull(method);
    94             root = method;
    95         }
    96 
    97         ContextKnowledge knowledge = new ContextKnowledge(index, root, node, caretOffset, caretOffset, parserResult);
    98         return RubyTypeInferencer.create(knowledge);
    99     }
   100 
   101     private void assertTypes(final RubyType actualTypes, final String... expectedTypes) {
   102         assertTypes(null, actualTypes, expectedTypes);
   103     }
   104 
   105     private void assertTypes(final String message, final RubyType actualTypes, final String... expectedTypes) {
   106         assertTypes(message, actualTypes, false, expectedTypes);
   107     }
   108 
   109     private void assertTypes(final String message, final RubyType actualTypes,
   110             final boolean hasUnknownMember, final String... expectedTypes) {
   111         RubyType expected = new RubyType(expectedTypes);
   112         if (hasUnknownMember) {
   113             expected.append(RubyType.unknown());
   114         }
   115         assertTrue(message + ":" +
   116                 "\n  actualTypes:   " + actualTypes +
   117                 "\n  expectedTypes: " + expected, actualTypes.equals(expected));
   118     }
   119 
   120     private void assertTypes(String relFilePath, String matchingLine,
   121             String exprToInfer, String... expectedTypes) throws Exception {
   122         assertTypes(relFilePath, matchingLine, exprToInfer, false, expectedTypes);
   123     }
   124     private void assertTypes(String relFilePath, String matchingLine,
   125             String exprToInfer, boolean hasUnknownMember, String... expectedTypes) throws Exception {
   126         RubyTypeInferencer instance = getInferencer(relFilePath, matchingLine, false);
   127         assertTypes("Types correctly inferred", instance.inferType(exprToInfer), hasUnknownMember, expectedTypes);
   128     }
   129 
   130     public void testGetType() throws Exception {
   131         RubyTypeInferencer instance = getInferencer("types.rb", " l^oc = {", false);
   132 
   133         assertTypes(instance.inferType("x"), "Integer");
   134         // y is reassigned later in the file - make sure that at this
   135         // point in scope we have the right type
   136         assertTypes(instance.inferType("y"), "File");
   137         assertTypes(instance.inferType("$baz"), "Hash");
   138         assertTypes(instance.inferType("@bar"), "Fixnum");
   139         assertTypes(instance.inferType("@foo"), "Array");
   140     }
   141 
   142     public void testGetType2() throws Exception {
   143         RubyTypeInferencer instance = getInferencer("types.rb", " # d^one", false);
   144 
   145         // Y is assigned different types - make sure that at this position, it's a number
   146         assertTypes(instance.inferType("y"), "Fixnum");
   147         // Lots of reassignments - track types through vars, statics, fields, classvars
   148         assertTypes(instance.inferType("loc"), "Hash");
   149         assertTypes(instance.inferType("$glob"), "Hash");
   150         assertTypes(instance.inferType("@field"), "Hash");
   151         assertTypes(instance.inferType("@@clsvar"), "Hash");
   152         assertTypes(instance.inferType("loc2"), "Hash");
   153     }
   154 
   155     public void testMultipleAssigments() throws Exception {
   156         RubyTypeInferencer instance = getInferencer("types.rb", " # d^one", false);
   157         assertTypes(instance.inferType("q"), "Fixnum");
   158         assertTypes(instance.inferType("w"), "Fixnum");
   159         assertTypes(instance.inferType("e"), "String");
   160         assertTypes(instance.inferType("@r"), "String");
   161         assertTypes(instance.inferType("@t"), "Fixnum");
   162     }
   163 
   164     public void testNestedMultipleAssigments() throws Exception {
   165         RubyTypeInferencer instance = getInferencer("types.rb", " # d^one", false);
   166         assertTypes(instance.inferType("u"), "String");
   167         assertTypes(instance.inferType("i"), "Fixnum");
   168         assertTypes(instance.inferType("o"), "Float");
   169         assertTypes(instance.inferType("p"), "File");
   170     }
   171 
   172     public void testTypeAssertions() throws Exception {
   173         RubyTypeInferencer instance = getInferencer("types.rb", " l^oc = {", true);
   174         assertTypes(instance.inferType("param1"), "String");
   175         assertTypes(instance.inferType("param2"), "Hash");
   176     }
   177 
   178     public void testBegin() throws Exception {
   179         RubyTypeInferencer instance = getInferencer("types2.rb", " @f^iles = ARGV.dup", false);
   180         assertTypes(instance.inferType("go"), "GetoptLong");
   181     }
   182 
   183     public void testRailsController() throws Exception {
   184         assertTypes("type_controller.rb", "^end", "request", "ActionController::CgiRequest");
   185         RubyTypeInferencer instance = getInferencer("type_controller.rb", "^end", false);
   186         assertTypes(instance.inferType("request"), "ActionController::CgiRequest");
   187     }
   188 
   189 // This test doesn't work; the behavior works in the IDE but the
   190 // Lucene index isn't returning local symbols in the testing framework yet
   191 //    public void testComplex1() throws Exception {
   192 //        RubyTypeInferencer instance = getAnalyzer("types3.rb", "^caret", false);
   193 //        assertEquals("Product", instance.getType("@product"));
   194 //    }
   195 
   196 //    public void testComplex2() throws Exception {
   197 //        RubyTypeInferencer instance = getAnalyzer("types3.rb", "^caret", true);
   198 //        assertEquals("ActiveRecord::ConnectionAdapters::TableDefinition", instance.getType("t"));
   199 //    }
   200 
   201     //public void testComplex3() throws Exception {
   202     //    // XXX TODO
   203     //    assertFalse("Check that I do closures for each, collect, map, etc.", true);
   204     //    // also check to_s
   205     //}
   206     // TODO: Make sure I can handle compound expressions like this one:
   207     //  Product.find(params[:id]).destroy
   208     public void testMigrationType() throws Exception {
   209         RubyTypeInferencer instance = getInferencer("migrate/20080726182725_create_posts.rb", " t.^time", true);
   210         assertTypes(instance.inferType("t"), "ActiveRecord::ConnectionAdapters::TableDefinition");
   211     }
   212 
   213     public void testIfType() throws Exception {
   214         assertTypes("if_type.rb", "p va^r.in", "var", "String", "NilClass");
   215     }
   216 
   217     public void testIfElseType() throws Exception {
   218         assertTypes("if_else_type.rb", "p va^r.in", "var", "String", "Array");
   219     }
   220 
   221     public void testIfElseType2() throws Exception {
   222         assertTypes("if_else_type_2.rb", "p va^r.in", "var", "String", "NilClass");
   223     }
   224 
   225     public void testIfElseWithInBlockReassignmentType() throws Exception {
   226         assertTypes("if_else_with_block_reassignment_type.rb", "p va^r.in", "var", "Hash", "Array");
   227     }
   228 
   229     public void testIfElseIfElseType() throws Exception {
   230         assertTypes("if_elsif_else_type.rb", "p va^r.in", "var", "String", "Array", "Hash");
   231     }
   232 
   233     public void testUnlessType() throws Exception {
   234         assertTypes("unless_type.rb", "var.i^", "var", "Array", "Hash");
   235     }
   236 
   237     public void testForType() throws Exception {
   238         assertTypes("for_type.rb", "tmp^ = i", "i", false, "Fixnum");
   239         assertTypes("for_type.rb", "s^ = j", "j", false, "String");
   240     }
   241 
   242     public void testEqualsType() throws Exception {
   243         assertTypes("equals_type.rb", "var.t^", "var", "TrueClass", "FalseClass");
   244         assertTypes("equals_type.rb", "var2.t^", "var2", "TrueClass", "FalseClass");
   245     }
   246 
   247     public void testIfWithFailingInferenceInBranchType() throws Exception {
   248         assertTypes("if_with_failing_inference_in_branch_type.rb", "var.to_^", "var", true, "NilClass");
   249     }
   250 
   251     // TODO inference is still not able to do the below
   252     public void FIXME_testIfElseNestedSimpleType() throws Exception {
   253         assertTypes("if_else_nested_simple_type.rb", "var.^ifcond1b", "var", "Float");
   254         assertTypes("if_else_nested_simple_type.rb", "var.^aa", "var", "NilClass", "Float");
   255     }
   256 
   257     // TODO inference is still not able to do the below
   258     public void FIXME_testIfElseNestedType() throws Exception {
   259         assertTypes("if_else_nested_type.rb", "va^r.ifcond2", "var", "Hash");
   260         // XXX more, see the if_else_nested_type.rb
   261     }
   262 
   263     public void testConstant() throws Exception {
   264         assertTypes("constants.rb", "Colors::RED.byte^", "Colors::RED", "String");
   265         assertTypes("constants.rb", "puts b.down^case", "b", "String");
   266         // TODO fix and uncomment when reindexed
   267         // assertTypes("indexed constants type inference", "constants.rb", "REXML::COPY^RIGHT", "COPYRIGHT", "String");
   268     }
   269 
   270     public void testMethodType() throws Exception {
   271         assertTypes("method_type_inference.rb", "puts n^um.abs", "num", "Fixnum");
   272         assertTypes("method_type_inference.rb", "puts cons^t", "const", "Fixnum");
   273     }
   274 
   275 //    public void testCoreMethodType() throws Exception {
   276 //        assertTypes("core_methods.rb", "ance^stors.delete(String)", "ancestors", "Array");
   277 //        assertTypes("core_methods.rb", "puts has_^one.t", "has_one", "TrueClass", "FalseClass");
   278 //        assertTypes("core_methods.rb", "huh = a.eq^l?(123)", "a", "Fixnum", "Numeric");
   279 //    }
   280 
   281     public void testMethodsChaining() throws Exception {
   282         assertTypes("methods_chaining.rb", "puts gree^ting", "greeting", "String");
   283     }
   284 }