Package jmockit.assist

Source Code of jmockit.assist.MockASTVisitor

/*
* Copyright (c) 2012 Andrejs Jermakovics.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Andrejs Jermakovics - initial implementation
*/
package jmockit.assist;

import static org.eclipse.jdt.core.dom.Modifier.isPrivate;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;

@SuppressWarnings("restriction")
public final class MockASTVisitor extends ASTVisitor
{
  private final ICompilationUnit icunit;
  private CompilationUnit cu;
  private boolean hasMockitImport = false;

  private final List<CategorizedProblem> probs = new ArrayList<CategorizedProblem>();

  public MockASTVisitor(final ICompilationUnit cunitPar)
  {
    icunit = cunitPar;
  }

  @Override
  public boolean visit(final CompilationUnit node)
  {
    cu = node;
    return true;
  }

  @Override
  public void endVisit(final CompilationUnit node)
  {
    super.endVisit(node);
  }

  @Override
  public boolean visit(final MethodDeclaration node)
  {
    IMethodBinding meth = node.resolveBinding();
    ITypeBinding mockedType = MockUtil.findMockedType(node, meth); // new MockUp< MockedType >

    if ( mockedType != null )
    {
      boolean hasMockAnn = MockUtil.isMockMethod(meth);
      boolean methodExists = findRealMethod(node, meth, mockedType) != null;

      if (!hasMockAnn && methodExists )
      {
        addMarker(node.getName(), "Mocked method missing @Mock annotation", false);
      }

      if (hasMockAnn && !methodExists )
      {
        addMarker(node.getName(), "Mocked real method not found in type", true);
      }

      if (hasMockAnn && methodExists && isPrivate(meth.getModifiers()))
      {
        addMarker(node.getName(), "Mocked method should not be private", true);
      }
    }

    return true;
  }

  public IMethodBinding findRealMethod(final MethodDeclaration node,
      final IMethodBinding meth, final ITypeBinding mockedType)
  {
    IMethodBinding origMethod = null;

    if (mockedType != null)
      origMethod = MockUtil.findRealMethodInType(mockedType, new InvocationFilter(meth), node.getAST());

    return origMethod;
  }

  @Override
  public boolean visit(final AnonymousClassDeclaration node)
  {
    ITypeBinding binding = node.resolveBinding();

    if ( MockUtil.isMockUpType(binding.getSuperclass()) ) // new MockUp< type >
    {
      ITypeBinding typePar = ASTUtil.getFirstTypeParameter(node);
      ASTNode parent = node.getParent();

      if (parent instanceof ClassInstanceCreation && typePar.isInterface()  ) // creating interface mock
      {
        ClassInstanceCreation creation = (ClassInstanceCreation) parent;

        visitMockUpCreation(creation);
      }

    }

    return true;
  }

  public void visitMockUpCreation(final ClassInstanceCreation creation)
  {
    ASTNode gparent = creation.getParent();

    Type typeNode = creation.getType();
    boolean invokesGetInst = false;

    if (gparent instanceof MethodInvocation) // method invocation follows
    {
      MethodInvocation inv = (MethodInvocation) gparent;
      IMethodBinding meth = inv.resolveMethodBinding();

      if ( MockUtil.GET_INST.equals(meth.getName()))
      {
        invokesGetInst = true;
        if (gparent.getParent() instanceof ExpressionStatement)
        {
          addMarker(inv.getName(), "Returned mock instance is not being used.", false);
        }
      }
    }

    if( !invokesGetInst )
    {
      addMarker(typeNode, "Missing call to getMockInstance() on MockUp of interface",
          false);
    }
  }

  @Override
  public boolean visit(final MethodInvocation node)
  {
    IMethodBinding meth = node.resolveMethodBinding();

    if (meth == null)
    {
      return false;
    }

    if ( MockUtil.isMockUpType(meth.getDeclaringClass()) && MockUtil.GET_INST.equals(meth.getName()))
    {
      ITypeBinding returnType = node.resolveTypeBinding();

      if (!returnType.isInterface())
      {
        String msg = "getMockInstance() used on mock of class " + returnType.getName()
            + ". Use on interfaces";
        addMarker(node.getName(), msg, false);
      }
    }

    visitItFieldInvocation(node, meth);

    return true;
  }

