content: /*******************************************************************************
   * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
   * 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:
   *    Marc R. Hoffmann - initial API and implementation
   *    
   *******************************************************************************/
  package org.jacoco.core.internal.flow;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertTrue;
  
  import org.junit.Test;
  import org.objectweb.asm.ClassVisitor;
  import org.objectweb.asm.Label;
  import org.objectweb.asm.MethodVisitor;
  import org.objectweb.asm.Opcodes;
  
  /**
   * Unit tests for {@link ClassProbesAdapter}.
   */
  public class ClassProbesAdapterTest {
  
  	private static class MockMethodVisitor extends MethodProbesVisitor {
  
  		boolean frame = false;
  
  		@Override
  		public void visitJumpInsnWithProbe(int opcode, Label label,
  				int probeId, IFrame frame) {
  			frame.accept(this);
  		}
  
  		@Override
  		public void visitTableSwitchInsnWithProbes(int min, int max,
  				Label dflt, Label[] labels, IFrame frame) {
  			frame.accept(this);
  		}
  
  		@Override
  		public void visitLookupSwitchInsnWithProbes(Label dflt, int[] keys,
  				Label[] labels, IFrame frame) {
  			frame.accept(this);
  		}
  
  		@Override
  		public void visitFrame(int type, int nLocal, Object[] local,
  				int nStack, Object[] stack) {
  			frame = true;
  		}
  	}
  
  	private static class MockClassVisitor extends ClassProbesVisitor {
  
  		int count;
  
  		@Override
  		public MethodProbesVisitor visitMethod(int access, String name,
  				String desc, String signature, String[] exceptions) {
  			return null;
  		}
  
  		@Override
  		public void visitTotalProbeCount(int count) {
  			this.count = count;
  		}
  	}
  
  	@Test
  	public void testProbeCounter() {
  		final MockClassVisitor cv = new MockClassVisitor();
  		final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false);
  		assertEquals(0, adapter.nextId());
  		assertEquals(1, adapter.nextId());
  		assertEquals(2, adapter.nextId());
  		adapter.visitEnd();
  		assertEquals(3, cv.count);
  	}
  
  	@Test
  	public void testVisitClassMethods() {
  		final MockClassVisitor cv = new MockClassVisitor() {
  			@Override
  			public MethodProbesVisitor visitMethod(int access, String name,
  					String desc, String signature, String[] exceptions) {
  				return new MockMethodVisitor();
  			}
  		};
  		final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false);
  		adapter.visit(Opcodes.V1_5, 0, "Foo", null, "java/lang/Object", null);
  		writeMethod(adapter);
  		writeMethod(adapter);
  		writeMethod(adapter);
  
  		assertEquals(0, cv.count);
  		adapter.visitEnd();
  		assertEquals(3, cv.count);
  	}
  
  	@Test
  	public void testVisitMethodNullMethodVisitor() {
  		final MockClassVisitor cv = new MockClassVisitor();
  		final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false);
  		writeMethod(adapter); // 1 probe
  		writeMethodWithBranch(adapter); // 3 probes
  		writeMethodWithTableSwitch(adapter); // 3 probes
  		writeMethodWithLookupSwitch(adapter); // 3 probes
  		adapter.visitEnd();
  		assertEquals(10, cv.count);
  	}
  
  	@Test
  	public void testVisitWithFrames() {
  		final MockMethodVisitor mv = new MockMethodVisitor();
  		final MockClassVisitor cv = new MockClassVisitor() {
  			@Override
  			public MethodProbesVisitor visitMethod(int access, String name,
  					String desc, String signature, String[] exceptions) {
  				return mv;
  			}
  		};
  		final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, true);
  		writeMethodWithBranch(adapter);
  		adapter.visitEnd();
  		assertTrue(mv.frame);
  	}
  
  	@Test
  	public void testVisitWithoutFrames() {
  		final MockMethodVisitor mv = new MockMethodVisitor();
  		final MockClassVisitor cv = new MockClassVisitor() {
  			@Override
  			public MethodProbesVisitor visitMethod(int access, String name,
  					String desc, String signature, String[] exceptions) {
  				return mv;
  			}
  		};
  		final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false);
  		writeMethodWithBranch(adapter);
  		adapter.visitEnd();
  		assertFalse(mv.frame);
  	}
  
  	private void writeMethod(final ClassVisitor cv) {
  		MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null);
  		mv.visitCode();
  		mv.visitInsn(Opcodes.RETURN);
  		mv.visitMaxs(0, 1);
  		mv.visitEnd();
  	}
  
  	private void writeMethodWithBranch(final ClassVisitor cv) {
  		MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null);
  		mv.visitCode();
  		mv.visitInsn(Opcodes.ICONST_0);
  		Label l = new Label();
  		mv.visitJumpInsn(Opcodes.IFEQ, l);
  		mv.visitInsn(Opcodes.NOP);
  		mv.visitLabel(l);
  		mv.visitInsn(Opcodes.RETURN);
  		mv.visitMaxs(1, 1);
  		mv.visitEnd();
  	}
  
  	private void writeMethodWithTableSwitch(final ClassVisitor cv) {
  		MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null);
  		mv.visitCode();
  		mv.visitInsn(Opcodes.ICONST_0);
  		Label l1 = new Label();
  		Label l2 = new Label();
  		mv.visitTableSwitchInsn(0, 0, l1, new Label[] { l2 });
  		mv.visitLabel(l1);
  		mv.visitInsn(Opcodes.NOP);
  		mv.visitLabel(l2);
  		mv.visitInsn(Opcodes.RETURN);
  		mv.visitMaxs(1, 1);
  		mv.visitEnd();
  	}
  
  	private void writeMethodWithLookupSwitch(final ClassVisitor cv) {
  		MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null);
  		mv.visitCode();
  		mv.visitInsn(Opcodes.ICONST_0);
  		Label l1 = new Label();
  		Label l2 = new Label();
  		mv.visitLookupSwitchInsn(l1, new int[] { 0 }, new Label[] { l2 });
  		mv.visitLabel(l1);
  		mv.visitInsn(Opcodes.NOP);
  		mv.visitLabel(l2);
  		mv.visitInsn(Opcodes.RETURN);
  		mv.visitMaxs(1, 1);
  		mv.visitEnd();
  	}
  
  }
  