All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.etlunit.feature.file.FileAssertExtender Maven / Gradle / Ivy

There is a newer version: 1.6.9
Show newest version
package org.etlunit.feature.file;

import com.google.inject.name.Named;
import org.etlunit.*;
import org.etlunit.context.VariableContext;
import org.etlunit.feature.Feature;
import org.etlunit.feature.RuntimeOption;
import org.etlunit.feature.extend.Extender;
import org.etlunit.io.FileBuilder;
import org.etlunit.io.file.*;
import org.etlunit.json.validator.ResourceNotFoundException;
import org.etlunit.parser.ETLTestOperation;
import org.etlunit.parser.ETLTestValueObject;
import org.etlunit.util.IOUtils;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.*;

public class FileAssertExtender implements Extender, AssertOperationProcessor
{
	private final FileFeatureModule fileFeatureModule;
	private FileRuntimeSupport fileRuntimeSupport;
	private RuntimeSupport runtimeSupport;

	private DiffManager diffManager;
	private Log applicationLog;

	private DataFileManager dataFileManager;

	private boolean refreshAssertionData = false;

	public FileAssertExtender(FileFeatureModule fileFeatureModule)
	{
		this.fileFeatureModule = fileFeatureModule;
	}

	@Inject
	public void receiveDataFileManager(DataFileManager dfm)
	{
		dataFileManager = dfm;
	}

	@Inject
	public void receiveRuntimeSupport(RuntimeSupport rs)
	{
		runtimeSupport = rs;
	}

	@Inject
	public void receiveApplicationLog(@Named("applicationLog") Log log)
	{
		this.applicationLog = log;
	}

	@Inject
	public void setRefreshAssertionData(@Named("file.refreshAssertionData") RuntimeOption option)
	{
		refreshAssertionData = option.isEnabled();

		applicationLog.info(refreshAssertionData ? "File Module refreshing assertion data" : "File Module not refreshing assertion data");
	}

	@Inject
	public void receiveFileRuntimeSupport(FileRuntimeSupport support)
	{
		fileRuntimeSupport = support;
	}

	@Inject
	public void receiveDiffManager(DiffManager reporter)
	{
		diffManager = reporter;
	}

	public boolean canHandleRequest(ETLTestOperation operation, ETLTestValueObject operands, VariableContext context, ExecutionContext executionContext)
	{
		// this module requires a file-equals operation
		return operands.query("file-equals") != null;
	}

	public Feature getFeature()
	{
		return fileFeatureModule;
	}