  // consider visit(FieldAccess )
  private void visitItFieldInvocation(final MethodInvocation node, final IMethodBinding meth)
  {
    if (node.getExpression() instanceof SimpleName)
    {
      SimpleName var = (SimpleName) node.getExpression();
      String varName = var.getIdentifier();

      IBinding exprBinding = var.resolveBinding();

      if ( "it".equals(varName) && exprBinding instanceof IVariableBinding ) // call on 'it' field
      {
        IVariableBinding varBinding = (IVariableBinding) exprBinding;
        IMethodBinding mockMethod = getSurroundingMockMethod(node);

        if( mockMethod != null && varBinding.isField() && mockMethod.isSubsignature(meth)
            && varBinding.getDeclaringClass().equals(mockMethod.getDeclaringClass()) )
        {
          ITypeBinding mockedType = MockUtil.findMockedType(node);

          if( mockedType.equals(varBinding.getType()) // field type is correct mocked type
              && !MockUtil.isReentrantMockMethod(mockMethod) )
          {
            addMarker( node,
                "Method calls itself. Set @Mock(reentrant=true) on method '"
                    + mockMethod.getName() + "'", true);
          }
        }
      }
    }
  }

  private IMethodBinding getSurroundingMockMethod(final MethodInvocation node)
  {
    MethodDeclaration surroundingMeth = ASTUtil.findAncestor(node, MethodDeclaration.class);
    IMethodBinding mockMethod = null;

    if (surroundingMeth != null)
    {
      mockMethod = surroundingMeth.resolveBinding();
    }

    if( mockMethod != null && MockUtil.isMockMethod(mockMethod) )
    {
      return mockMethod;
    }
    else
    {
      return null;
    }
  }

  @Override
  public boolean visit(final FieldDeclaration node)
  {
    //add checks:
    // - 'it' field should have the correct type
    // - it field should not be private
    // - adding other fields to a  mock - warning about shared state?

    //      for(Object fragment: node.fragments())
    //      {
    //        if(fragment instanceof VariableDeclarationFragment)
    //        {
    //          VariableDeclarationFragment varDec = (VariableDeclarationFragment) fragment;
    //
    //          if( "it".equals(varDec.getName().getIdentifier()) )
    //          {
    //            IVariableBinding ivar = varDec.resolveBinding();
    //
    //            if( ASTUtil.isMockUpType(ivar.getDeclaringClass().getSuperclass()) )
    //            {
    //            }
    //          }
    //        }
    //      }

    return true;
  }

  @Override
  public boolean visit(final TypeDeclaration node)
  {
    return hasMockitImport;
  }

  @Override
  public boolean visit(final ImportDeclaration node)
  {
    String importName = node.getName().getFullyQualifiedName();
    hasMockitImport = hasMockitImport || importName.startsWith("mockit.Mock");
    return true;
  }

  private void addMarker(final ASTNode node, final String msg, final boolean isError)
  {
    try
    {
      int start = node.getStartPosition();
      int endChar = start + node.getLength();
      int line = cu.getLineNumber(start);
      int col = cu.getColumnNumber(start);
      int id = IProblem.TypeRelated;
      String filePath = icunit.getPath().toOSString();
      String[] args = new String[]{};

      int severity = isError ? ProblemSeverities.Error : ProblemSeverities.Warning;

      CategorizedProblem problem = new DefaultProblem(filePath.toCharArray(), msg, id,
          args, severity, start, endChar, line, col)
      {
        @Override
        public String getMarkerType()
        {
          return JMockitCompilationParticipant.MARKER;
        }
      };

      probs.add(problem);
    }
    catch (Exception e)
    {
      Activator.log(e);
    }
  }

  public CategorizedProblem[] getProblems()
  {
    return probs.toArray(new CategorizedProblem[]{});
  }

}
TOP

Related Classes of jmockit.assist.MockASTVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.