Skip to content

Commit

Permalink
Convert revit converters to use proxies for unit testing (#3501)
Browse files Browse the repository at this point in the history
* Initial commit from converter-tests

* tests now run

* adding more to proxy map

* switch to FluentAssertions

* fix IRevitLevel conversions

* more cast handling

* use proxymap for ofclass

* update revit interfaces

* fmt

* add more proxies

* better handling for wrapped types

* fix the RevitContext

* inject interfaces

* convert to new way of casting

* update and rejigger dependencies

* handle null elements

* proxy map is also proxy aware

* fmt

* change namespaces

* updates to proxies

* more cast changes

* another cast issue fixed

* update nugets

* fmt

* add System.IO to make CI happy?
  • Loading branch information
adamhathcock authored Jun 14, 2024
1 parent 6b767fe commit 76c6aba
Show file tree
Hide file tree
Showing 109 changed files with 2,070 additions and 1,530 deletions.
25 changes: 23 additions & 2 deletions All.sln
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
CodeMetricsConfig.txt = CodeMetricsConfig.txt
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
global.json = global.json
EndProjectSection
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ConnectorAutocadCivilShared", "ConnectorAutocadCivil\ConnectorAutocadCivil\ConnectorAutocadCivilShared.shproj", "{B47492D9-2EDA-4016-A930-7FA708C85C3D}"
Expand Down Expand Up @@ -536,7 +537,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{
ProjectSection(SolutionItems) = preProject
.github\workflows\ci.yml = .github\workflows\ci.yml
.github\workflows\main.yml = .github\workflows\main.yml
.github\workflows\test.yml = .github\workflows\test.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevitSharedResources2025", "ConnectorRevit\RevitSharedResources2025\RevitSharedResources2025.csproj", "{7B02BACC-D9B6-4FFE-A450-7ECB5F71F209}"
Expand All @@ -553,6 +553,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorCivil2025", "Conne
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterCivil2025", "Objects\Converters\ConverterAutocadCivil\ConverterCivil2025\ConverterCivil2025.csproj", "{F06E4C37-4076-4272-9CA6-FB505E02CD31}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B1324D25-C601-40F2-8AE2-6131F492B911}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Revit2023.Tests", "DUI3-DX\Converters\Revit\Speckle.Converters.Revit2023.Tests\Speckle.Converters.Revit2023.Tests.csproj", "{AEC26A0B-25F3-4544-A9D6-A427BFF79250}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug Mac|Any CPU = Debug Mac|Any CPU
Expand Down Expand Up @@ -2787,6 +2791,22 @@ Global
{F06E4C37-4076-4272-9CA6-FB505E02CD31}.Release|Any CPU.Build.0 = Release|Any CPU
{F06E4C37-4076-4272-9CA6-FB505E02CD31}.Release|x64.ActiveCfg = Release|Any CPU
{F06E4C37-4076-4272-9CA6-FB505E02CD31}.Release|x64.Build.0 = Release|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug Mac|Any CPU.ActiveCfg = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug Mac|Any CPU.Build.0 = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug Mac|x64.ActiveCfg = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug Mac|x64.Build.0 = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug|x64.ActiveCfg = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Debug|x64.Build.0 = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release Mac|Any CPU.ActiveCfg = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release Mac|Any CPU.Build.0 = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release Mac|x64.ActiveCfg = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release Mac|x64.Build.0 = Debug|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release|Any CPU.Build.0 = Release|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release|x64.ActiveCfg = Release|Any CPU
{AEC26A0B-25F3-4544-A9D6-A427BFF79250}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -2964,7 +2984,6 @@ Global
{2E00592E-558D-492D-88F9-3ECEE4C0C7DA} = {9DB74760-01DE-4AC1-A81B-BC7784351D22}
{01F98733-7352-47AD-A594-537D979DE3DE} = {4838C66E-8677-4FBD-9609-25376042E981}
{DC570FFF-6FE5-47BD-8BC1-B471A6067786} = {4838C66E-8677-4FBD-9609-25376042E981}
{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{26391930-F86F-47E0-A5F6-B89919E38CE1} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{D81C0B87-F0C1-4297-A147-02F001FB7E1E} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
Expand Down Expand Up @@ -2998,6 +3017,8 @@ Global
{829688CD-CECE-4F6C-A5A0-032BB39CD9E0} = {BE521908-7944-46F3-98BF-B47D34509934}
{70DEAA13-6DC8-44A0-B287-9E806A8054F1} = {890F3257-FCC2-4ED8-9180-22B3641B494C}
{F06E4C37-4076-4272-9CA6-FB505E02CD31} = {BE521908-7944-46F3-98BF-B47D34509934}
{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{AEC26A0B-25F3-4544-A9D6-A427BFF79250} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D43D91B-4F01-4A78-8250-CC6F9BD93A14}
Expand Down
1 change: 1 addition & 0 deletions Build/Consts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Build;
public static class Consts
{
public static readonly string[] Solutions = { "DUI3-DX.slnf" };
public static readonly string[] TestProjects = { "Speckle.Converters.Revit2023.Tests" };

public static readonly InstallerProject[] InstallerManifests =
{
Expand Down
5 changes: 3 additions & 2 deletions Build/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,15 @@ void RemoveDirectory(string d)
Target(
TEST,
DependsOn(BUILD),
() =>
Consts.TestProjects,
t =>
{
IEnumerable<string> GetFiles(string d)
{
return Glob.Files(".", d);
}

foreach (var file in GetFiles("**/*.Test.csproj"))
foreach (var file in GetFiles($"**/{t}.csproj"))
{
Run("dotnet", $"test {file} -c Release --no-restore --verbosity=normal");
}
Expand Down
1 change: 1 addition & 0 deletions DUI3-DX.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"DUI3-DX\\Converters\\Autocad\\Speckle.Converters.Autocad2023\\Speckle.Converters.Autocad2023.csproj",
"DUI3-DX\\Converters\\Autocad\\Speckle.Converters.AutocadShared\\Speckle.Converters.AutocadShared.shproj",
"DUI3-DX\\Converters\\Revit\\Speckle.Converters.Revit2023.DependencyInjection\\Speckle.Converters.Revit2023.DependencyInjection.csproj",
"DUI3-DX\\Converters\\Revit\\Speckle.Converters.Revit2023.Tests\\Speckle.Converters.Revit2023.Tests.csproj",
"DUI3-DX\\Converters\\Revit\\Speckle.Converters.Revit2023\\Speckle.Converters.Revit2023.csproj",
"DUI3-DX\\Converters\\Revit\\Speckle.Converters.RevitShared\\Speckle.Converters.RevitShared.shproj",
"DUI3-DX\\Converters\\Rhino\\Speckle.Converters.Rhino7.DependencyInjection\\Speckle.Converters.Rhino7.DependencyInjection.csproj",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<TargetFramework>net48</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup>

<Import Project="..\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems" Label="Shared" />
Expand Down Expand Up @@ -31,7 +32,6 @@
<ItemGroup>
<PackageReference Include="CefSharp.Wpf" Version="92.0.260" IncludeAssets="compile" NoWarn="NU1903"/>
<PackageReference Include="Revit.Async" Version="2.0.1" />
<PackageReference Include="Speckle.Revit.API" Version="2023.0.0" IncludeAssets="compile;build" />
</ItemGroup>

<Target AfterTargets="Clean" Name="CleanAddinFolder">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.RevitShared.Helpers;
using Speckle.Connectors.Utils;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Core.Logging;

namespace Speckle.Connectors.DUI.Bindings;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Connectors.RevitShared.Helpers;

namespace Speckle.Connectors.Revit.Bindings;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.Utils;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.RevitShared.Helpers;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.Utils.Operations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.RevitShared.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;

namespace Speckle.Connectors.Revit.Bindings;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using Speckle.Revit.Api;
using Speckle.Revit.Interfaces;

namespace Speckle.Connectors.Revit2023.Converters;

[SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline")]
public class ProxyMap : IProxyMap
{
private static readonly ConcurrentDictionary<Type, Type> s_revitToInterfaceMap = new();
private static readonly ConcurrentDictionary<Type, Type> s_proxyToInterfaceMap = new();
private static readonly ConcurrentDictionary<Type, Type> s_interfaceToRevit = new();
private static readonly ConcurrentDictionary<Type, Func<object, object>> s_proxyFactory = new();

[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling")]
static ProxyMap()
{
Add<DB.Element, IRevitElement, ElementProxy>(x => new ElementProxy(x));
Add<DB.FamilyInstance, IRevitFamilyInstance, FamilyInstanceProxy>(x => new FamilyInstanceProxy(x));
Add<DB.Curve, IRevitCurve, CurveProxy>(x => new CurveProxy(x));
Add<DB.BoundarySegment, IRevitBoundarySegment, BoundarySegmentProxy>(x => new BoundarySegmentProxy(x));
Add<DB.Level, IRevitLevel, LevelProxy>(x => new LevelProxy(x));
Add<DB.Location, IRevitLocation, LocationProxy>(x => new LocationProxy(x));
Add<DB.Material, IRevitMaterial, MaterialProxy>(x => new MaterialProxy(x));
Add<DB.ModelCurveArray, IRevitModelCurveArray, ModelCurveArrayProxy>(x => new ModelCurveArrayProxy(x));
Add<DB.ModelCurveArrArray, IRevitModelCurveArrArray, ModelCurveArrArrayProxy>(x => new ModelCurveArrArrayProxy(x));
Add<DB.Parameter, IRevitParameter, ParameterProxy>(x => new ParameterProxy(x));
Add<DB.BasePoint, IRevitBasePoint, BasePointProxy>(x => new BasePointProxy(x));
Add<DB.Wall, IRevitWall, WallProxy>(x => new WallProxy(x));
Add<DB.Panel, IRevitPanel, PanelProxy>(x => new PanelProxy(x));
Add<DB.Floor, IRevitFloor, FloorProxy>(x => new FloorProxy(x));
Add<DB.Ceiling, IRevitCeiling, CeilingProxy>(x => new CeilingProxy(x));
Add<DB.FootPrintRoof, IRevitFootPrintRoof, FootPrintRoofProxy>(x => new FootPrintRoofProxy(x));
Add<DB.ModelLine, IRevitModelLine, ModelLineProxy>(x => new ModelLineProxy(x));
Add<DB.RoofBase, IRevitRoofBase, RoofBaseProxy>(x => new RoofBaseProxy(x));
}

private static void Add<T, TInterface, TProxy>(Func<T, TProxy> f)
where T : class
where TInterface : notnull
where TProxy : TInterface
{
s_revitToInterfaceMap.TryAdd(typeof(T), typeof(TInterface));
s_proxyToInterfaceMap.TryAdd(typeof(TProxy), typeof(TInterface));
s_proxyFactory.TryAdd(typeof(TInterface), w => f((T)w));
s_interfaceToRevit.TryAdd(typeof(TInterface), typeof(T));
}

public Type? GetMappedTypeFromHostType(Type type)
{
if (s_revitToInterfaceMap.TryGetValue(type, out var t))
{
return t;
}
return null;
}

public Type? GetMappedTypeFromProxyType(Type type)
{
if (s_proxyToInterfaceMap.TryGetValue(type, out var t))
{
return t;
}

return null;
}

public Type? GetHostTypeFromMappedType(Type type)
{
if (s_interfaceToRevit.TryGetValue(type, out var t))
{
return t;
}

return null;
}

public object CreateProxy(Type type, object toWrap) => s_proxyFactory[type](toWrap);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@
using Speckle.Connectors.Revit.HostApp;
using Speckle.Connectors.Revit.Operations.Send;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.Revit2023.Converters;
using Speckle.Connectors.RevitShared.Helpers;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Operations;
using Speckle.Converters.Common;
using Speckle.Revit.Api;
using Speckle.Revit.Interfaces;

namespace Speckle.Connectors.Revit.DependencyInjection;

Expand All @@ -25,7 +30,11 @@ public void Load(SpeckleContainerBuilder builder)
builder.AddAutofac();
builder.AddConnectorUtils();
builder.AddDUI();
//builder.AddDUIView();
builder.AddSingleton(new RevitContext());

// POC: the concrete type can come out if we remove all the reference to it
builder.AddScoped<IConversionContextStack<IRevitDocument, IRevitForgeTypeId>, RevitConversionContextStack>();
builder.ScanAssemblyOfType<RevitUnitUtils>();

builder.AddSingletonInstance<ISyncToThread, SyncToCurrentThread>();

Expand Down Expand Up @@ -64,5 +73,6 @@ public void Load(SpeckleContainerBuilder builder)

// register send conversion cache
builder.AddSingleton<ISendConversionCache, SendConversionCache>();
builder.AddSingleton<IProxyMap, ProxyMap>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using DB = Autodesk.Revit.DB;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Autodesk.Revit.UI;
using Autodesk.Revit.UI;

namespace Speckle.Converters.RevitShared.Helpers;
namespace Speckle.Connectors.RevitShared.Helpers;

public class RevitContext
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Autodesk.Revit.DB;
using Speckle.Converters.Common;
using Speckle.Revit.Api;
using Speckle.Revit.Interfaces;

namespace Speckle.Connectors.RevitShared.Helpers;

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Naming",
"CA1711:Identifiers should not have incorrect suffix",
Justification = "See base class justification"
)]
// POC: so this should *probably* be Document and NOT UI.UIDocument, the former is Conversion centric
// and the latter is more for connector
public class RevitConversionContextStack
: ConversionContextStack<IRevitDocument, IRevitForgeTypeId>,
IConversionContextStack<IRevitDocument, IRevitForgeTypeId>
{
public RevitConversionContextStack(RevitContext context, IHostToSpeckleUnitConverter<IRevitForgeTypeId> unitConverter)
: base(
// POC: we probably should not get here without a valid document
// so should this perpetuate or do we assume this is valid?
// relting on the context.UIApplication?.ActiveUIDocument is not right
// this should be some IActiveDocument I suspect?
new DocumentProxy(
context.UIApplication?.ActiveUIDocument?.Document
?? throw new SpeckleConversionException("Active UI document could not be determined")
),
new ForgeTypeIdProxy(
context.UIApplication.ActiveUIDocument.Document.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
),
unitConverter
) { }

ContextWrapper<IRevitDocument, IRevitForgeTypeId> IConversionContextStack<IRevitDocument, IRevitForgeTypeId>.Push(
string speckleUnit
) => throw new NotImplementedException();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
using Revit.Async;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.RevitShared.Helpers;
using Speckle.Connectors.Utils;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Core.Logging;
using Speckle.Newtonsoft.Json;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@
using Speckle.Core.Models;
using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Operations;
using Speckle.Core.Logging;
using Speckle.Revit.Api;
using Speckle.Revit.Interfaces;

namespace Speckle.Connectors.Revit.Operations.Send;

public class RevitRootObjectBuilder : IRootObjectBuilder<ElementId>
{
// POC: SendSelection and RevitConversionContextStack should be interfaces, former needs interfaces
private readonly IRootToSpeckleConverter _converter;
private readonly IRevitConversionContextStack _contextStack;
private readonly IConversionContextStack<IRevitDocument, IRevitForgeTypeId> _contextStack;
private readonly Dictionary<string, Collection> _collectionCache;
private readonly Collection _rootObject;
private readonly ISendConversionCache _sendConversionCache;

public RevitRootObjectBuilder(
IRootToSpeckleConverter converter,
IRevitConversionContextStack contextStack,
IConversionContextStack<IRevitDocument, IRevitForgeTypeId> contextStack,
ISendConversionCache sendConversionCache
)
{
Expand All @@ -45,7 +46,7 @@ public RootObjectBuilderResult Build(
CancellationToken ct = default
)
{
var doc = _contextStack.Current.Document;
var doc = ((DocumentProxy)_contextStack.Current.Document)._Instance;

if (doc.IsFamilyDocument)
{
Expand All @@ -56,7 +57,7 @@ public RootObjectBuilderResult Build(

foreach (var id in objects)
{
var el = _contextStack.Current.Document.GetElement(id);
var el = doc.GetElement(id);
if (el != null)
{
revitElements.Add(el);
Expand Down
Loading

0 comments on commit 76c6aba

Please sign in to comment.