/*******************************************************************************
 * Copyright (c) 2005, 2006 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.validation.test.internal.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jem.util.logger.proxy.Logger;
import org.eclipse.jst.validation.test.BVTValidationException;
import org.eclipse.jst.validation.test.BVTValidationPlugin;
import org.eclipse.jst.validation.test.ValidationTypeEnum;
import org.eclipse.jst.validation.test.internal.registry.BVTValidationRegistryReader;
import org.eclipse.jst.validation.test.internal.registry.ITestcaseMetaData;
import org.eclipse.jst.validation.test.internal.registry.MessageMetaData;
import org.eclipse.jst.validation.test.internal.registry.MessageUtility;
import org.eclipse.jst.validation.test.internal.registry.OperationTestcase;
import org.eclipse.jst.validation.test.internal.registry.TestSetupImport;
import org.eclipse.jst.validation.test.internal.registry.TestcaseUtility;
import org.eclipse.jst.validation.test.internal.registry.ValidatorTestcase;
import org.eclipse.jst.validation.test.setup.IBuffer;
import org.eclipse.jst.validation.test.setup.ITestStatus;
import org.eclipse.wst.validation.internal.InternalValidatorManager;
import org.eclipse.wst.validation.internal.TaskListUtility;
import org.eclipse.wst.validation.internal.VThreadManager;
import org.eclipse.wst.validation.internal.ValidatorMetaData;
import org.eclipse.wst.validation.internal.operations.OneValidatorOperation;
import org.eclipse.wst.validation.internal.operations.ValidationOperation;
import org.eclipse.wst.validation.internal.plugin.ValidationPlugin;

/**
 */
public final class BVTRunner {
	private static BVTRunner _inst = null;
	private List _extra = null; // The extra markers reported (stored in a list to make reading easier).
	private List _missing = null; // The missing MMD that were supposed to be reported (stored in a list to make reading easier).

	public static BVTRunner singleton() {
		if(_inst == null) {
			_inst = new BVTRunner();
		}
		return _inst;
	}
	
	private BVTRunner() {
		super();
		_extra = new ArrayList();
		_missing = new ArrayList();
	}
	
	private boolean isVerbose() {
		Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
		if(logger.isLoggingLevel(Level.FINEST)) {
			return true;
		}
		return false;
	}

	private char getSeverityChar(Integer severity) {
		if(severity == null) {
			return '?';
		}
		
		switch(severity.intValue()) {
			case(IMarker.SEVERITY_ERROR): {
				return 'E';
			}
			
			case(IMarker.SEVERITY_WARNING): {
				return 'W';
			}
			
			case(IMarker.SEVERITY_INFO): {
				return 'I';
			}
			
			default: {
				return '?';
			}
		}
	}
	
	private String[] getNames(IProject[] projects) {
		if(projects == null) {
			return new String[0];
		}
		String[] names = new String[projects.length];
		for(int i=0; i<projects.length; i++) {
			IProject project = projects[i];
			names[i] = project.getName();
		}
		return names;
	}

	private void debug(IBuffer buffer, IMarker[] messages) {
		if(messages == null) {
			buffer.write("Nothing to debug because nothing was reported"); //$NON-NLS-1$
		}
		
		for(int i=0; i<messages.length; i++) {
			IMarker marker = messages[i];
			buffer.write(MessageUtility.toString(marker));
		}
	}

	protected IProgressMonitor getDebugMonitor(IProgressMonitor monitor) {
		if(isVerbose()) {
			return monitor;
		}
		else {
			return new NullProgressMonitor(); // if verbose is true, show progress messages for the creation & validation of the project. Otherwise show only the BVT status.
		}
	}
	
