private void ProcessComparisonResults( DataComparerResults results, int pkCompareResult, IDataReader expectedData, IDataReader actualData, ref bool expectedReaderHasMoreData, ref bool actualReaderHasMoreData, IDictionary <string, bool> additionalFieldsToIgnore) { switch (Math.Sign(pkCompareResult)) { case -1: // Missing row in actuals HandleMissingActualRow(expectedData, results); expectedReaderHasMoreData = ReadDataReader(expectedData); break; case 0: // Row matches as expected, advance both readers HandleSameRow(expectedData, actualData, results, additionalFieldsToIgnore); expectedReaderHasMoreData = ReadDataReader(expectedData); actualReaderHasMoreData = ReadDataReader(actualData); break; case 1: // Unexpected row in actuals HandleUnexpectedActualsRow(actualData, results); actualReaderHasMoreData = ReadDataReader(actualData); break; } }
private void HandleSameRow( IDataReader expectedReader, IDataReader actualsReader, DataComparerResults results, IDictionary <string, bool> additionalFieldsToIgnore) { DataComparerRowResults rowResults = null; // Iterate through all the fields, comparing values for (int i = 0; i < actualsReader.FieldCount; i++) { string fieldName = actualsReader.GetName(i); if (ShouldSkipFieldValueComparison(fieldName, additionalFieldsToIgnore)) { continue; } // Get the expected value int expectedOrdinal = GetExpectedOrdinal(expectedReader, fieldName); if (expectedOrdinal < 0) { _logger.WarnFormat("Expected row does not have column '{0}'", fieldName); continue; } object expectedValue = expectedReader.GetValue(expectedOrdinal); object actualValue = actualsReader.GetValue(i); if (!expectedValue.Equals(actualValue)) { // Make sure row results object is created if (rowResults == null) { rowResults = new DataComparerRowResults(DataComparerRowDifference.ValuesDiffer); } rowResults.ExpectedValues[fieldName] = MakeSerializable(expectedValue); rowResults.ActualValues[fieldName] = MakeSerializable(actualValue); } } if (rowResults != null) { CollectPrimaryKeyValues(actualsReader, rowResults.KeyValues); results.RowDifferences.Add(rowResults); // Add difference to the DataTables as well... if (dataCopyMode == DataCopyMode.DifferencesOnly) { AddDataRowToTable(expectedReader, tablesByReader[expectedReader]); AddDataRowToTable(actualsReader, tablesByReader[actualsReader]); } } }
private void CompareAndDisposeDataReaders( DbDataReaderComparer comparer, DataComparerResults dataComparerResults, IDataReader actualData, IDataReader expectedData, IEnumerable <string> additionalColumnNamesToIgnore) { try { bool expectedReaderHasMoreData = ReadDataReader(expectedData); bool actualReaderHasMoreData = ReadDataReader(actualData); // No data? Quit now if (!actualReaderHasMoreData && !expectedReaderHasMoreData) { return; } Dictionary <string, bool> additionalFieldsToIgnore = GetAdditionalFieldsToIgnoreAsDictionary(additionalColumnNamesToIgnore); do { // Using primary key values, determine whether readers are on same record, or one is "ahead" of the other int pkCompareResult = CompareCurrentReadersPKValues( comparer, expectedData, actualData, expectedReaderHasMoreData, actualReaderHasMoreData); ProcessComparisonResults( dataComparerResults, pkCompareResult, expectedData, actualData, ref expectedReaderHasMoreData, ref actualReaderHasMoreData, additionalFieldsToIgnore); }while ((expectedReaderHasMoreData || actualReaderHasMoreData) && dataComparerResults.RowDifferences.Count < MaximumDifferences); } finally { // Make sure the the data readers are properly disposed actualData.Dispose(); expectedData.Dispose(); } }
private void HandleMissingActualRow(IDataReader expectedReader, DataComparerResults results) { // Missing actual row DataComparerRowResults diff = new DataComparerRowResults(DataComparerRowDifference.RowMissing); CollectPrimaryKeyValues(expectedReader, diff.KeyValues); CollectRowValues(expectedReader, diff.ExpectedValues); results.RowDifferences.Add(diff); // Add difference to the DataTable as well... if (dataCopyMode == DataCopyMode.DifferencesOnly) { AddDataRowToTable(expectedReader, tablesByReader[expectedReader]); } }
private void HandleUnexpectedActualsRow(IDataReader actualsReader, DataComparerResults results) { // Unexpected actual row DataComparerRowResults diff = new DataComparerRowResults(DataComparerRowDifference.UnexpectedRow); CollectPrimaryKeyValues(actualsReader, diff.KeyValues); CollectRowValues(actualsReader, diff.ActualValues); results.RowDifferences.Add(diff); // Add difference to the DataTable as well... if (dataCopyMode == DataCopyMode.DifferencesOnly) { AddDataRowToTable(actualsReader, tablesByReader[actualsReader]); } }
/// <summary> /// Compares the contents of two IDataReader instances, ignoring the specified columns, optionally creating DataTables for further inspection of the data. /// </summary> /// <param name="tableName">The name of the table for which the data comparison was performed.</param> /// <param name="expectedData">A <see cref="IDataReader"/> containing the expected data.</param> /// <param name="actualData">A <see cref="IDataReader"/> containing the actual data.</param> /// <param name="uniqueConstraintFieldNames">An array of the names of the primary key fields to use as the basis for identifying corresponding rows between the two readers.</param> /// <param name="isPrimaryKey"></param> /// <param name="dataCopyMode">Indicates whether or not a <see cref="DataTable"/> should be created for each <see cref="IDataReader"/> (expected and actual) and the data copied in while processing the readers.</param> /// <param name="additionalFieldsToIgnore">Fields to ignore for the current comparison (mismatches in data values will not be counted as failures).</param> /// <returns>The results of the comparison.</returns> public DataComparerResults Compare( string tableName, IDataReader expectedData, IDataReader actualData, string[] uniqueConstraintFieldNames, bool isPrimaryKey, DataCopyMode dataCopyMode, string[] additionalFieldsToIgnore) { //Initialize an empty array if the parameter is null. additionalFieldsToIgnore = additionalFieldsToIgnore ?? new string[0]; var dataComparerResults = new DataComparerResults { TableName = tableName }; this.dataCopyMode = dataCopyMode; // Prepare data tables, if necessary if (dataCopyMode == DataCopyMode.All || dataCopyMode == DataCopyMode.DifferencesOnly) { DataTable expectedTable = InitializeDataTable(expectedData, uniqueConstraintFieldNames, isPrimaryKey); expectedTable.TableName = "ExpectedDataTable"; tablesByReader[expectedData] = expectedTable; dataComparerResults.ExpectedData = expectedTable; DataTable actualTable = InitializeDataTable(actualData, uniqueConstraintFieldNames, isPrimaryKey); actualTable.TableName = "ActualDataTable"; tablesByReader[actualData] = actualTable; dataComparerResults.ActualData = actualTable; } primaryKeyFields = new List <string>(uniqueConstraintFieldNames); InitializeColumnNamesFromReader(dataComparerResults.ColumnNames, actualData); VerifyThatAllNonTestColumnsFromExpectedDataArePresent( dataComparerResults.TableName, dataComparerResults.ColumnNames, expectedData, additionalFieldsToIgnore); // Initialize counts rowCountsByReader[expectedData] = 0; rowCountsByReader[actualData] = 0; int[] expectedPKOrdinals = (from field in uniqueConstraintFieldNames select expectedData.GetOrdinal(field)).ToArray(); actualPKOrdinals = (from field in uniqueConstraintFieldNames select actualData.GetOrdinal(field)).ToArray(); DbDataReaderComparer comparer = new DbDataReaderComparer(expectedPKOrdinals, actualPKOrdinals); CompareAndDisposeDataReaders(comparer, dataComparerResults, actualData, expectedData, additionalFieldsToIgnore); // Set counts dataComparerResults.ExpectedRowCount = rowCountsByReader[expectedData]; dataComparerResults.ActualRowCount = rowCountsByReader[actualData]; return(dataComparerResults); }