	public action_code processAssert(AssertOperation operation, ETLTestOperation op, ETLTestValueObject obj, VariableContext context, ExecutionContext econtext) throws TestAssertionFailure, TestExecutionError, TestWarning
	{
		ETLTestValueObject fcontext = FileFeatureModule.getFileContextContainer(obj, context);

		String fileName = operation.getFile();

		File assertionSource = fileRuntimeSupport.getAssertionFile(op.getTestMethod().getTestClass().getPackage(), fileName);

		String targetFileName = operation.getTargetFileName();

		if (targetFileName == null)
		{
			targetFileName = fileName;
		}

		FileProducer fp = fileRuntimeSupport.getRegisteredProducer(operation.getProducer());

		File target = fp.locateFileWithClassifierAndContext(targetFileName, operation.getClassifier(), operation.getContext());

		// check for the operation type:
		AssertOperation.assertionMode_enum assertionMode = operation.getAssertionMode();

		if (assertionMode == null)
		{
			assertionMode = AssertOperation.assertionMode_enum.equals;
		}

		switch (assertionMode)
		{
			case equals:
				// this is the usual case and is handled outside this switch
				break;
			case exists:
				// target file must exist.  It's a failure if it does not.
				if (!target.exists())
				{
					String failureId = FileConstants.FAIL_FILE_NOT_EXIST;

					if (operation.getFailureId() != null)
					{
						failureId = operation.getFailureId();
					}

					throw new TestAssertionFailure(target.getAbsolutePath(), failureId);
				}

				return ClassResponder.action_code.handled;
			case notExists:
				// target file must not exist.  It's a failure if it does.
				if (target.exists())
				{
					String failureId = FileConstants.FAIL_FILE_EXISTS;

					if (operation.getFailureId() != null)
					{
						failureId = operation.getFailureId();
					}

					throw new TestAssertionFailure(target.getAbsolutePath(), failureId);
				}

				return ClassResponder.action_code.handled;
			case empty:
				// target file must exist and be empty.  It's a failure if it does not exist or contains data.
			case notEmpty:
				// target file must exist and contain data.  It's a failure if it does not exist or does not contain data.
				simpleCheckContents(target, obj, operation, assertionMode, op, context);

				return ClassResponder.action_code.handled;
		}

		AssertOperation.columnListMode_enum columnListMode = operation.getColumnListMode();

		List actualList = new ArrayList();
		List list = operation.getColumnList();

		if (columnListMode == null)
		{
			columnListMode = AssertOperation.columnListMode_enum.exclude;
		}
		else
		{
			if (list == null)
			{
				throw new TestExecutionError("Must specify " + AssertOperation.COLUMNLIST_JSON_NAME + " when the " + AssertOperation.COLUMNLISTMODE_JSON_NAME + " attribute is speicified", FileConstants.ERR_STATE_COLUMN_MODE_STATE);
			}
		}

		/** It's okay for the source file not to exist if refresh assertion data is enabled
		 *  since the source file will be refreshed prior to the assertion being tested
		 */
		if (!assertionSource.exists() && !refreshAssertionData)
		{
			throw new TestExecutionError("Source file does not exist: " + assertionSource, FileConstants.ERR_SOURCE_FILE_MISSING);
		}

		if (target == null || !target.exists())
		{
			throw new TestExecutionError("Target file does not exist: " + target, FileConstants.ERR_TARGET_FILE_MISSING);
		}

		DataFileSchema ffs = null;

		try
		{
			ffs = fileRuntimeSupport.locateReferenceFileSchema(op.getTestClass().getPackage(), operation.getFile(), null, obj);

			applicationLog.info("Resolved data file schema: " + ffs.getId());

			context.declareAndSetStringValue(FileConstants.ASSERT_FILE_SCHEMA, ffs.getId());
		}
		catch (IllegalArgumentException exc)
		{
			throw new TestExecutionError("Error loading Flat File Schema", FileConstants.ERR_LOAD_FFML, exc);
		}
		catch (ResourceNotFoundException exc)
		{
			throw new TestExecutionError("Error resolving Flat File Schema", FileConstants.ERR_LOAD_FFML, exc);
		}

		actualList.addAll(ffs.getColumnNames());

		if (list != null)
		{
			switch (columnListMode)
			{
				case include:
					// our list only includes mentioned columns
					actualList.clear();
					actualList.addAll(list);
					break;
				case exclude:
					// start with all columns, and remove the ones that are called out
					actualList.removeAll(list);
					break;
			}
		}

		// create a delimited local copy of the remote file
		String localID = op.getQualifiedName() + "-target-" + ffs.getId() + "." + target.getName() + ".delimited";

		File localSource = assertionSource;
		File localTarget = fileRuntimeSupport.getGeneratedDataFile(op.getTestClass().getPackage(), localID);

		DataFile sourceFF = null;
		DataFile localFF = null;

		try
		{
			DataFileSchema ffView = ffs;

			ffView = ffs.createSubViewIncludingColumns(actualList, "synthetic-" + ffs.getId() + "-delimited-" + op.getQualifiedName(), DataFileSchema.format_type.delimited);
			ffView.setColumnDelimiter(dataFileManager.getDefaultColumnDelimiter());
			ffView.setRowDelimiter(dataFileManager.getDefaultRowDelimiter());
			ffView.setNullToken(dataFileManager.getDefaultNullToken());

			File fileGen = runtimeSupport.getGeneratedSourceDirectory("file");

			IOUtils.writeStringToFile(new FileBuilder(fileGen).subdir("synthetic-fml").mkdirs().name(ffView.getId()).file(), ffView.toJsonString());

			// copy both files - source and target, into a local delimited copy
			localSource = fileRuntimeSupport.getGeneratedDataFile(op.getTestClass().getPackage(), op.getQualifiedName() + "-source-" + ffs.getId() + "." + assertionSource.getName() + ".delimited");

			sourceFF = dataFileManager.loadDataFile(target, ffs);
			localFF = dataFileManager.loadDataFile(localTarget, ffView);

			applicationLog.info("Caching local copy of target " + target.getName() + " to " + localTarget.getAbsolutePath());
			dataFileManager.copyDataFile(sourceFF, localFF);

			// before processing the source, refresh the data if necessary
			if (refreshAssertionData)
			{
				// copy from the local target, since it has the schema the source will be validated against
				applicationLog.info("Refreshing assertion data " + assertionSource.getName() + " from " + localTarget.getAbsolutePath());
				IOUtils.copyFiles(localTarget, assertionSource);
			}

			sourceFF = dataFileManager.loadDataFile(assertionSource, ffView);
			localFF = dataFileManager.loadDataFile(localSource, ffView);

			applicationLog.info("Caching local copy of source " + assertionSource.getName() + " to " + localSource.getAbsolutePath());
			dataFileManager.copyDataFile(sourceFF, localFF);

			// set the file pointers to the new files
			localFF = dataFileManager.loadDataFile(localTarget, ffView);
			sourceFF = dataFileManager.loadDataFile(localSource, ffView);

			// compare localSource to localTarget

			// we now have a delimited local file to compare the master file to (created for convenience for debugging)
			List diffResults = dataFileManager.diff(sourceFF, localFF);

			String failureId = FileConstants.FAIL_FILE_DIFF;

			if (operation.getFailureId() != null)
			{
				failureId = operation.getFailureId();
			}

			if (diffResults.size() != 0)
			{
				dataFileManager.report(diffManager, op, failureId, diffResults);

				throw new TestAssertionFailure("File : " + assertionSource.getAbsolutePath() + " does not match : " + target.getAbsolutePath(), failureId);
			}
		}
		catch (IOException e)
		{
			throw new TestExecutionError("Error comparing files: " + e.toString(), FileConstants.ERR_FILE_COMPARE, e);
		}

		return ClassResponder.action_code.handled;
	}