	/**
	 * When invoking the test from the UI, the input has already been imported, so just test it.
	 * The name of the test is derived from the name of the project.
	 * Return the number of tests that passed.
	 */	
	public int test(IBuffer buffer, IProject[] projects) throws BVTValidationException {	
		String[] testNames = getNames(projects);
		BVTValidationRegistryReader reader = BVTValidationRegistryReader.getReader();
		
		buffer.getProgressMonitor().beginTask("Running BVT tests", projects.length); //$NON-NLS-1$
		int totalTests = 0;
		int totalPassed = 0;
		for(int i=0; i<projects.length; i++) {
			buffer.getProgressMonitor().subTask("Testing project " + (i+1) + " of " + projects.length); //$NON-NLS-1$ //$NON-NLS-2$
			IProject project = projects[i];
			ValidatorTestcase[] tests = reader.getValidatorTests(buffer.getProgressMonitor(), testNames[i]);
			if((tests == null) || (tests.length == 0)) {
				buffer.getProgressMonitor().subTask("There are no tests to run."); //$NON-NLS-1$
				continue;
			}
			
			int numTestsPassed = 0;
			for(int j=0; j<tests.length; j++) {
				ValidatorTestcase tmd = tests[j];
				if(test(buffer, project, tmd, ValidationTypeEnum.RUN_VALIDATION)) {
					numTestsPassed++;
					buffer.getProgressMonitor().subTask("Test " + (j+1) + " of " + tests.length + " passed."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				}
				else {
					buffer.getProgressMonitor().subTask("Test " + (j+1) + " of " + tests.length + " failed."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				}
				buffer.getProgressMonitor().worked(1);
			}
			
			totalTests += tests.length;
			totalPassed += numTestsPassed;
			
			buffer.write(numTestsPassed + " of " + tests.length + " tests passed."); //$NON-NLS-1$ //$NON-NLS-2$
		}
		buffer.write("\n>>>>>>>>>>TEST RESULTS>>>>>>>>>>"); //$NON-NLS-1$
		buffer.write(totalPassed + " of " + totalTests + " tests passed."); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.getProgressMonitor().done();
		
		return totalPassed;
	}
	
	/**
	 * Construct a ValidationOperation on the IProject, invoke the operation, and verify that the
	 * resulting IMarkers match the expected messages in the tmd.
	 */	
	public boolean test(IBuffer buffer, IProject project, ValidatorTestcase tmd, int validationEnumType) throws BVTValidationException {
		switch(validationEnumType) {
			case(ValidationTypeEnum.RUN_VALIDATION): {
				return test(buffer, tmd.getName(), ValidationTypeEnum.RUN_VALIDATION_NAME, tmd.getMessages(validationEnumType), new OneValidatorOperation(project, tmd.getValidatorClass(), true, false), InternalValidatorManager.getManager().getValidatorNames(new ValidatorMetaData[]{tmd.getValidatorMetaData()})); // true=force, false=no for
			}
			
			case(ValidationTypeEnum.FULL_VALIDATION): {
				return build(buffer, project, tmd, validationEnumType, ValidationTypeEnum.FULL_VALIDATION_NAME);
			}
			
			case(ValidationTypeEnum.INCREMENTAL_VALIDATION): {
				return build(buffer, project, tmd, validationEnumType, ValidationTypeEnum.INCREMENTAL_VALIDATION_NAME);
			}
			
			case(ValidationTypeEnum.AUTO_VALIDATION): {
				return build(buffer, project, tmd, validationEnumType, ValidationTypeEnum.AUTO_VALIDATION_NAME);
			}
			
			default: {
				buffer.addExecutionStatus(tmd.getName(), null, ITestStatus.FAIL, buffer, "Unrecognized validation option:" + validationEnumType); //$NON-NLS-1$
				return false;
			}
		}
	}
	
	private boolean build(IBuffer buffer, final IProject project, final ValidatorTestcase tmd, final int validationEnumType, String subTaskName) throws BVTValidationException {
		try {
			IProgressMonitor nullMonitor = new NullProgressMonitor();
			ResourcesPlugin.getWorkspace().run(
			new IWorkspaceRunnable(){
				public void run(IProgressMonitor monitor) {
					try {
						IResource[] resources = tmd.getResources(project);
						for(int i=0; i<resources.length; i++) {
							IResource res = resources[i];
							res.touch(monitor);
						}
					}
					catch(CoreException exc) {
						Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
						if(logger.isLoggingLevel(Level.SEVERE)) {
							logger.write(exc);
						}
					}
				}
			}, nullMonitor);
		}
		catch(CoreException exc) {
			Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
			if(logger.isLoggingLevel(Level.SEVERE)) {
				logger.write(exc);
			}
			buffer.addExecutionStatus(tmd.getName(), subTaskName, ITestStatus.FAIL, buffer, "CoreException caught; stack trace in LoggingUtil.log"); //$NON-NLS-1$
			return false;
		}

		try {
			// Do not build using only the validation builder because some validators
			// (i.e., the EJB Validator) depend on the output of other builders 
			// (i.e., the Java Builder).
			project.build(validationEnumType, new NullProgressMonitor());
		}
		catch(CoreException exc) {
			Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
			if(logger.isLoggingLevel(Level.SEVERE)) {
				logger.write(exc);
			}
			buffer.write("CoreException caught; stack trace in LoggingUtil.log"); //$NON-NLS-1$
			return false;
		}

		IMarker[] messages = TaskListUtility.getValidationTasks(project, InternalValidatorManager.getManager().getValidatorNames(new ValidatorMetaData[]{tmd.getValidatorMetaData()}));
		return verify(buffer, tmd.getName(), subTaskName, project, tmd.getMessages(validationEnumType), messages);
	}
	
	/**
	 * Run the given ValidationOperation and ensure that the results match the results of the given test case.
	 * Return true if the results match and false if they don't.
	 * 
	 * This method is public because it's needed for the TestOpConstr operation.
	 */
	public boolean test(IBuffer buffer, String testName, String subTaskName, List expectedMessages, ValidationOperation op, String[] validatorNames) throws BVTValidationException {
		IProgressMonitor debugMonitor = getDebugMonitor(buffer.getProgressMonitor());
		boolean passed = true;
		try {
			
			if(op == null) {
				Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
				if(logger.isLoggingLevel(Level.WARNING)) {
					logger.write(Level.WARNING, "Cannot run tests because the Operation is null."); //$NON-NLS-1$
				}
				passed = false;
			}
			else {
				try {
						//ValidatorManager.getManager().setNoMessageLimit(op.getProject());
					
						ResourcesPlugin.getWorkspace().run(op, debugMonitor); 
						
						if(op.isFork()) {
							// Wait until all forked threads are complete
							while(!VThreadManager.getManager().isDone()){};
						}
			
						// Load the enabled validators from the test, not the operation,
						// because the test has the messages for a single validator, and
						// if the messages from all enabled validators are retrieved,
						// then "extra" messages, reported by validators whose messages
						// aren't expected by the test, fail the test case. 
						IMarker[] messages = TaskListUtility.getValidationTasks(op.getProject(), validatorNames);
						passed = verify(buffer, testName, subTaskName, op.getProject(), expectedMessages, messages);
				}
				catch(Throwable exc) {
					Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
					if(logger.isLoggingLevel(Level.SEVERE)) {
						logger.write(Level.SEVERE, exc);
					}
					passed = false;
				}
			}
		}
		catch(Throwable exc) {
			Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
			if(logger.isLoggingLevel(Level.SEVERE)) {
				logger.write(Level.SEVERE, exc);
			}
			passed = false;
		}
		return passed;
	}

	public boolean setupTests(IBuffer buffer, boolean verbose) {
		ValidatorTestcase[] vts = BVTValidationRegistryReader.getReader().getValidatorTests(buffer.getProgressMonitor(), (String)null);
		OperationTestcase[] ots = BVTValidationRegistryReader.getReader().getOperationTests(buffer.getProgressMonitor(), (String)null);
		boolean successful = true;
		for(int i=0; i<vts.length; i++) {
			ValidatorTestcase vtest = vts[i];
			successful = setupTests(buffer, vtest, false) && successful;
		}
		
		for(int j=0; j<ots.length; j++) {
			OperationTestcase otest = ots[j];
			successful = setupTests(buffer, otest, false) && successful;
		}
		
		return successful;
	}
	
	/**
	 * Return true if all of the test cases' input were found and imported.
	 */
	public boolean setupTests(IBuffer buffer, ITestcaseMetaData tmd, boolean verbose) {
		int executionMap = 0x0;
		boolean imported = true;
		try {
			BVTValidationRegistryReader reader = BVTValidationRegistryReader.getReader();
	
			// Each import operation deletes any project before it re-imports it.
			String dir = TestcaseUtility.getInputDir(tmd);
			TestSetupImport[] setup = reader.getTestSetup(buffer, dir, tmd, verbose);
			if((setup == null) || (setup.length == 0)) {
				String message = "Cannot import input that does not exist. Check that directory " + dir + " exists."; //$NON-NLS-1$ //$NON-NLS-2$ 
				buffer.write(message);
				executionMap |= 0x1;
				imported = false;
				ValidationPlugin.getPlugin().logMessage(IStatus.ERROR, message);
			}
			else {
				buffer.getProgressMonitor().beginTask("Importing files; please wait...", setup.length); //$NON-NLS-1$
				for(int i=0; i<setup.length; i++) {
					buffer.getProgressMonitor().subTask("Importing file " + (i+1) + " of " + setup.length); //$NON-NLS-1$ //$NON-NLS-2$
					if(!setup[i].importFile(buffer.getProgressMonitor(), dir)) {
						// One of the input files couldn't be imported.
						executionMap |= 0x2;
						imported = false;
						ValidationPlugin.getPlugin().logMessage(IStatus.ERROR, "Import failed for dir " + dir); //$NON-NLS-1$
						continue;
					}
					buffer.getProgressMonitor().worked(1);
				}
				buffer.getProgressMonitor().done();
			}
		}
		finally {
			if(!imported) {
				String tmdName = (tmd == null) ? "?" : tmd.getName(); //$NON-NLS-1$
				ValidationPlugin.getPlugin().logMessage(IStatus.ERROR, 
					"Test setup for " + tmdName + " was unsuccessful."); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		return imported;
	}

	/**
	 * Return true if the expected messages (tmdMmdList) matches the reported messages (ml2).
	 */
	public boolean compare(IBuffer buffer, String testName, String subTaskName, List tmdMmdList, IMarker[] ml2) {
		// Convert one of the arrays into a list so that it can be sorted.
		int numMatched = 0;
		List mmdList = new ArrayList(tmdMmdList); // create a copy of the tmd's expected output so that the tmd's output remains intact
		int numExpected = mmdList.size();
		int numReported = ml2.length;
		boolean passed = true;
	

		try {		
			Collections.sort(mmdList, MessageUtility.getMessageComparator(buffer, isVerbose()));
	
			try {
				for(int i=0; i<ml2.length; i++) {
					IMarker marker = ml2[i];
					MessageMetaData matching = getMatchingMetaData(buffer, mmdList, marker);
					if(matching == null) {
						_extra.add(marker);
						continue;
					}
					else {
						// Every time a mmd is matched to a marker, remove the mmd
						// from the list so that mmds without a marker can be flagged.
						mmdList.remove(matching);
						numMatched++;
					}
				}
	
				if(mmdList.size() != 0) {
					_missing.addAll(mmdList);
				}
			}
			catch(Throwable exc) {
				Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
				if(logger.isLoggingLevel(Level.SEVERE)) {
					logger.write(Level.SEVERE, exc);
				}
				passed = false;
			}
			finally {
				passed = passed && ((_missing.size() == 0) && (_extra.size() == 0));
				
				// Report the missing markers after the MMD list is sorted
				Collections.sort(_missing, MessageUtility.getMessageComparator(buffer, isVerbose()));
				Iterator iterator = _missing.iterator();
				while(iterator.hasNext()) {
					MessageMetaData mmd = (MessageMetaData)iterator.next();
					buffer.addExecutionStatus(testName, subTaskName, ITestStatus.FAIL, buffer, "ERROR[missing message]: " + MessageUtility.toString(mmd)); //$NON-NLS-1$
				}
				
				// Report the extra markers after they're sorted.
				Collections.sort(_extra, MessageUtility.getMessageComparator(buffer, isVerbose()));
				iterator = _extra.iterator();
				while(iterator.hasNext()) {
					IMarker marker = (IMarker)iterator.next();
					buffer.addExecutionStatus(testName, subTaskName, ITestStatus.FAIL, buffer, "ERROR[extra message]: " + MessageUtility.toString(marker) + ", Text is: \"" + MessageUtility.getMessage(marker) + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				}
		
				if(numExpected != numReported) {
					buffer.write("Wrong number of messages reported. Expected " + numExpected + " and got " + numReported); //$NON-NLS-1$ //$NON-NLS-2$
				}
	
				buffer.write("Number of extra messages: " + _extra.size()); //$NON-NLS-1$
				buffer.write("Number of missing messages: " + _missing.size()); //$NON-NLS-1$
				buffer.write("Number of matched messages: " + numMatched); //$NON-NLS-1$
			}
		}
		finally {
			mmdList.clear();
			_extra.clear(); // Clear the list of extra markers (stored in a list to make reading easier).
			_missing.clear(); // Clear the list of missing markers (stored in a list to make reading easier).
		}
		return passed;
	}
	
	public boolean verify(IBuffer buffer, String testName, String subTaskName, IProject project, List mmdListOrig, IMarker[] messages) {
		buffer.write("Test case: " + testName); //$NON-NLS-1$

		int numMatched = 0;
		int numExpected = 0;
		int numReported = 0;
		boolean passed = true;
		List mmdList = new ArrayList(mmdListOrig); // Need to modify the list, but do not want to modify the original, so create a copy.
		try {
			// First, check that the number of messages match.
			if(messages == null) {
				passed = false;
				Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
				if(logger.isLoggingLevel(Level.INFO)) {
					logger.write(Level.INFO, "No messages were reported."); //$NON-NLS-1$
				}
				return passed;
			}
			
			numExpected = mmdList.size();
			numReported = messages.length;
			
			Collections.sort(mmdList, MessageUtility.getMessageComparator(buffer, isVerbose()));

			for(int i=0; i<messages.length; i++) {
				IMarker marker = messages[i];
				MessageMetaData mmd = getMatchingMetaData(buffer, mmdList, marker);
	
				if(mmd == null) {
					_extra.add(marker);
					continue;
				}
				else {
					// Every time a mmd is matched to a marker, remove the mmd
					// from the list so that mmds without a marker can be flagged.
					mmdList.remove(mmd);
					if(isVerbose()) {
						buffer.write("matched " + MessageUtility.toString(marker) + " to " + MessageUtility.toString(mmd)); //$NON-NLS-1$ //$NON-NLS-2$
					}
					numMatched++;
				}
				
				// Check severity
				try {
					// Fail a test case based on severity mismatch because EJB deploy
					// relies on the severity to determine whether or not deployment
					// can continue?
					verifySeverity(buffer, testName, subTaskName, mmd, marker);
				}
				catch(CoreException e) {
					passed = false;
					Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
					if(logger.isLoggingLevel(Level.SEVERE)) {
						logger.write(Level.SEVERE, e);
					}
					buffer.write("ERROR[Testcase::" + testName + "]. CoreException caught while verifying severity"); //$NON-NLS-1$ //$NON-NLS-2$
					continue;
				}
				
				// Check resource
				try {
					// Don't fail a test case based on resource, because the "location" column will
					// uniquely identify the location of the message, anyway. Should still let the 
					// user know, though.
					verifyResource(buffer, testName, subTaskName, mmd, marker);
				}
				catch(CoreException e) {
					passed = false;
					buffer.write("ERROR[Testcase::" + testName + "]. CoreException caught while verifying resource"); //$NON-NLS-1$ //$NON-NLS-2$
					Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
					if(logger.isLoggingLevel(Level.SEVERE)) {
						logger.write(Level.SEVERE, e);
					}
					continue;
				}
				
				// Check location
				try {
					// Don't fail a test case based on location, because the line number calculation
					// is different in UI & batch, and the test cases are based on UI test results.
					// Should still alert the user, though.
					verifyLocation(buffer, testName, subTaskName,  mmd, marker);
				}
				catch(CoreException e) {
					passed = false;
					Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
					if(logger.isLoggingLevel(Level.SEVERE)) {
						logger.write(Level.SEVERE, e);
					}
					buffer.write("ERROR[Testcase::" + testName + "]. CoreException caught while verifying line number"); //$NON-NLS-1$ //$NON-NLS-2$
					continue;
				}
			}
			
			if(mmdList.size() != 0) {
				_missing.addAll(mmdList);
			}
		}
		catch(Throwable exc) {
			Logger logger = BVTValidationPlugin.getPlugin().getMsgLogger();
			if(logger.isLoggingLevel(Level.SEVERE)) {
				logger.write(Level.SEVERE, exc);
			}
			passed = false;
		}
		finally {
			if(!((_missing.size() == 0) && (_extra.size() == 0))) {
				passed = false;
			}
			
			if(passed) {
				buffer.write("PASS[Testcase::" + testName + "]"); //$NON-NLS-1$ //$NON-NLS-2$
				buffer.addExecutionStatus(testName, subTaskName, true); // If fail, will be reported in the extra & missing below.
			}
			else {
				buffer.write("FAIL[Testcase::" + testName + "]"); //$NON-NLS-1$ //$NON-NLS-2$

				// No point in listing the mmdList because it's empty by the time the finally block is reached
				if(isVerbose()) {
					buffer.write("List of markers reported:"); //$NON-NLS-1$
					debug(buffer, messages);
				}
			}
			
			// Report the missing markers after the MMD list is sorted
			Collections.sort(_missing, MessageUtility.getMessageComparator(buffer, isVerbose()));
			Iterator iterator = _missing.iterator();
			while(iterator.hasNext()) {
				MessageMetaData mmd = (MessageMetaData)iterator.next();
				buffer.addExecutionStatus(testName, subTaskName, ITestStatus.FAIL, buffer, "ERROR[missing message]: " + MessageUtility.toString(mmd)); //$NON-NLS-1$
			}
			
			// Report the extra markers after they're sorted.
			Collections.sort(_extra, MessageUtility.getMessageComparator(buffer, isVerbose()));
			iterator = _extra.iterator();
			while(iterator.hasNext()) {
				IMarker marker = (IMarker)iterator.next();
				buffer.addExecutionStatus(testName, subTaskName, ITestStatus.FAIL, buffer, "ERROR[extra message]: " + MessageUtility.toString(marker) + ", Text is: \"" + MessageUtility.getMessage(marker) + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			}
	
			if(numExpected != numReported) {
				buffer.write("Wrong number of messages reported. Expected " + numExpected + " and got " + numReported); //$NON-NLS-1$ //$NON-NLS-2$
			}

			buffer.write("Number of extra messages: " + _extra.size()); //$NON-NLS-1$
			buffer.write("Number of missing messages: " + _missing.size()); //$NON-NLS-1$
			buffer.write("Number of matched messages: " + numMatched); //$NON-NLS-1$
			buffer.write("End of test case: " + testName); //$NON-NLS-1$
			_extra.clear(); // Clear the list of extra markers (stored in a list to make reading easier).
			_missing.clear(); // Clear the list of missing markers (stored in a list to make reading easier).
		}
		return passed;
	}
	
	private int verifySeverity(IBuffer buffer, String testName, String subTaskName, MessageMetaData mmd, IMarker marker) throws CoreException {
		// If prefixSeverity is null, marker has no prefix, therefore it has no severity
		Integer prefixSeverity = MessageUtility.getSeverity(marker);
		if(prefixSeverity != null) {
			Integer severity = (Integer)marker.getAttribute(IMarker.SEVERITY); // IMarker.SEVERITY_ERROR, IMarker.SEVERITY_WARNING, IMarker.SEVERITY_INFO
			boolean severityEqual = true;
			if((severity == null) && (prefixSeverity == null)) {
				severityEqual = true;
			}
			else if(severity == null) {
				severityEqual = false;
			}
			else if(prefixSeverity == null) {
				severityEqual = false;
			}
			else {
				severityEqual = severity.equals(prefixSeverity);
			}
			
			if(!severityEqual) {
				buffer.addExecutionStatus(testName, subTaskName, ITestStatus.WARN, buffer, "WARNING[mismatching severity]: expected " + getSeverityChar(prefixSeverity) + ", got " + getSeverityChar(severity) + " on marker " + MessageUtility.toString(marker)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				return ITestStatus.WARN;
			}
		}
			
		return ITestStatus.PASS;
	}
	
	private int verifyResource(IBuffer buffer, String testName, String subTaskName, MessageMetaData mmd, IMarker marker) throws CoreException {
		String resource = MessageUtility.getResource(marker);
		if(!resource.equals(mmd.getResource())) {
			buffer.addExecutionStatus(testName, subTaskName, ITestStatus.WARN, buffer, "INFO[wrong resource]: expected " + mmd.getResource() + ", got " + resource + " on marker " + MessageUtility.toString(marker)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			return ITestStatus.WARN;
		}
		return ITestStatus.PASS;
	}
	
	private int verifyLocation(IBuffer buffer, String testName, String subTaskName, MessageMetaData mmd, IMarker marker) throws CoreException {
		// Either compare two line numbers or two text locations, but not a line number to a text location.
		if(mmd.isSetLineNumber()) {
			Integer lineNumber = MessageUtility.getLineNumber(marker);
			if(lineNumber == null) {
				// Marker has a text location, not a line number.
				return ITestStatus.PASS;
			}
			
			if(lineNumber.intValue() != mmd.getLineNumber()) {
				buffer.addExecutionStatus(testName, subTaskName, ITestStatus.WARN, buffer, "INFO[wrong location]: expected " + mmd.getLineNumber() + ", got " + lineNumber + " on marker " + MessageUtility.toString(marker)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				return ITestStatus.WARN;
			}
		}
		else if(mmd.isSetLocation()) {
			String location = MessageUtility.getLocation(marker);
			if(location == null) {
				// marker has a line number, not a text location
				return ITestStatus.PASS;
			}
			
			if(!(mmd.getLocation().equals(location))) {
				buffer.addExecutionStatus(testName, subTaskName, ITestStatus.WARN, buffer, "INFO[wrong location]: expected " + mmd.getLocation() + ", got " + location + " on marker " + MessageUtility.toString(marker)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				return ITestStatus.WARN;
			}
		}
		
		return ITestStatus.PASS;
	}
	
	
	private MessageMetaData getMatchingMetaData(IBuffer buffer, List mmdList, IMarker marker) {
		int result = Collections.binarySearch(mmdList, marker, MessageUtility.getMessageComparator(buffer, isVerbose()));
		if(result < 0) {
			return null;
		}
		return (MessageMetaData)mmdList.get(result);
	}
}