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