/*******************************************************************************
 * Copyright (c) 2011, 2012 Anton Gorenkov
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Anton Gorenkov - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.testsrunner.internal.boost;

import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProvider;
import org.eclipse.cdt.testsrunner.model.ITestModelUpdater;
import org.eclipse.cdt.testsrunner.model.TestingException;
import org.xml.sax.SAXException;

/**
 * The Tests Runner provider plug-in to run tests with Boost.Test framework.
 *
 * Configures the test module to output in XML format, parses the output and
 * provides the data for the Tests Runner Core.
 *
 * @note There is a note about Tests Runner provider plug-in accuracy. Communication
 * between Boost test module and Boost Tests Runner provider plug-in is done through
 * standard output (which is buffered by default). Boost.Test (at least current
 * version - 1.48.0) does not provide a way to flush the data about tests
 * execution when they are available, so there may be a delay between test event
 * happening (e.g. test case is started) and its displaying in the results view.
 * The possible solution is to turn off the standard output buffering like this:
 * <pre>
 *     static struct DisableStdCoutBuffering
 *     {
 *         DisableStdCoutBuffering()
 *         {
 *             std::cout.setf(std::ios_base::unitbuf);
 *         }
 *     } s_disableStdCoutBuffering;
 * </pre>
 * It will make the results view showing progress more accurate.
 */
public class BoostTestsRunnerProvider implements ITestsRunnerProvider {

	@Override
	public String[] getAdditionalLaunchParameters(String[][] testPaths) throws TestingException {
		final String[] boostParameters = { "--output_format=XML", //$NON-NLS-1$
				"--log_level=all", //$NON-NLS-1$
				"--report_level=no" //$NON-NLS-1$
		};
		String[] result = boostParameters;

		// Build tests filter
		if (testPaths != null && testPaths.length >= 1) {
			if (testPaths.length != 1) {
				throw new TestingException(BoostTestsRunnerMessages.BoostTestsRunner_wrong_tests_paths_count);
			}
			StringBuilder sb = new StringBuilder("--run_test="); //$NON-NLS-1$
			String[] testPath = testPaths[0];
			for (int i = 1; i < testPath.length; i++) {
				if (i != 1) {
					sb.append("/"); //$NON-NLS-1$
				}
				sb.append(testPath[i]);
			}
			result = new String[boostParameters.length + 1];
			System.arraycopy(boostParameters, 0, result, 0, boostParameters.length);
			result[boostParameters.length] = sb.toString();
		}
		return result;
	}

	/**
	 * Construct the error message from prefix and detailed description.
	 *
	 * @param prefix prefix
	 * @param description detailed description
	 * @return the full message
	 */
	private String getErrorText(String prefix, String description) {
		return MessageFormat.format(BoostTestsRunnerMessages.BoostTestsRunner_error_format, prefix, description);
	}

	@Override
	public void run(ITestModelUpdater modelUpdater, InputStream inputStream) throws TestingException {
		try {
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			sp.parse(inputStream, new BoostXmlLogHandler(modelUpdater));

		} catch (IOException e) {
			throw new TestingException(
					getErrorText(BoostTestsRunnerMessages.BoostTestsRunner_io_error_prefix, e.getLocalizedMessage()));

		} catch (NumberFormatException e) {
			throw new TestingException(
					getErrorText(BoostTestsRunnerMessages.BoostTestsRunner_xml_error_prefix, e.getLocalizedMessage()));

		} catch (ParserConfigurationException e) {
			throw new TestingException(
					getErrorText(BoostTestsRunnerMessages.BoostTestsRunner_xml_error_prefix, e.getLocalizedMessage()));

		} catch (SAXException e) {
			throw new TestingException(
					getErrorText(BoostTestsRunnerMessages.BoostTestsRunner_xml_error_prefix, e.getLocalizedMessage()));
		}
	}

}
