Skip to content

Commit

Permalink
Adding a test for UtilPack.TabularData and updating CIPipeline.
Browse files Browse the repository at this point in the history
  • Loading branch information
stazz committed Dec 26, 2018
1 parent 91fedaf commit 9d8ffc7
Show file tree
Hide file tree
Showing 17 changed files with 241 additions and 194 deletions.
3 changes: 2 additions & 1 deletion Build/UtilPackILGenerator/ILGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public sealed class ILGenerator
public const String SIZE_OF_TYPE = "Type";
public const String SIZE_OF_TYPE_TTYPE = "TType";

public const String EXTENSIONS = "UtilPack.UtilPackExtensions";
// We must name this differently in order not to disturb order of MethodDef table
public const String EXTENSIONS = "UtilPack.UtilPackExtensionsAdditional";
public const String EXTENSIONS_INVOKE_ALL_HANDLERS = "InvokeAllEventHandlers";
public const String EXTENSIONS_INVOKE_ALL_HANDLERS_TDELEGATE = "TDelegate";
public const String EXTENSIONS_INVOKE_ALL_HANDLERS_PARAM_DEL = "del";
Expand Down
5 changes: 5 additions & 0 deletions Build/package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ find "${UTILPACK_DIR}/" -mindepth 1 -maxdepth 1 -type d -exec "${DOTNET_DIR}/ila
-quiet \
"{}/UtilPack.il" \
"{}/AdditionalIL.il" \;

# ilasm removes (!) .pdb file, so re-copy it from obj folder
find "/repo-dir/BuildTarget/Release/obj/UtilPack/" -mindepth 1 -maxdepth 1 -type d -exec sh -c 'mv "{}/UtilPack.pdb" "/repo-dir/BuildTarget/Release/bin/UtilPack/$(basename {})/UtilPack.pdb"' \;

# TODO We actually need to add the debug directory to the resulting PE image. Right now the resulting .DLL does not have link to the .PDB file.

# Check that the IL code actually got into assembly, since find does not return error code if -exec'ed command fails
"${DOTNET_DIR}/ildasm" "-out=/repo-dir/tmpout/UtilPack.il" -utf8 -all "${UTILPACK_DIR}/net40/UtilPack.dll"
Expand Down
2 changes: 1 addition & 1 deletion CIPipeline
7 changes: 6 additions & 1 deletion Source/Code/UtilPack.Configuration/DynamicLoading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,16 @@ public async ValueTask<Object> InstantiateWithConfiguration(
{
var type = await this._typeLoader( config, targetType );

if ( configurationTypeLoader == null )
{
configurationTypeLoader = DefaultConfigurationTypeLoaderCallback;
}

Object retVal = null;
if ( type != null && !type.IsInterface && !type.IsAbstract )
{
// Type load successful - now figure out constructor and arguments to it
(var ctor, var ctorArgs) = this.DeduceConstructor( configurationTypeLoader ?? DefaultConfigurationTypeLoaderCallback, type, this._constructorArgumentsLoader( config, type ) );
(var ctor, var ctorArgs) = this.DeduceConstructor( configurationTypeLoader, type, this._constructorArgumentsLoader( config, type ) );
if ( ctor != null )
{
Object[] actualCtorArgs;
Expand Down
47 changes: 24 additions & 23 deletions Source/Code/UtilPack.TabularData/Implementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;
using UtilPack;
using UtilPack.TabularData;

Expand All @@ -48,7 +48,7 @@ AsyncDataColumn[] columns
}

/// <summary>
/// Implements the <see cref="DataRow{TDataColumn, TDataColumnMetaData}.GetColumn(int)"/> method.
/// Implements the <see cref="DataRow{TDataColumn, TDataColumnMetaData}.GetColumn(Int32)"/> method.
/// </summary>
/// <param name="index">The <c>0</c>-based column index.</param>
/// <returns>The <see cref="AsyncDataColumn"/> at given <c>0</c>-basd index in <see cref="Columns"/> array.</returns>
Expand Down Expand Up @@ -116,7 +116,7 @@ Int32 columnIndex
/// <returns>A task which will on completion contain <see cref="ResultOrNone{TResult}"/> struct describing the data.</returns>
/// <seealso cref="ResultOrNone{TResult}"/>
/// <remarks>
/// If value reading has already been started by <see cref="ReadBytesAsync(byte[], int, int)"/> method and not yet finished, this method will return <see cref="ResultOrNone{TResult}"/> such that its <see cref="ResultOrNone{TResult}.HasResult"/> property is <c>false</c>.
/// If value reading has already been started by <see cref="ReadBytesAsync(Byte[], Int32, Int32)"/> method and not yet finished, this method will return <see cref="ResultOrNone{TResult}"/> such that its <see cref="ResultOrNone{TResult}.HasResult"/> property is <c>false</c>.
/// </remarks>
public async ValueTask<ResultOrNone<Object>> TryGetValueAsync()
{
Expand Down Expand Up @@ -151,7 +151,7 @@ public async ValueTask<ResultOrNone<Object>> TryGetValueAsync()
}
}
}
retVal = this._state == FAULTED ? default( ResultOrNone<Object> ) : new ResultOrNone<Object>( this._value );
retVal = this._state == FAULTED ? default : new ResultOrNone<Object>( this._value );
}

return retVal;
Expand Down Expand Up @@ -226,12 +226,12 @@ public async ValueTask<ResultOrNone<Object>> TryGetValueAsync()
protected abstract ValueTask<Object> PerformReadAsValueAsync();

/// <summary>
/// This method should be overridden in derived class, and is called by <see cref="ReadBytesAsync(byte[], int, int)"/> after checks for concurrency and parameters pass.
/// This method should be overridden in derived class, and is called by <see cref="ReadBytesAsync(Byte[], Int32, Int32)"/> after checks for concurrency and parameters pass.
/// </summary>
/// <param name="array">The byte array where to read the data to.</param>
/// <param name="offset">The offset in <paramref name="array"/> where to start writing bytes.</param>
/// <param name="count">The maximum amount of bytes to write.</param>
/// <param name="isInitialRead">Whether this is first call to <see cref="ReadBytesAsync(byte[], int, int)"/> method.</param>
/// <param name="isInitialRead">Whether this is first call to <see cref="ReadBytesAsync(Byte[], Int32, Int32)"/> method.</param>
/// <returns>A task which should return how many bytes has written to <paramref name="array"/>, and whether whole data reading is complete.</returns>
protected abstract ValueTask<(Int32 BytesRead, Boolean IsComplete)> PerformReadToBytes( Byte[] array, Int32 offset, Int32 count, Boolean isInitialRead );

Expand Down Expand Up @@ -284,7 +284,7 @@ TColumnMetaData[] columnMetaDatas
public Int32 ColumnCount => this.ColumnMetaDatas.Length;

/// <summary>
/// This method implements the <see cref="DataRowMetaData.GetIndexFor(string)"/> method.
/// This method implements the <see cref="DataRowMetaData.GetIndexFor(String)"/> method.
/// Tries to get an index for column with given label.
/// </summary>
/// <param name="columnName">The column label.</param>
Expand All @@ -303,7 +303,7 @@ TColumnMetaData[] columnMetaDatas
protected TColumnMetaData[] ColumnMetaDatas { get; }

/// <summary>
/// Implements <see cref="DataRowMetaData{TColumnMetaData}.GetColumnMetaData(int)"/>
/// Implements <see cref="DataRowMetaData{TColumnMetaData}.GetColumnMetaData(Int32)"/>
/// Gets the <see cref="DataColumnMetaData"/> for given column index.
/// </summary>
/// <param name="columnIndex">The index of the column in <see cref="ColumnMetaDatas"/> array.</param>
Expand All @@ -317,7 +317,7 @@ public TColumnMetaData GetColumnMetaData( Int32 columnIndex )

/// <summary>
/// This class provides straightforward implementation for <see cref="DataColumnMetaData"/>.
/// The <see cref="ColumnCLRType"/> and <see cref="Label"/> are read-only properties, and the <see cref="ChangeType(object, Type)"/> method is left <c>abstract</c>.
/// The <see cref="ColumnCLRType"/> and <see cref="Label"/> are read-only properties, and the <see cref="ChangeType(Object, Type)"/> method is left <c>abstract</c>.
/// </summary>
public abstract class AbstractDataColumnMetaData : DataColumnMetaData
{
Expand Down Expand Up @@ -351,7 +351,7 @@ String label
public String Label { get; }

/// <summary>
/// Implements signature of <see cref="DataColumnMetaData.ChangeType(object, Type)"/> method, but leaves implementation for derived classes.
/// Implements signature of <see cref="DataColumnMetaData.ChangeType(Object, Type)"/> method, but leaves implementation for derived classes.
/// </summary>
/// <param name="value">The data that was acquired from <see cref="AsyncDataColumn"/> this <see cref="DataColumnMetaData"/> was obtained from.</param>
/// <param name="targetType">The type to transform the <paramref name="value"/> to.</param>
Expand All @@ -364,7 +364,7 @@ String label
}

/// <summary>
/// This class provides straightforward implementation for <see cref="AsyncDataColumnMetaData"/>, by extending <see cref="AbstractDataColumnMetaData"/> and leaving <see cref="ConvertFromBytesAsync(Stream, int)"/> method <c>abstract</c>.
/// This class provides straightforward implementation for <see cref="AsyncDataColumnMetaData"/>, by extending <see cref="AbstractDataColumnMetaData"/> and leaving <see cref="ConvertFromBytesAsync(Stream, Int32)"/> method <c>abstract</c>.
/// </summary>
public abstract class AbstractAsyncDataColumnMetaData : AbstractDataColumnMetaData, AsyncDataColumnMetaData
{
Expand All @@ -382,7 +382,7 @@ String label
}

/// <summary>
/// Implements signature of <see cref="AsyncDataColumnMetaData.ConvertFromBytesAsync(Stream, int)"/> method, but leaves implementation for derived classes.
/// Implements signature of <see cref="AsyncDataColumnMetaData.ConvertFromBytesAsync(Stream, Int32)"/> method, but leaves implementation for derived classes.
/// </summary>
/// <param name="stream">The stream containing raw byte data representation.</param>
/// <param name="byteCount">The amount of bytes to read from the stream.</param>
Expand Down Expand Up @@ -433,10 +433,10 @@ AsyncDataColumn previousColumn
/// <summary>
/// Overrides <see cref="AbstractAsyncDataColumn.PerformReadAsValueAsync"/> to first force all previous columns to be read by calling <see cref="E_UtilPack.SkipBytesAsync(AsyncDataColumn, Byte[])"/> method for previous column, if it was given.
/// Then, the amount of bytes the data takes is read from the stream by calling <see cref="ReadByteCountAsync"/> method.
/// If the byte count is greater or equal to <c>0</c>, then the <see cref="ReadValueAsync(int)"/> method is called to read actual value, and that is returned.
/// If the byte count is greater or equal to <c>0</c>, then the <see cref="ReadValueAsync(Int32)"/> method is called to read actual value, and that is returned.
/// Otherwise, <c>null</c> is returned.
/// </summary>
/// <returns>A task which on completion will have the value of <see cref="ReadValueAsync(int)"/> or <c>null</c>.</returns>
/// <returns>A task which on completion will have the value of <see cref="ReadValueAsync(Int32)"/> or <c>null</c>.</returns>
/// <remarks>
/// The values of all previous columns are forced to read because the underlying <see cref="Stream"/> is assumed to be unseekable.
/// Therefore, if user first tries to get value of e.g. 3rd column, the 1st and 2nd column values must be read before that in order for the stream to be in correct position to read value for 3rd column.
Expand All @@ -458,16 +458,16 @@ protected override async ValueTask<Object> PerformReadAsValueAsync()
}

/// <summary>
/// Overrides <see cref="AbstractAsyncDataColumn.PerformReadToBytes(byte[], int, int, bool)"/> to first, if <paramref name="isInitialRead"/> is <c>true</c>, force all previous columns to be read by calling <see cref="E_UtilPack.SkipBytesAsync(AsyncDataColumn, Byte[])"/> method for previous column, if it was given.
/// Overrides <see cref="AbstractAsyncDataColumn.PerformReadToBytes(Byte[], Int32, Int32, Boolean)"/> to first, if <paramref name="isInitialRead"/> is <c>true</c>, force all previous columns to be read by calling <see cref="E_UtilPack.SkipBytesAsync(AsyncDataColumn, Byte[])"/> method for previous column, if it was given.
/// Then, if <paramref name="isInitialRead"/> is <c>true</c>, the amount of bytes the data takes is read from the stream by calling <see cref="ReadByteCountAsync"/> method.
/// Otherwise the byte count is what was calculated to remain from previous <see cref="PerformReadToBytes(byte[], int, int, bool)"/>.
/// If the byte count is greater or equal to <c>0</c>, then the <see cref="ReadValueAsync(int)"/> method is called, and the return value of that is returned.
/// Otherwise the byte count is what was calculated to remain from previous <see cref="PerformReadToBytes(Byte[], Int32, Int32, Boolean)"/>.
/// If the byte count is greater or equal to <c>0</c>, then the <see cref="ReadValueAsync(Int32)"/> method is called, and the return value of that is returned.
/// Otherwise, <c>0</c> is returned.
/// </summary>
/// <param name="array">The byte array where to read the data to.</param>
/// <param name="offset">The offset in <paramref name="array"/> where to start writing bytes.</param>
/// <param name="count">The maximum amount of bytes to write.</param>
/// <param name="isInitialRead">Whether this is first call to <see cref="AbstractAsyncDataColumn.ReadBytesAsync(byte[], int, int)"/> method.</param>
/// <param name="isInitialRead">Whether this is first call to <see cref="AbstractAsyncDataColumn.ReadBytesAsync(Byte[], Int32, Int32)"/> method.</param>
/// <returns>A task which returns how many bytes has written to <paramref name="array"/>, and whether whole data reading is complete.</returns>
/// <remarks>
/// The values of all previous columns are forced to read because the underlying <see cref="Stream"/> is assumed to be unseekable.
Expand Down Expand Up @@ -505,14 +505,14 @@ protected override async ValueTask<Object> PerformReadAsValueAsync()
protected abstract ValueTask<Object> ReadValueAsync( Int32 byteCount );

/// <summary>
/// This method is called by <see cref="PerformReadToBytes(byte[], int, int, bool)"/> in order to read data as raw bytes from underlying <see cref="Stream"/>.
/// This method is called by <see cref="PerformReadToBytes(Byte[], Int32, Int32, Boolean)"/> in order to read data as raw bytes from underlying <see cref="Stream"/>.
/// </summary>
/// <param name="array">The byte array where to read the data to.</param>
/// <param name="offset">The offset in <paramref name="array"/> where to start writing bytes.</param>
/// <param name="count">The maximum amount of bytes to write.</param>
/// <returns>The amount of bytes read.</returns>
/// <remarks>
/// The <see cref="PerformReadToBytes(byte[], int, int, bool)"/> will take care of putting up correct <paramref name="count"/> value.
/// The <see cref="PerformReadToBytes(Byte[], Int32, Int32, Boolean)"/> will take care of putting up correct <paramref name="count"/> value.
/// </remarks>
protected abstract ValueTask<Int32> DoReadFromStreamAsync( Byte[] array, Int32 offset, Int32 count );

Expand All @@ -535,10 +535,10 @@ protected override void Reset()
public static partial class E_UtilPack
{
/// <summary>
/// Helper method to call <see cref="AsyncDataColumn.TryGetValueAsync"/> if <paramref name="rawBytes"/> is <c>null</c>, or keep reading raw bytes into <paramref name="rawBytes"/> using <see cref="AsyncDataColumn.ReadBytesAsync(byte[], int, int)"/> until all required bytes have been read.
/// Helper method to call <see cref="AsyncDataColumn.TryGetValueAsync"/> if <paramref name="rawBytes"/> is <c>null</c>, or keep reading raw bytes into <paramref name="rawBytes"/> using <see cref="AsyncDataColumn.ReadBytesAsync(Byte[], Int32, Int32)"/> until all required bytes have been read.
/// </summary>
/// <param name="stream">This <see cref="AsyncDataColumn"/>.</param>
/// <param name="rawBytes">The byte array to read to using <see cref="AsyncDataColumn.ReadBytesAsync(byte[], int, int)"/>, or <c>null</c> to use <see cref="AsyncDataColumn.TryGetValueAsync"/> instead.</param>
/// <param name="rawBytes">The byte array to read to using <see cref="AsyncDataColumn.ReadBytesAsync(Byte[], Int32, Int32)"/>, or <c>null</c> to use <see cref="AsyncDataColumn.TryGetValueAsync"/> instead.</param>
/// <returns>A task which will always return <c>true</c> on completion.</returns>
public static async ValueTask<Boolean> SkipBytesAsync( this AsyncDataColumn stream, Byte[] rawBytes )
{
Expand All @@ -549,7 +549,8 @@ public static async ValueTask<Boolean> SkipBytesAsync( this AsyncDataColumn stre
}
else
{
while ( ( ( await stream.ReadBytesAsync( rawBytes, 0, rawBytes.Length ) ) ?? 0 ) != 0 ) ;
while ( ( ( await stream.ReadBytesAsync( rawBytes, 0, rawBytes.Length ) ) ?? 0 ) != 0 )
;
}
return false;
}
Expand Down
13 changes: 13 additions & 0 deletions Source/Code/UtilPack/CollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1924,5 +1924,18 @@ IEnumerable<Z> seventh
}
}
}

/// <summary>
/// This is ease-of-life method to invoke <see cref="Enumerable.Distinct{TSource}(IEnumerable{TSource}, IEqualityComparer{TSource})"/> when there is need to pass equality comparison functions instead of making own class.
/// </summary>
/// <typeparam name="T">The type of items of this enumerable.</typeparam>
/// <param name="enumerable">This enumerable.</param>
/// <param name="equality">The callback to check when two items are the same.</param>
/// <param name="hashCode">The callback to get hash code from an item.</param>
/// <returns>Enumerable with distinct elements as its items.</returns>
/// <exception cref="NullReferenceException">If this enumerable is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">If either of <paramref name="equality"/> or <paramref name="hashCode"/> is <c>null</c>.</exception>
public static IEnumerable<T> Distinct<T>( this IEnumerable<T> enumerable, Equality<T> equality, HashCode<T> hashCode )
=> ArgumentValidator.ValidateNotNullReference( enumerable ).Distinct( ComparerFromFunctions.NewEqualityComparer( equality, hashCode ) );
}
}
11 changes: 8 additions & 3 deletions Source/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
</PropertyGroup>

<PropertyGroup>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<TreatSpecificWarningsAsErrors />
<LangVersion>latest</LangVersion>
</PropertyGroup>

<!-- Disable all debug information in release mode-->
<PropertyGroup Condition=" '$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>False</DebugSymbols>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>
7 changes: 7 additions & 0 deletions Source/Tests/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project>
<Import Project="$(MSBuildThisFileDirectory)/../Directory.Build.props" />

<PropertyGroup>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
</PropertyGroup>
</Project>
Loading

0 comments on commit 9d8ffc7

Please sign in to comment.