diff --git a/YaNco.sln.DotSettings b/YaNco.sln.DotSettings new file mode 100644 index 00000000..7ce9f154 --- /dev/null +++ b/YaNco.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/src/YaNco.Abstractions/IRfcRuntime.cs b/src/YaNco.Abstractions/IRfcRuntime.cs index 03d51ab3..17fe180f 100644 --- a/src/YaNco.Abstractions/IRfcRuntime.cs +++ b/src/YaNco.Abstractions/IRfcRuntime.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Dbosoft.YaNco; using LanguageExt; @@ -46,5 +47,10 @@ public interface IRfcRuntime Either GetTimeString(IDataContainerHandle containerHandle, string name); Option Logger { get; } + + Either SetFieldValue(IDataContainerHandle handle, T value, Func> func); + Either GetFieldValue(IDataContainerHandle handle, Func> func); } + + } \ No newline at end of file diff --git a/src/YaNco.Abstractions/TypeMapping/FieldMappingContext.cs b/src/YaNco.Abstractions/TypeMapping/FieldMappingContext.cs new file mode 100644 index 00000000..3ffbbe2a --- /dev/null +++ b/src/YaNco.Abstractions/TypeMapping/FieldMappingContext.cs @@ -0,0 +1,16 @@ +namespace Dbosoft.YaNco.TypeMapping +{ + public class FieldMappingContext + { + public readonly IRfcRuntime RfcRuntime; + public readonly IDataContainerHandle Handle; + public readonly RfcFieldInfo FieldInfo; + + public FieldMappingContext(IRfcRuntime rfcRuntime, IDataContainerHandle handle, RfcFieldInfo fieldInfo) + { + RfcRuntime = rfcRuntime; + Handle = handle; + FieldInfo = fieldInfo; + } + } +} diff --git a/src/YaNco.Abstractions/TypeMapping/IFieldMapper.cs b/src/YaNco.Abstractions/TypeMapping/IFieldMapper.cs new file mode 100644 index 00000000..b65e3e3f --- /dev/null +++ b/src/YaNco.Abstractions/TypeMapping/IFieldMapper.cs @@ -0,0 +1,11 @@ +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public interface IFieldMapper + { + Either SetField(T value, FieldMappingContext context); + Either GetField(FieldMappingContext context); + + } +} \ No newline at end of file diff --git a/src/YaNco.Abstractions/TypeMapping/IFromAbapValueConverter.cs b/src/YaNco.Abstractions/TypeMapping/IFromAbapValueConverter.cs new file mode 100644 index 00000000..b64030f7 --- /dev/null +++ b/src/YaNco.Abstractions/TypeMapping/IFromAbapValueConverter.cs @@ -0,0 +1,11 @@ +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public interface IFromAbapValueConverter + { + Try ConvertTo(AbapValue abapValue); + bool CanConvertTo(RfcType rfcType); + + } +} \ No newline at end of file diff --git a/src/YaNco.Abstractions/TypeMapping/IRfcConverterResolver.cs b/src/YaNco.Abstractions/TypeMapping/IRfcConverterResolver.cs new file mode 100644 index 00000000..3176a24a --- /dev/null +++ b/src/YaNco.Abstractions/TypeMapping/IRfcConverterResolver.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace Dbosoft.YaNco.TypeMapping +{ + public interface IRfcConverterResolver + { + IEnumerable> GetToRfcConverters(RfcType rfcType); + IEnumerable> GetFromRfcConverters(RfcType rfcType, Type abapValueType); + } +} \ No newline at end of file diff --git a/src/YaNco.Abstractions/TypeMapping/IToAbapValueConverter.cs b/src/YaNco.Abstractions/TypeMapping/IToAbapValueConverter.cs new file mode 100644 index 00000000..0d8f6669 --- /dev/null +++ b/src/YaNco.Abstractions/TypeMapping/IToAbapValueConverter.cs @@ -0,0 +1,11 @@ +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public interface IToAbapValueConverter + { + Try ConvertFrom(T value, RfcFieldInfo fieldInfo); + bool CanConvertFrom(RfcType rfcType); + + } +} \ No newline at end of file diff --git a/src/YaNco.Abstractions/YaNco.Abstractions.csproj b/src/YaNco.Abstractions/YaNco.Abstractions.csproj index 0ec99b05..ac39ff23 100644 --- a/src/YaNco.Abstractions/YaNco.Abstractions.csproj +++ b/src/YaNco.Abstractions/YaNco.Abstractions.csproj @@ -20,7 +20,7 @@ This package contains abstraction definitions. - + all runtime; build; native; contentfiles; analyzers diff --git a/src/YaNco.Core/DataContainer.cs b/src/YaNco.Core/DataContainer.cs index c263f213..d9ed3cd2 100644 --- a/src/YaNco.Core/DataContainer.cs +++ b/src/YaNco.Core/DataContainer.cs @@ -17,162 +17,14 @@ protected DataContainer(IDataContainerHandle handle, IRfcRuntime rfcRuntime) public Either SetField(string name, T value) { - switch (value) - { - case int intValue: - return _rfcRuntime.SetInt(_handle, name, intValue); - case long longValue: - return _rfcRuntime.SetLong(_handle, name, longValue); - case DateTime dateTime: - return GetFieldInfo(name) - .Bind(typeDesc => - { - switch (typeDesc.Type) - { - case RfcType.DATE: - return SetFieldAsDate(name, dateTime); - case RfcType.TIME: - return SetFieldAsTime(name, dateTime); - default: - return _rfcRuntime.SetString(_handle, name, - (string)Convert.ChangeType(value, typeof(string))); - } - }); - - default: - { - return _rfcRuntime.SetString(_handle, name, - (string)Convert.ChangeType(value, typeof(string))); - - } - } + return _rfcRuntime.SetFieldValue(_handle, value, () => GetFieldInfo(name)); } protected abstract Either GetFieldInfo(string name); public Either GetField(string name) { - return GetFieldInfo(name) - .Bind(typeDesc => - { - switch (typeDesc.Type) - { - case RfcType.BYTE: - return GetFieldAsInt(name); - case RfcType.NUM: - return GetFieldAsInt(name); - case RfcType.INT: - return GetFieldAsInt(name); - case RfcType.INT2: - return GetFieldAsInt(name); - case RfcType.INT1: - return GetFieldAsInt(name); - case RfcType.INT8: - return GetFieldAsLong(name); - case RfcType.DATE: - return GetFieldAsDate(name); - case RfcType.TIME: - return GetFieldAsTime(name); - default: - return GetFieldAsString(name); - } - }); - } - - private Either GetFieldAsString(string name) - { - return _rfcRuntime.GetString(_handle, name).Map(r => - { - object value = r; - - if (typeof(T) == typeof(bool)) - { - value = !string.IsNullOrWhiteSpace(r); - } - - return (T)Convert.ChangeType(value, typeof(T)); - }); - } - - private Either SetFieldAsDate(string name, DateTime value) - { - var dateString = value.ToString("yyyyMMdd", CultureInfo.InvariantCulture); - return _rfcRuntime.SetDateString(_handle, name, dateString); - } - - private Either SetFieldAsTime(string name, DateTime value) - { - var dateString = value.ToString("HHmmss", CultureInfo.InvariantCulture); - return _rfcRuntime.SetDateString(_handle, name, dateString); - } - - private Either GetFieldAsInt(string name) - { - return _rfcRuntime.GetInt(_handle, name).Map(r => - { - object value = r; - - if (typeof(T) == typeof(bool)) - { - value = r != 0; - } - - return (T)Convert.ChangeType(value, typeof(T)); - }); - } - - private Either GetFieldAsLong(string name) - { - return _rfcRuntime.GetLong(_handle, name).Map(r => - { - object value = r; - - if (typeof(T) == typeof(bool)) - { - value = r != 0; - } - - return (T)Convert.ChangeType(value, typeof(T)); - }); - } - - private Either GetFieldAsDate(string name) - { - return _rfcRuntime.GetDateString(_handle, name).Map(r => - { - object value; - if (typeof(T) == typeof(string)) - value = r; - else - { - if (r == "00000000" || r == string.Empty) - value = DateTime.MinValue; - else - value = DateTime.ParseExact(r, "yyyyMMdd", CultureInfo.InvariantCulture); - } - - - return (T)Convert.ChangeType(value, typeof(T)); - }); - } - - private Either GetFieldAsTime(string name) - { - return _rfcRuntime.GetDateString(_handle, name).Map(r => - { - object value; - if (typeof(T) == typeof(string)) - value = r; - else - { - if (r == "000000" || r == string.Empty) - value = DateTime.MinValue; - else - value = DateTime.ParseExact(r, "HHmmss", CultureInfo.InvariantCulture); - } - - return (T)Convert.ChangeType(value, typeof(T)); - }); + return _rfcRuntime.GetFieldValue(_handle,() => GetFieldInfo(name)); } public Either SetFieldBytes(string name, byte[] buffer, long bufferLength) @@ -195,8 +47,7 @@ public Either GetTable(string name) { return _rfcRuntime.GetTable(_handle, name).Map(handle => (ITable) new Table(handle, _rfcRuntime)); } - - + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/src/YaNco.Core/Internal/Api.cs b/src/YaNco.Core/Internal/Api.cs index 1c98991b..e1a3972e 100644 --- a/src/YaNco.Core/Internal/Api.cs +++ b/src/YaNco.Core/Internal/Api.cs @@ -319,13 +319,13 @@ public static RfcRc GetBytes(IDataContainerHandle containerHandle, string name, if (rc != RfcRc.RFC_BUFFER_TOO_SMALL) { buffer = new byte[bufferLength]; - tempBuffer.CopyTo(buffer, 0); + Array.Copy(tempBuffer, buffer, bufferLength); return rc; } tempBuffer = new byte[bufferLength]; - rc = Interopt.RfcGetXString(containerHandle.Ptr, name, tempBuffer, 255, out _, out errorInfo); + rc = Interopt.RfcGetXString(containerHandle.Ptr, name, tempBuffer, bufferLength, out _, out errorInfo); buffer = new byte[bufferLength]; tempBuffer.CopyTo(buffer, 0); diff --git a/src/YaNco.Core/RfcRuntime.cs b/src/YaNco.Core/RfcRuntime.cs index da186157..5a76b415 100644 --- a/src/YaNco.Core/RfcRuntime.cs +++ b/src/YaNco.Core/RfcRuntime.cs @@ -1,15 +1,26 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Dbosoft.YaNco.Internal; +using Dbosoft.YaNco.TypeMapping; using LanguageExt; +using FieldMappingContext = Dbosoft.YaNco.TypeMapping.FieldMappingContext; + // ReSharper disable UnusedMember.Global namespace Dbosoft.YaNco { public class RfcRuntime : IRfcRuntime { - public RfcRuntime(ILogger logger = null) + private readonly IFieldMapper _fieldMapper; + + public RfcRuntime(ILogger logger = null, IFieldMapper fieldMapper = null) { Logger = logger == null ? Option.None : Option.Some(logger); + _fieldMapper = fieldMapper ?? + new DefaultFieldMapper( + new CachingConverterResolver( + DefaultConverterResolver.CreateWithBuildInConverters())); + } private Either ResultOrError(TResult result, RfcErrorInfo errorInfo, bool logAsError = false) @@ -292,6 +303,27 @@ public Either GetTimeString(IDataContainerHandle container public Option Logger { get; } + public Either SetFieldValue(IDataContainerHandle handle, T value, Func> func) + { + return func().Bind(fieldInfo => + { + Logger.IfSome(l => l.LogTrace("setting field value", new { handle, fieldInfo, SourceType= typeof(T) })); + return _fieldMapper.SetField(value, new FieldMappingContext(this, handle, fieldInfo)); + }); + + } + + public Either GetFieldValue(IDataContainerHandle handle, Func> func) + { + return func().Bind(fieldInfo => + { + Logger.IfSome(l => l.LogTrace("reading field value", new { handle, fieldInfo, TargetType = typeof(T) })); + return _fieldMapper.GetField(new FieldMappingContext(this, handle, fieldInfo)); + }); + + + } + public Either SetInt(IDataContainerHandle containerHandle, string name, int value) { Logger.IfSome(l => l.LogTrace("setting int value by name", new { containerHandle, name, value })); diff --git a/src/YaNco.Core/TypeMapping/ByteValueConverter.cs b/src/YaNco.Core/TypeMapping/ByteValueConverter.cs new file mode 100644 index 00000000..771c3ee2 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/ByteValueConverter.cs @@ -0,0 +1,49 @@ +using System; +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class ByteValueConverter: IToAbapValueConverter, IFromAbapValueConverter + { + public Try ConvertFrom(byte[] value, RfcFieldInfo fieldInfo) + { + return Prelude.Try(() => + { + if (!IsSupportedRfcType(fieldInfo.Type)) + throw new NotSupportedException($"Cannot convert from RfcType {fieldInfo.Type} to byte array."); + + return new AbapByteValue(fieldInfo, value); + }); + + } + + public bool CanConvertFrom(RfcType rfcType) + { + return IsSupportedRfcType(rfcType); + } + + public Try ConvertTo(AbapValue abapValue) + { + return Prelude.Try(() => (abapValue as AbapByteValue)?.Value); + } + + public bool CanConvertTo(RfcType rfcType) + { + return IsSupportedRfcType(rfcType); + } + + private bool IsSupportedRfcType(RfcType rfcType) + { + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (rfcType) + { + case RfcType.BYTE: + case RfcType.XSTRING: + return true; + default: + return false; + } + + } + } +} \ No newline at end of file diff --git a/src/YaNco.Core/TypeMapping/CachingConverterResolver.cs b/src/YaNco.Core/TypeMapping/CachingConverterResolver.cs new file mode 100644 index 00000000..b5ea0da9 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/CachingConverterResolver.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class CachingConverterResolver : IRfcConverterResolver + { + private readonly IRfcConverterResolver _decoratedResolver; + + public CachingConverterResolver(IRfcConverterResolver decoratedResolver) + { + _decoratedResolver = decoratedResolver; + } + + private readonly IDictionary _fromRfcConverters = new ConcurrentDictionary(); + private readonly IDictionary _toRfcConverters = new ConcurrentDictionary(); + + public IEnumerable> GetToRfcConverters(RfcType rfcType) + { + var sourceType = typeof(T); + var key = $"{rfcType}_{sourceType}"; + + if (!_toRfcConverters.ContainsKey(key)) + { + var converters = _decoratedResolver.GetToRfcConverters(rfcType).ToArray(); + _toRfcConverters.Add(key, converters.Length == 0 ? null : converters); + + } + + var entry = _toRfcConverters[key]; + + if (entry != null) + return (IEnumerable>)entry; + return new IToAbapValueConverter[0]; + + + } + + public IEnumerable> GetFromRfcConverters(RfcType rfcType, Type abapValueType) + { + var targetType = typeof(T); + var key = $"{rfcType}_{targetType}"; + + if (!_fromRfcConverters.ContainsKey(key)) + { + var converters = _decoratedResolver.GetFromRfcConverters(rfcType, abapValueType).ToArray(); + _fromRfcConverters.Add(key, converters.Length == 0 ? null : converters); + } + + var entry = _fromRfcConverters[key]; + + if (entry != null) + return (IEnumerable>)entry; + return new IFromAbapValueConverter[0]; + } + } +} \ No newline at end of file diff --git a/src/YaNco.Core/TypeMapping/DateTimeValueConverter.cs b/src/YaNco.Core/TypeMapping/DateTimeValueConverter.cs new file mode 100644 index 00000000..929cc709 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/DateTimeValueConverter.cs @@ -0,0 +1,99 @@ +using System; +using System.Globalization; +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class DateTimeValueConverter: IToAbapValueConverter, IFromAbapValueConverter + { + public Try ConvertFrom(DateTime value, RfcFieldInfo fieldInfo) + { + return Prelude.Try(() => + { + if (!IsSupportedRfcType(fieldInfo.Type)) + throw new NotSupportedException($"Cannot convert DateTime to RfcType {fieldInfo.Type} ."); + + string stringValue; + + var dateTime = (DateTime)Convert.ChangeType(value, typeof(DateTime), CultureInfo.InvariantCulture); + + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (fieldInfo.Type) + { + case RfcType.DATE: + stringValue = dateTime.ToString("yyyyMMdd", CultureInfo.InvariantCulture); + break; + case RfcType.TIME: + stringValue = dateTime.ToString("HHmmss", CultureInfo.InvariantCulture); + break; + default: + throw new ArgumentOutOfRangeException(nameof(fieldInfo), fieldInfo.Type, + $"not supported Type field in {nameof(fieldInfo)}."); + } + + return new AbapStringValue(fieldInfo, stringValue); + }); + + } + + public bool CanConvertFrom(RfcType rfcType) + { + return IsSupportedRfcType(rfcType); + } + + private bool IsSupportedRfcType(RfcType rfcType) + { + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (rfcType) + { + case RfcType.DATE: + case RfcType.TIME: + return true; + default: + return false; + } + + } + + public Try ConvertTo(AbapValue abapValue) + { + return Prelude.Try(() => + { + if (!(abapValue is AbapStringValue stringValue)) + throw new ArgumentException($"DateTimeConverter cannot convert type {abapValue.GetType()}", + nameof(abapValue)); + + DateTime dateTime; + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (stringValue.FieldInfo.Type) + { + case RfcType.DATE: + if (stringValue.Value == "00000000" || stringValue.Value == string.Empty) + dateTime = DateTime.MinValue; + else + dateTime = DateTime.ParseExact(stringValue.Value, "yyyyMMdd", CultureInfo.InvariantCulture); + break; + case RfcType.TIME: + if (stringValue.Value == "000000" || stringValue.Value == string.Empty) + dateTime = DateTime.MinValue; + else + dateTime = default(DateTime).Add( + DateTime.ParseExact(stringValue.Value, "HHmmss", CultureInfo.InvariantCulture).TimeOfDay); + break; + default: + throw new NotSupportedException( + $"It is not supported to convert RfcType {abapValue.FieldInfo.Type} to DateTime"); + } + + return dateTime; + + }); + + } + + public bool CanConvertTo(RfcType rfcType) + { + return IsSupportedRfcType(rfcType); + } + } +} \ No newline at end of file diff --git a/src/YaNco.Core/TypeMapping/DefaultConverterResolver.cs b/src/YaNco.Core/TypeMapping/DefaultConverterResolver.cs new file mode 100644 index 00000000..58112b54 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/DefaultConverterResolver.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class DefaultConverterResolver : IRfcConverterResolver + { + private readonly IEnumerable _toRfcConverters; + private readonly IEnumerable _fromRfcConverters; + + public static DefaultConverterResolver CreateWithBuildInConverters( + IEnumerable fromRfcConverters = null, IEnumerable toRfcConverters = null) + { + fromRfcConverters = (fromRfcConverters ?? new Type[0]).Append(new [] + { + typeof(DateTimeValueConverter), + typeof(ByteValueConverter), + typeof(DefaultFromAbapValueConverter<>), + }); + + toRfcConverters = (toRfcConverters ?? new Type[0]).Append(new[] + { + typeof(IntValueConverter<>), + typeof(LongValueConverter<>), + typeof(StringValueConverter<>), + typeof(ByteValueConverter), + typeof(DateTimeValueConverter) + }); + + return new DefaultConverterResolver( + fromRfcConverters, toRfcConverters); + } + + public DefaultConverterResolver(IEnumerable fromRfcConverters, IEnumerable toRfcConverters) + { + _fromRfcConverters = fromRfcConverters; + _toRfcConverters = toRfcConverters; + } + + protected virtual object CreateConverter(Type type, Type targetType = null, Type abapType = null) + { + if (!type.IsGenericTypeDefinition) + return Activator.CreateInstance(type); + + var typeArguments = new List(); + foreach (var argument in type.GetGenericArguments()) + { + if (!(targetType is null) && !argument.IsSubclassOf(typeof(AbapValue))) + { + typeArguments.Add(targetType); + } + else + { + if (abapType == null) continue; + + if (abapType.IsSubclassOf(typeof(AbapValue))) + typeArguments.Add(abapType); + } + } + + return Activator.CreateInstance(type.MakeGenericType(typeArguments.ToArray())); + } + + public IEnumerable> GetToRfcConverters(RfcType rfcType) + { + return _toRfcConverters + .Map(type => CreateConverter(type, typeof(T)) as IToAbapValueConverter) + .Where(c => c != null) + .Where(c => c.CanConvertFrom(rfcType)); + + } + + public IEnumerable> GetFromRfcConverters(RfcType rfcType, Type abapValueType) + { + return _fromRfcConverters + .Map(type => + (CreateConverter(type, typeof(T), abapValueType)) as + IFromAbapValueConverter) + .Where(c => c != null) + .Where(c => c.CanConvertTo(rfcType)); + } + + + } +} \ No newline at end of file diff --git a/src/YaNco.Core/TypeMapping/DefaultFieldMapper.cs b/src/YaNco.Core/TypeMapping/DefaultFieldMapper.cs new file mode 100644 index 00000000..c5a6bbf0 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/DefaultFieldMapper.cs @@ -0,0 +1,122 @@ +using System; +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class DefaultFieldMapper : IFieldMapper + { + private readonly IRfcConverterResolver _converterResolver; + + public DefaultFieldMapper(IRfcConverterResolver converterResolver) + { + _converterResolver = converterResolver; + } + + public Either SetField(T value, FieldMappingContext context) + { + AbapValue abapValue = null; + + foreach (var converter in _converterResolver.GetToRfcConverters(context.FieldInfo.Type)) + { + var result = converter.ConvertFrom(value, context.FieldInfo)(); + if(result.IsFaulted) + continue; + result.IfSucc(v => abapValue = v); + break; + + } + + if (abapValue == null) + return new RfcErrorInfo(RfcRc.RFC_CONVERSION_FAILURE, RfcErrorGroup.EXTERNAL_APPLICATION_FAILURE, "", + $"Converting from type {typeof(T)} to abap type {context.FieldInfo.Type} is not supported.", + "", "E", "", "", "", "", ""); + + switch (abapValue) + { + case AbapIntValue abapIntValue: + return context.RfcRuntime.SetInt(context.Handle, context.FieldInfo.Name, abapIntValue.Value); + case AbapLongValue abapLongValue: + return context.RfcRuntime.SetLong(context.Handle, context.FieldInfo.Name, abapLongValue.Value); + case AbapByteValue abapByteValue: + return context.RfcRuntime.SetBytes(context.Handle, context.FieldInfo.Name, abapByteValue.Value, + abapByteValue.Value.LongLength); + case AbapStringValue abapStringValue: + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (context.FieldInfo.Type) + { + case RfcType.DATE: + return context.RfcRuntime.SetDateString(context.Handle, context.FieldInfo.Name, + abapStringValue.Value); + case RfcType.TIME: + return context.RfcRuntime.SetTimeString(context.Handle, context.FieldInfo.Name, + abapStringValue.Value); + default: + return context.RfcRuntime.SetString(context.Handle, context.FieldInfo.Name, abapStringValue.Value); + } + + default: + throw new ArgumentOutOfRangeException(nameof(abapValue)); + + } + } + + public Either GetField(FieldMappingContext context) + { + return context.Apply(c => + { + switch (context.FieldInfo.Type) + { + case RfcType.DATE: + return context.RfcRuntime.GetDateString(context.Handle, context.FieldInfo.Name).Map(v => + (AbapValue) new AbapStringValue(context.FieldInfo, v)); + case RfcType.TIME: + return context.RfcRuntime.GetTimeString(context.Handle, context.FieldInfo.Name).Map(v => + (AbapValue) new AbapStringValue(context.FieldInfo, v)); + case RfcType.CHAR: + case RfcType.NUM: + case RfcType.STRING: + case RfcType.BCD: + case RfcType.FLOAT: + case RfcType.DECF16: + case RfcType.DECF34: + return context.RfcRuntime.GetString(context.Handle, context.FieldInfo.Name).Map(v => + (AbapValue) new AbapStringValue(context.FieldInfo, v)); + case RfcType.INT: + case RfcType.INT2: + case RfcType.INT1: + return context.RfcRuntime.GetInt(context.Handle, context.FieldInfo.Name).Map(v => + (AbapValue) new AbapIntValue(context.FieldInfo, v)); + case RfcType.INT8: + return context.RfcRuntime.GetLong(context.Handle, context.FieldInfo.Name).Map(v => + (AbapValue) new AbapLongValue(context.FieldInfo, v)); + case RfcType.BYTE: + case RfcType.XSTRING: + return context.RfcRuntime.GetBytes(context.Handle, context.FieldInfo.Name).Map(v => + (AbapValue) new AbapByteValue(context.FieldInfo, v)); + + default: + throw new NotSupportedException( + $"Reading a field of RfcType {context.FieldInfo.Type} is not supported for this method."); + } + }).Bind(abapValue => + { + T value = default; + foreach (var converter in _converterResolver.GetFromRfcConverters(context.FieldInfo.Type, abapValue.GetType())) + { + var result = converter.ConvertTo(abapValue)(); + if (result.IsFaulted) + continue; + result.IfSucc(v => value = v); + break; + } + + if (value == null) + return new RfcErrorInfo(RfcRc.RFC_CONVERSION_FAILURE, RfcErrorGroup.EXTERNAL_APPLICATION_FAILURE, "", + $"Converting from abap type {context.FieldInfo.Type} to type {typeof(T)} is not supported.", + "", "E", "", "", "", "", ""); + + return Prelude.Right(value); + }); + } + } +} \ No newline at end of file diff --git a/src/YaNco.Core/TypeMapping/DefaultFromAbapValueConverter.cs b/src/YaNco.Core/TypeMapping/DefaultFromAbapValueConverter.cs new file mode 100644 index 00000000..2731f948 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/DefaultFromAbapValueConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.Globalization; +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class DefaultFromAbapValueConverter : IFromAbapValueConverter + { + public Try ConvertTo(AbapValue abapValue) + { + return Prelude.Try( () => (T) Convert.ChangeType(abapValue, typeof(T), CultureInfo.InvariantCulture)); + } + + public bool CanConvertTo(RfcType rfcType) + { + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (rfcType) + { + case RfcType.CHAR: + case RfcType.DATE: + case RfcType.BCD: + case RfcType.TIME: + case RfcType.BYTE: + case RfcType.NUM: + case RfcType.FLOAT: + case RfcType.INT: + case RfcType.INT2: + case RfcType.INT1: + case RfcType.DECF16: + case RfcType.DECF34: + case RfcType.STRING: + case RfcType.INT8: + return true; + default: + return false; + } + } + } +} \ No newline at end of file diff --git a/src/YaNco.Core/TypeMapping/IntValueConverter.cs b/src/YaNco.Core/TypeMapping/IntValueConverter.cs new file mode 100644 index 00000000..743385f7 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/IntValueConverter.cs @@ -0,0 +1,42 @@ +using System; +using System.Globalization; +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class IntValueConverter: IToAbapValueConverter + { + public Try ConvertFrom(T value, RfcFieldInfo fieldInfo) + { + return Prelude.Try(() => + { + if (!IsSupportedRfcType(fieldInfo.Type)) + throw new NotSupportedException($"Cannot convert from RfcType {fieldInfo.Type} to integer value."); + + return new AbapIntValue(fieldInfo, (int)Convert.ChangeType(value, typeof(int), CultureInfo.InvariantCulture)); + + }); + + } + + public bool CanConvertFrom(RfcType rfcType) + { + return IsSupportedRfcType(rfcType); + } + + private static bool IsSupportedRfcType(RfcType rfcType) + { + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (rfcType) + { + case RfcType.INT: + case RfcType.INT2: + case RfcType.INT1: + return true; + default: + return false; + } + + } + } +} diff --git a/src/YaNco.Core/TypeMapping/LongValueConverter.cs b/src/YaNco.Core/TypeMapping/LongValueConverter.cs new file mode 100644 index 00000000..3df9e38e --- /dev/null +++ b/src/YaNco.Core/TypeMapping/LongValueConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.Globalization; +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class LongValueConverter : IToAbapValueConverter + { + public Try ConvertFrom(T value, RfcFieldInfo fieldInfo) + { + return Prelude.Try(() => + { + if (!IsSupportedRfcType(fieldInfo.Type)) + throw new NotSupportedException($"Cannot convert from RfcType {fieldInfo.Type} to long value."); + + return new AbapLongValue(fieldInfo, (long)Convert.ChangeType(value, typeof(long), CultureInfo.InvariantCulture)); + + }); + } + + public bool CanConvertFrom(RfcType rfcType) + { + return IsSupportedRfcType(rfcType); + } + + private static bool IsSupportedRfcType(RfcType rfcType) + { + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (rfcType) + { + case RfcType.INT8: + return true; + default: + return false; + } + + } + } +} \ No newline at end of file diff --git a/src/YaNco.Core/TypeMapping/StringValueConverter.cs b/src/YaNco.Core/TypeMapping/StringValueConverter.cs new file mode 100644 index 00000000..a107f1a3 --- /dev/null +++ b/src/YaNco.Core/TypeMapping/StringValueConverter.cs @@ -0,0 +1,60 @@ +using System; +using System.Globalization; +using LanguageExt; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class StringValueConverter : IToAbapValueConverter + { + public Try ConvertFrom(T value, RfcFieldInfo fieldInfo) + { + return Prelude.Try(() => + { + if (!IsSupportedRfcType(fieldInfo.Type)) + throw new NotSupportedException($"Cannot convert string to RfcType {fieldInfo.Type} ."); + + string stringValue = null; + + if (value is IConvertible convertible) + { + switch (convertible.GetTypeCode()) + { + case TypeCode.Boolean: + stringValue = Convert.ToBoolean(value, CultureInfo.InvariantCulture) ? "X" : ""; + break; + default: + stringValue = (string)Convert.ChangeType(value, typeof(string), CultureInfo.InvariantCulture); + break; + } + } + + return new AbapStringValue(fieldInfo, stringValue); + + }); + } + + public bool CanConvertFrom(RfcType rfcType) + { + return IsSupportedRfcType(rfcType); + } + + private static bool IsSupportedRfcType(RfcType rfcType) + { + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (rfcType) + { + case RfcType.CHAR: + case RfcType.NUM: + case RfcType.BCD: + case RfcType.FLOAT: + case RfcType.DECF16: + case RfcType.DECF34: + case RfcType.STRING: + return true; + default: + return false; + } + + } + } +} \ No newline at end of file diff --git a/src/YaNco.Core/YaNco.Core.csproj b/src/YaNco.Core/YaNco.Core.csproj index e48ff7cd..84724a65 100644 --- a/src/YaNco.Core/YaNco.Core.csproj +++ b/src/YaNco.Core/YaNco.Core.csproj @@ -22,7 +22,7 @@ This package contains the core implementation. - + all runtime; build; native; contentfiles; analyzers diff --git a/src/YaNco.Primitives/TypeMapping/AbapByteValue.cs b/src/YaNco.Primitives/TypeMapping/AbapByteValue.cs new file mode 100644 index 00000000..d4a5b835 --- /dev/null +++ b/src/YaNco.Primitives/TypeMapping/AbapByteValue.cs @@ -0,0 +1,13 @@ +namespace Dbosoft.YaNco.TypeMapping +{ + public class AbapByteValue : AbapValue + { + public readonly byte[] Value; + + public AbapByteValue(RfcFieldInfo fieldInfo, byte[] value) : + base(fieldInfo) + { + Value = value; + } + } +} \ No newline at end of file diff --git a/src/YaNco.Primitives/TypeMapping/AbapIntValue.cs b/src/YaNco.Primitives/TypeMapping/AbapIntValue.cs new file mode 100644 index 00000000..6275abd7 --- /dev/null +++ b/src/YaNco.Primitives/TypeMapping/AbapIntValue.cs @@ -0,0 +1,101 @@ +using System; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class AbapIntValue : AbapValue, IConvertible + { + public readonly int Value; + + public AbapIntValue(RfcFieldInfo fieldInfo, int value) : + base(fieldInfo) + { + Value = value; + + } + + public TypeCode GetTypeCode() + { + return Value.GetTypeCode(); + } + + public bool ToBoolean(IFormatProvider provider) + { + return ((IConvertible) Value).ToBoolean(provider); + } + + public byte ToByte(IFormatProvider provider) + { + return ((IConvertible) Value).ToByte(provider); + } + + public char ToChar(IFormatProvider provider) + { + return ((IConvertible) Value).ToChar(provider); + } + + public DateTime ToDateTime(IFormatProvider provider) + { + return ((IConvertible) Value).ToDateTime(provider); + } + + public decimal ToDecimal(IFormatProvider provider) + { + return ((IConvertible) Value).ToDecimal(provider); + } + + public double ToDouble(IFormatProvider provider) + { + return ((IConvertible) Value).ToDouble(provider); + } + + public short ToInt16(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt16(provider); + } + + public int ToInt32(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt32(provider); + } + + public long ToInt64(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt64(provider); + } + + public sbyte ToSByte(IFormatProvider provider) + { + return ((IConvertible) Value).ToSByte(provider); + } + + public float ToSingle(IFormatProvider provider) + { + return ((IConvertible) Value).ToSingle(provider); + } + + public string ToString(IFormatProvider provider) + { + return Value.ToString(provider); + } + + public object ToType(Type conversionType, IFormatProvider provider) + { + return ((IConvertible) Value).ToType(conversionType, provider); + } + + public ushort ToUInt16(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt16(provider); + } + + public uint ToUInt32(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt32(provider); + } + + public ulong ToUInt64(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt64(provider); + } + } +} \ No newline at end of file diff --git a/src/YaNco.Primitives/TypeMapping/AbapLongValue.cs b/src/YaNco.Primitives/TypeMapping/AbapLongValue.cs new file mode 100644 index 00000000..88de4562 --- /dev/null +++ b/src/YaNco.Primitives/TypeMapping/AbapLongValue.cs @@ -0,0 +1,101 @@ +using System; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class AbapLongValue : AbapValue, IConvertible + { + public readonly long Value; + + public AbapLongValue(RfcFieldInfo fieldInfo, long value) : + base(fieldInfo) + { + Value = value; + + } + + public TypeCode GetTypeCode() + { + return Value.GetTypeCode(); + } + + public bool ToBoolean(IFormatProvider provider) + { + return ((IConvertible) Value).ToBoolean(provider); + } + + public byte ToByte(IFormatProvider provider) + { + return ((IConvertible) Value).ToByte(provider); + } + + public char ToChar(IFormatProvider provider) + { + return ((IConvertible) Value).ToChar(provider); + } + + public DateTime ToDateTime(IFormatProvider provider) + { + return ((IConvertible) Value).ToDateTime(provider); + } + + public decimal ToDecimal(IFormatProvider provider) + { + return ((IConvertible) Value).ToDecimal(provider); + } + + public double ToDouble(IFormatProvider provider) + { + return ((IConvertible) Value).ToDouble(provider); + } + + public short ToInt16(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt16(provider); + } + + public int ToInt32(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt32(provider); + } + + public long ToInt64(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt64(provider); + } + + public sbyte ToSByte(IFormatProvider provider) + { + return ((IConvertible) Value).ToSByte(provider); + } + + public float ToSingle(IFormatProvider provider) + { + return ((IConvertible) Value).ToSingle(provider); + } + + public string ToString(IFormatProvider provider) + { + return Value.ToString(provider); + } + + public object ToType(Type conversionType, IFormatProvider provider) + { + return ((IConvertible) Value).ToType(conversionType, provider); + } + + public ushort ToUInt16(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt16(provider); + } + + public uint ToUInt32(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt32(provider); + } + + public ulong ToUInt64(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt64(provider); + } + } +} \ No newline at end of file diff --git a/src/YaNco.Primitives/TypeMapping/AbapStringValue.cs b/src/YaNco.Primitives/TypeMapping/AbapStringValue.cs new file mode 100644 index 00000000..2c600005 --- /dev/null +++ b/src/YaNco.Primitives/TypeMapping/AbapStringValue.cs @@ -0,0 +1,101 @@ +using System; +using System.Globalization; + +namespace Dbosoft.YaNco.TypeMapping +{ + public class AbapStringValue : AbapValue, IConvertible + { + public readonly string Value; + + public AbapStringValue(RfcFieldInfo fieldInfo, string value) : + base(fieldInfo) + { + Value = value; + } + + public TypeCode GetTypeCode() + { + return Value.GetTypeCode(); + } + + public bool ToBoolean(IFormatProvider provider) + { + return !string.IsNullOrWhiteSpace(Value); + } + + public byte ToByte(IFormatProvider provider) + { + return ((IConvertible) Value).ToByte(CultureInfo.InvariantCulture); + } + + public char ToChar(IFormatProvider provider) + { + return ((IConvertible) Value).ToChar(CultureInfo.InvariantCulture); + } + + public DateTime ToDateTime(IFormatProvider provider) + { + return ((IConvertible)Value).ToDateTime(CultureInfo.InvariantCulture); + } + + public decimal ToDecimal(IFormatProvider provider) + { + return ((IConvertible) Value).ToDecimal(CultureInfo.InvariantCulture); + } + + public double ToDouble(IFormatProvider provider) + { + return ((IConvertible) Value).ToDouble(CultureInfo.InvariantCulture); + } + + public short ToInt16(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt16(CultureInfo.InvariantCulture); + } + + public int ToInt32(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt32(CultureInfo.InvariantCulture); + } + + public long ToInt64(IFormatProvider provider) + { + return ((IConvertible) Value).ToInt64(CultureInfo.InvariantCulture); + } + + public sbyte ToSByte(IFormatProvider provider) + { + return ((IConvertible) Value).ToSByte(CultureInfo.InvariantCulture); + } + + public float ToSingle(IFormatProvider provider) + { + return ((IConvertible) Value).ToSingle(CultureInfo.InvariantCulture); + } + + public string ToString(IFormatProvider provider) + { + return Value.ToString(CultureInfo.InvariantCulture); + } + + public object ToType(Type conversionType, IFormatProvider provider) + { + return ((IConvertible) Value).ToType(conversionType, CultureInfo.InvariantCulture); + } + + public ushort ToUInt16(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt16(CultureInfo.InvariantCulture); + } + + public uint ToUInt32(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt32(CultureInfo.InvariantCulture); + } + + public ulong ToUInt64(IFormatProvider provider) + { + return ((IConvertible) Value).ToUInt64(CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/src/YaNco.Primitives/TypeMapping/AbapValue.cs b/src/YaNco.Primitives/TypeMapping/AbapValue.cs new file mode 100644 index 00000000..0ebab41b --- /dev/null +++ b/src/YaNco.Primitives/TypeMapping/AbapValue.cs @@ -0,0 +1,12 @@ +namespace Dbosoft.YaNco.TypeMapping +{ + public class AbapValue + { + public readonly RfcFieldInfo FieldInfo; + + protected AbapValue(RfcFieldInfo fieldInfo) + { + FieldInfo = fieldInfo; + } + } +} \ No newline at end of file diff --git a/test/SAPSystemTests/Program.cs b/test/SAPSystemTests/Program.cs index dcee38c8..7e3bbb12 100644 --- a/test/SAPSystemTests/Program.cs +++ b/test/SAPSystemTests/Program.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Dbosoft.YaNco; +using KellermanSoftware.CompareNetObjects; using LanguageExt; using Microsoft.Extensions.Configuration; @@ -41,7 +42,6 @@ static async Task Main(string[] args) var rows = Convert.ToInt32(config["tests:rows"]); var repeats = Convert.ToInt32(config["tests:repeats"]); - Console.WriteLine($"Test rows: {rows}"); Console.WriteLine($"Test repeats: {repeats}"); @@ -59,14 +59,17 @@ static async Task Main(string[] args) using (var context = new RfcContext(settings, new SimpleConsoleLogger())) { await context.PingAsync(); + + await RunIntegrationTests(context); + long totalTest1 = 0; long totalTest2 = 0; for (var run = 0; run < repeats; run++) { - Console.WriteLine($"starting Test Run {run+1} of {repeats}\tTest 01"); + Console.WriteLine($"starting Test Run {run + 1} of {repeats}\tTest 01"); totalTest1 += await RunPerformanceTest01(context, rows); - Console.WriteLine($"starting Test Run {run+1} of {repeats}\tTest 02"); + Console.WriteLine($"starting Test Run {run + 1} of {repeats}\tTest 02"); totalTest2 += await RunPerformanceTest02(context, rows); GC.Collect(); @@ -81,6 +84,143 @@ static async Task Main(string[] args) } } + private static async Task RunIntegrationTests(IRfcContext context) + { + Console.WriteLine("*** BEGIN OF Integration Tests ***"); + + await RunIntegrationTest01(context); + + Console.WriteLine("*** END OF Integration Tests ***"); + } + private static async Task RunIntegrationTest01(IRfcContext context) + { + Console.WriteLine("Integration Tests 01 (I/O field)"); + + // ReSharper disable StringLiteralTypo + var inputData = new TypesTestData + { + ACCP = "123456", + CHAR = "AB", + CLNT = "ABC", + CUKY = "ABCDE", + CURR = 9.12, + DATS = DateTime.MaxValue.Date, + DEC = 1.999M, + FLTP = +1E-6143, + INT1 = 1, + INT2 = 32767, + INT4 = 2147483647, + LANG = "A", + LCHR = RandomString(256), + LRAW = RandomByteArray(256), + NUMC = "123", + PREC = 99, + QUAN = 99.123, + RAW = RandomByteArray(10), + SSTRING = RandomString(10), + TIMS = default(DateTime).Add(new TimeSpan(23, 59,59)), + STRING = "ABCDE", + RAWSTRING = RandomByteArray(300), + UNIT = "ABC" + }; + + var outputData = await context.CallFunction("ZYANCO_IT_1", + Input: f => f.SetStructure("IS_IN", + s => s + .SetField("FIELD_ACCP", inputData.ACCP) + .SetField("FIELD_CHAR", inputData.CHAR) + .SetField("FIELD_CLNT", inputData.CLNT) + .SetField("FIELD_CUKY", inputData.CUKY) + .SetField("FIELD_CURR", inputData.CURR) + .SetField("FIELD_DATS", inputData.DATS) + .SetField("FIELD_DEC", inputData.DEC) + .SetField("FIELD_FLTP", inputData.FLTP) + .SetField("FIELD_INT1", inputData.INT1) + .SetField("FIELD_INT2", inputData.INT2) + .SetField("FIELD_INT4", inputData.INT4) + .SetField("FIELD_LANG", inputData.LANG) + .SetField("FIELD_LCHR", inputData.LCHR) + .SetField("FIELD_LRAW", inputData.LRAW) + .SetField("FIELD_NUMC", inputData.NUMC) + .SetField("FIELD_PREC", inputData.PREC) + .SetField("FIELD_QUAN", inputData.QUAN) + .SetField("FIELD_RAW", inputData.RAW) + .SetField("FIELD_SSTRING", inputData.SSTRING) + .SetField("FIELD_TIMS", inputData.TIMS) + .SetField("FIELD_STRING", inputData.STRING) + .SetField("FIELD_RAWSTRING", inputData.RAWSTRING) + .SetField("FIELD_UNIT", inputData.UNIT) + ), + Output: f => f.MapStructure("ES_ECHO", s => + // ReSharper disable InconsistentNaming + // ReSharper disable IdentifierTypo + from fieldACCP in s.GetField("FIELD_ACCP") + from fieldCHAR in s.GetField("FIELD_CHAR") + from fieldCLNT in s.GetField("FIELD_CLNT") + from fieldCUKY in s.GetField("FIELD_CUKY") + from fieldCURR in s.GetField("FIELD_CURR") + from fieldDATS in s.GetField("FIELD_DATS") + from fieldDEC in s.GetField("FIELD_DEC") + from fieldFLTP in s.GetField("FIELD_FLTP") + from fieldINT1 in s.GetField("FIELD_INT1") + from fieldINT2 in s.GetField("FIELD_INT2") + from fieldINT4 in s.GetField("FIELD_INT4") + from fieldLANG in s.GetField("FIELD_LANG") + from fieldLCHR in s.GetField("FIELD_LCHR") + from fieldLRAW in s.GetField("FIELD_LRAW") + from fieldNUMC in s.GetField("FIELD_NUMC") + from fieldPREC in s.GetField("FIELD_PREC") + from fieldQUAN in s.GetField("FIELD_QUAN") + from fieldRAW in s.GetField("FIELD_RAW") + from fieldSSTRING in s.GetField("FIELD_SSTRING") + from fieldTIMS in s.GetField("FIELD_TIMS") + from fieldSTRING in s.GetField("FIELD_STRING") + from fieldRAWSTRING in s.GetField("FIELD_RAWSTRING") + from fieldUNIT in s.GetField("FIELD_UNIT") + // ReSharper restore InconsistentNaming + // ReSharper restore IdentifierTypo + + select new TypesTestData + { + ACCP = fieldACCP, + CHAR = fieldCHAR, + CLNT = fieldCLNT, + CUKY = fieldCUKY, + CURR = fieldCURR, + DATS = fieldDATS, + DEC = fieldDEC, + FLTP = fieldFLTP, + INT1 = fieldINT1, + INT2 = fieldINT2, + INT4 = fieldINT4, + LANG = fieldLANG, + LCHR = fieldLCHR, + LRAW = fieldLRAW, + NUMC = fieldNUMC, + PREC = fieldPREC, + QUAN = fieldQUAN, + RAW = fieldRAW, + SSTRING = fieldSSTRING, + TIMS = fieldTIMS, + STRING = fieldSTRING, + RAWSTRING = fieldRAWSTRING, + UNIT = fieldUNIT + + })).Match( + r => r, + l => + { + Console.WriteLine(l.Message); + return new TypesTestData(); + }); + // ReSharper restore StringLiteralTypo + + + CompareLogic compareLogic = new CompareLogic(); + var result = compareLogic.Compare(inputData, outputData); + + Console.WriteLine(!result.AreEqual ? result.DifferencesString : "Test succeed"); + } private static async Task RunPerformanceTest01(IRfcContext context, int rows=0) { var watch = Stopwatch.StartNew(); @@ -135,8 +275,26 @@ private static Either SetRows(Either s[random.Next(s.Length)]).ToArray()); + } + + private static byte[] RandomByteArray(int size) + { + var rnd = new Random(); + var b = new byte[size]; // convert kb to byte + rnd.NextBytes(b); + return b; + } } + + public class TestData { public string Char40 { get; set; } @@ -145,5 +303,39 @@ public class TestData public string String { get; set; } + } + + public class TypesTestData + { + // ReSharper disable InconsistentNaming + public string ACCP { get; set; } + public string CHAR { get; set; } + public string CLNT { get; set; } + public string CUKY { get; set; } + public double CURR { get; set; } + public DateTime DATS { get; set; } + public decimal DEC { get; set; } + public double FLTP { get; set; } + public int INT1 { get; set; } + public short INT2 { get; set; } + public int INT4 { get; set; } + public string LANG { get; set; } + public string LCHR { get; set; } + public byte[] LRAW { get; set; } + public string NUMC { get; set; } + public int PREC { get; set; } + public double QUAN { get; set; } + + public byte[] RAW { get; set; } + public string SSTRING { get; set; } + public DateTime TIMS { get; set; } + public string STRING { get; set; } + public byte[] RAWSTRING { get; set; } + public string UNIT { get; set; } + + // ReSharper restore InconsistentNaming + + + } } \ No newline at end of file diff --git a/test/SAPSystemTests/SAPSystemTests.csproj b/test/SAPSystemTests/SAPSystemTests.csproj index feddb9ff..d31ffbf7 100644 --- a/test/SAPSystemTests/SAPSystemTests.csproj +++ b/test/SAPSystemTests/SAPSystemTests.csproj @@ -10,6 +10,7 @@ +