	private void simpleCheckContents(File target, ETLTestValueObject obj, AssertOperation operation, AssertOperation.assertionMode_enum mode, ETLTestOperation op, VariableContext context) throws TestAssertionFailure, TestExecutionError
	{
		String failureId = mode == AssertOperation.assertionMode_enum.notEmpty ? FileConstants.FAIL_FILE_EMPTY : FileConstants.FAIL_FILE_NOT_EMPTY;

		if (operation.getFailureId() != null)
		{
			failureId = operation.getFailureId();
		}

		if (!target.exists())
		{
			throw new TestExecutionError(target.getAbsolutePath(), FileConstants.ERR_TARGET_FILE_MISSING);
		}

		try
		{
			DataFileSchema ffs = fileRuntimeSupport.locateReferenceFileSchema(op.getTestClass().getPackage(), operation.getFile(), null, obj);

			applicationLog.info("Resolved data file schema: " + ffs.getId());

			context.declareAndSetStringValue(FileConstants.ASSERT_FILE_SCHEMA, ffs.getId());

			// open the file and count the lines
			DataFile df = dataFileManager.loadDataFile(target, ffs);

			DataFile.FileData data = df.getFileData();

			int rows = 0;

			try
			{
				Iterator it = data.iterator();

				while (it.hasNext())
				{
					it.next();
					rows++;
				}
			}
			finally
			{
				data.dispose();
			}

			switch (mode)
			{
				case empty:
					if (rows != 0)
					{
						throw new TestAssertionFailure(target.getAbsolutePath() + " contains " + rows + " rows of data", failureId);
					}
					break;
				case notEmpty:
					if (rows == 0)
					{
						throw new TestAssertionFailure(target.getAbsolutePath() + " contains no data", failureId);
					}
					break;
			}
		}
		catch(IOException exc)
		{
			throw new TestExecutionError("", FileConstants.ERR_FILE_COMPARE, exc);
		}
		catch (ResourceNotFoundException exc)
		{
			// fallback - just check the file length
			switch (mode)
			{
				case empty:
					if (target.length() != 0)
					{
						throw new TestAssertionFailure(target.getAbsolutePath(), failureId);
					}
					break;
				case notEmpty:
					if (target.length() == 0)
					{
						throw new TestAssertionFailure(target.getAbsolutePath(), failureId);
					}
					break;
			}
		}
	}

	public action_code process(ETLTestOperation op, ETLTestValueObject parameters, VariableContext vcontext, ExecutionContext econtext) throws TestAssertionFailure, TestExecutionError, TestWarning
	{
		throw new UnsupportedOperationException();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy