Skip to content

Commit

Permalink
Merge pull request #592 from xBimTeam/develop
Browse files Browse the repository at this point in the history
Dec 2024 release - v6.0
  • Loading branch information
andyward authored Dec 17, 2024
2 parents 8c4bfb2 + d3fbb28 commit 7871032
Show file tree
Hide file tree
Showing 31 changed files with 4,491 additions and 154 deletions.
57 changes: 25 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ _[**B**uilding **I**nformation **M**odelling](https://en.wikipedia.org/wiki/Buil
the .NET platform. This library enables software developers to easily read, write, validate and interrogate data in
the buildingSmart [IFC formats](https://en.wikipedia.org/wiki/Industry_Foundation_Classes), using any .NET language.

As of version 6.0 XbimEssentials includes support for `.netstandard2.0`, `.netstandard2.1`, `.net6.0` and `.net8.0`. Supporting `netstandard2.0` provides support .NET Framework 4.7.2 upwards.
Earlier .NET Framewaork versions may work, but we can't provide any support for them.


As of version 6.0 XbimEssentials includes support for `.netstandard2.0`, `.netstandard2.1`, `.net6.0` and `net8.0`.
Supporting `netstandard2.0` provides support .NET Framework 4.7.2 upwards.
Earlier .NET Framework versions may work, but we can't provide any support for them.

## Background / Motivation ##

Expand All @@ -35,7 +34,9 @@ This library supports *STEP*, *IfcXml* and *IfcZip* formats, and enables you to
[Ifc4 Addendum 2](http://www.buildingsmart-tech.org/specifications/ifc-releases/ifc4-add2)).

The wider XBIM toolkit contains additional repositories with libraries to read and write related Open BIM formats including
[COBie](https://github.com/xBimTeam/XbimExchange) and BIM Collaboration Format ([BCF](https://github.com/xBimTeam/XbimBCF))
[COBie](https://github.com/xBimTeam/XbimCobieExpress) and BIM Collaboration Format ([BCF](https://github.com/xBimTeam/XbimBCF))

xbim Toolkit also supports IFC model verification with IDS using our [IDS Library](https://github.com/xBimTeam/Xbim.IDS.Validator).

In order to visualise 3D Geometries you will need to include the [Xbim.Geometry](https://github.com/xBimTeam/XbimGeometry)
package which provides full support for geometric, topological operations and visualisation.
Expand All @@ -50,14 +51,20 @@ of STEP/Express parsing, 3D graphics - enabling you to work at a higher level th
Please see our [ChangeLog](CHANGELOG.md) for details on what's new and what you need to upgrade.
In particular, please **note the following section copied here:**

> **BREAKING CHANGE**: V6 implements a new mechanism for discovering internal resources and uses standard (.net Dependency Injection patterns)[https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection] for managing services internally.
> `Xbim.Common` now introduces an internal DI service that you can optionally integrate with your own DI implementation, for providing Logging services, and configuring xbim service behaviour. E.g. Invoking:
> ` XbimServices.Current.ConfigureServices(s => s.AddXbimToolkit(opt => opt.UseMemoryModel().UseLoggerFactory(yourloggerFactory)));
> registers the Toolkit internal dependencies, uses an In-memory model and inserts your configured ILoggerFactory into the service in place of our default one.
> **BREAKING CHANGE**: xbim Toolkit V6 implements a new mechanism for registering internal resources and makes use of standard [.net Dependency Injection patterns](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) for managing services internally.
> `Xbim.Common` now introduces an internal DI service that you can optionally integrate with your own DI implementation,
> for providing Logging services, and configuring xbim service behaviour such as the model provider to use. E.g. Invoking:
>
> `XbimServices.Current.ConfigureServices(s => s.AddXbimToolkit(opt => opt.UseMemoryModel().UseLoggerFactory(yourloggerFactory)));`
>
> registers the Toolkit internal dependencies, configures an In-memory model-provider and adds an existing ILoggerFactory into the service in place of our default one.
> `IfcStore.ModelProviderFactory` has been deprecated as this is provided for by the above mechanism
> The persistent EsentModel is now available automatically through IfcStore (on Windows) so there is no need for the `UseHeuristicModelProvider()` 'ceremony' to ensure Esent is used.
> Note: The default Logging implementation has been removed from Toolkit to reduce the amount of dependencies. To enable is a simple matter of adding logging to the Xbim Services.
> See (Example)[https://github.com/xBimTeam/XbimEssentials/blob/b3fce6f40a31fb87a75b24888a4e20740a378e31/Tests/DependencyInjectionTests.cs#L96]
> The persistent **EsentModel** is now available automatically through IfcStore (on Windows) so there is no need for the `UseHeuristicModelProvider()` 'ceremony'
> to ensure Esent is used although you can specify it explicitly with `XbimServices.Current.ConfigureServices(s => s.AddXbimToolkit(opt => opt.UseHeuristicModel())`
>
> Note: The default Logging implementation has been removed from Toolkit to reduce the amount of external dependencies.
> To enable is a simple matter of adding your preferred logging implementation to the Xbim Services.
> See [Example](https://github.com/xBimTeam/XbimEssentials/blob/b3fce6f40a31fb87a75b24888a4e20740a378e31/Tests/DependencyInjectionTests.cs#L96)

## Code Examples
Expand Down Expand Up @@ -230,7 +237,7 @@ and an ability open sementic model data from a JSON structure.

You will need Visual Studio 2019 or newer to compile the Solution. Visual Studio 2022 is recommended.
The [free VS 2022 Community Edition](https://visualstudio.microsoft.com/downloads/) should work fine.
Other IDEs such as JetBrains Rider and VS Code should also be fine.
Other IDEs such as JetBrains Rider and VS Code should work.

### Using the library

Expand All @@ -248,7 +255,6 @@ dependent packages directly. (Which is necessary for .NET Core currently, as Ess

## Toolkit Overview

![XBIM Libraries - high level dependencies](docs/img/XBIM-Architecture-0_1.png)

### How to use it?

Expand Down Expand Up @@ -278,15 +284,11 @@ the licence agreements.

The core XBIM library makes use of the following 3rd party software packages, under their associated licences:

* 'OpenCASCADE' Geometry Engine : http://www.opencascade.org/ - OPEN CASCADE Public License
* 'Gardens Point Parser Generator' http://gppg.codeplex.com/ - New BSD Licence
* Elements of '3D Tools' WPF library http://3dtools.codeplex.com/ - MS Permissive Licence
* Log4net : http://logging.apache.org/log4net/ - Apache 2.0 Licence
* NPOI : http://npoi.codeplex.com - Apache 2.0 Licence
* NewtonSoft JSON : http://json.codeplex.com/ - MIT Licence
* 'Gardens Point Parser Generator' https://github.com/KommuSoft/Gardens-Point-Parser-Generator - New BSD Licence
* 'OpenCASCADE' Geometry Engine : http://www.opencascade.org/ - GNU Lesser General Public License (LGPL) version 2.1, with additional [exception](https://dev.opencascade.org/doc/overview/html/occt_public_license.html#occt_lgpl_exception)

All 3rd party licences are permissive-style licences. We actively avoid Copyleft/GPL style licences to retain
compatibility with our CDDL licence - meaning you can use the XBIM Toolkit in a closed-source commercial software package.
All 3rd party licences are permissive-style licences. We actively avoid Strong Copyleft/GPL style licences to retain
compatibility with our CDDL licence - meaning you can use the XBIM Toolkit in a closed-source commercial software package.

## Support & Help

Expand All @@ -299,16 +301,7 @@ For bugs, and improvements, please use the GitHub Issues of the relevant reposit
If you have a question, or need some help, you may find the
[Stackoverflow xbim](https://stackoverflow.com/questions/tagged/xbim) tag a good place to start.

## Acknowledgements
While we do not qualify anymore for open source licenses of JetBrains, we would like to acknowledge the
good work and thank [JetBrains](https://www.jetbrains.com/) for supporting the XbimToolkit project with
free open source [Resharper](https://www.jetbrains.com/resharper/) licenses in the past.

[![ReSharper Logo](https://raw.githubusercontent.com/xBimTeam/XbimWindowsUI/master/ReadmeResources/icon_ReSharper.png)](https://www.jetbrains.com/resharper/)

Thanks also to Microsoft Azure DevOps for the use of [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/)
to automate our builds.

## Getting Involved

If you'd like to get involved and contribute to this project, please read the [CONTRIBUTING ](https://github.com/xBimTeam/XbimEssentials/blob/master/CONTRIBUTING.md) page or contact the Project Coordinators @CBenghi and @martin1cerny.
If you'd like to get involved and contribute to this project, please read the [CONTRIBUTING](https://github.com/xBimTeam/XbimEssentials/blob/master/CONTRIBUTING.md) page or contact the Project Coordinators @CBenghi and @martin1cerny.
219 changes: 219 additions & 0 deletions Tests/ChunkedDictionaryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using Xbim.Common.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;

namespace Xbim.Essentials.Tests
{
[TestClass]
public class ChunkedDictionaryTests
{

[TestMethod]
public void Init_ChunkSizeCanBeTotalSize()
{
var dictionary = new ChunkedDictionary<int, string>(2, 2);
var chunks = dictionary.GetChunkCount();
Assert.AreEqual(1, chunks, "Chunk count should be 1 when chunk size equals total size.");
}

[TestMethod]
public void Init_OnlyOneChunkInitializedAtTheBeginning()
{
var dictionary = new ChunkedDictionary<int, string>(20, 2);
var chunks = dictionary.GetChunkCount();
Assert.AreEqual(1, chunks, "Only one chunk should be initialized at the beginning.");
}

[TestMethod]
public void Add_SingleItem_ItemExists()
{
var dictionary = new ChunkedDictionary<int, string>(10);
dictionary.Add(1, "One");

Assert.IsTrue(dictionary.ContainsKey(1), "Key should exist after adding.");
Assert.AreEqual("One", dictionary[1], "Value should match the added value.");
}

[TestMethod]
public void Remove_ByKey_ItemRemoved()
{
var dictionary = new ChunkedDictionary<int, string>(10);
dictionary.Add(1, "One");
var removed = dictionary.Remove(1);

Assert.IsTrue(removed, "Item should be removed successfully.");
Assert.IsFalse(dictionary.ContainsKey(1), "Key should not exist after removal.");
}

[TestMethod]
public void Remove_ByKeyValuePair_ItemRemoved()
{
var dictionary = new ChunkedDictionary<int, string>(10);
dictionary.Add(1, "One");
var removed = dictionary.Remove(new KeyValuePair<int, string>(1, "One"));

Assert.IsTrue(removed, "Item should be removed successfully.");
Assert.IsFalse(dictionary.ContainsKey(1), "Key should not exist after removal.");
}

[TestMethod]
public void TryGetValue_ExistingItem_ReturnsTrueAndCorrectValue()
{
var dictionary = new ChunkedDictionary<int, string>(10);
dictionary.Add(1, "One");

string value;
var result = dictionary.TryGetValue(1, out value);

Assert.IsTrue(result, "TryGetValue should return true for existing key.");
Assert.AreEqual("One", value, "Value should match the added value.");
}

[TestMethod]
public void Add_MultipleItems_ExceedChunkSize_DistributesAcrossChunks()
{
var chunkSize = 5;
var dictionary = new ChunkedDictionary<int, string>(chunkSize);
for (int i = 0; i < chunkSize * 2; i++)
{
dictionary.Add(i, $"Item{i}");
}

Assert.AreEqual(chunkSize * 2, dictionary.Count, "Total items should match items added.");
Assert.IsTrue(dictionary.Keys.Max() >= chunkSize, "Keys should be distributed across chunks.");
}

[TestMethod]
public void Add_MultipleItems_WithTotalSize_DistributesAcrossChunks()
{
var chunkSize = 5;
var total = (chunkSize * 2) + 2; // +2 to ensure we need more than 2 chunks

var dictionary = new ChunkedDictionary<int, string>(total, chunkSize);
for (int i = 0; i < total; i++)
{
dictionary.Add(i, $"Item{i}");
}

Assert.AreEqual(total, dictionary.Count, "Total items should match items added.");
Assert.IsTrue(dictionary.GetChunkCount() == 3, "We have 3 chunks.");
}

[TestMethod]
public void Add_MultipleItems_WithTotalSize_GrowsWhenAddingMoreItems()
{
var chunkSize = 5;
var total = (chunkSize * 2) + 2;

var dictionary = new ChunkedDictionary<int, string>(total, chunkSize);
for (int i = 0; i < total; i++)
{
dictionary.Add(i, $"Item{i}");
}

dictionary.Add(total, $"Item{total}"); // Add one more item creating a new chunk

Assert.AreEqual(total + 1, dictionary.Count, "Total items should match items added.");
Assert.IsTrue(dictionary.GetChunkCount() == 4, "We have 4 chunks.");

// Add more items to ensure we can grow further
for (int i = 1; i < 6; i++)
{
dictionary.Add(total+i, $"Item{total+i}");
}

Assert.AreEqual(total + 6, dictionary.Count, "Total items should match items added.");
Assert.IsTrue(dictionary.GetChunkCount() == 5, "We have 5 chunks by now.");
}

[TestMethod]
public void KeysAndValues_AfterMutations_AccuratelyReflected()
{
var dictionary = new ChunkedDictionary<int, string>(10);
dictionary.Add(1, "One");
dictionary.Add(2, "Two");
dictionary.Remove(1);

var keys = dictionary.Keys;
var values = dictionary.Values;

Assert.IsTrue(keys.Contains(2) && keys.Count == 1, "Keys collection should accurately reflect current keys.");
Assert.IsTrue(values.Contains("Two") && values.Count == 1, "Values collection should accurately reflect current values.");
}

[TestMethod]
public void Enumeration_YieldsAllItemsExactlyOnce()
{
var testData = new Dictionary<int, string>
{
{ 1, "One" }, { 2, "Two" }, { 3, "Three" },
{ 4, "Four" }, { 5, "Five" }, { 6, "Six" }
};
var dictionary = new ChunkedDictionary<int, string>(testData.Count, 3);

foreach (var kvp in testData)
{
dictionary.Add(kvp.Key, kvp.Value);
}

var enumeratedItems = new Dictionary<int, string>();
foreach (var kvp in dictionary)
{
// Verify no duplicates during enumeration
Assert.IsFalse(enumeratedItems.ContainsKey(kvp.Key), "Duplicate key found during enumeration.");
enumeratedItems.Add(kvp.Key, kvp.Value);
}

// Verify all items were enumerated
Assert.AreEqual(testData.Count, enumeratedItems.Count, "Not all items were enumerated.");
foreach (var kvp in testData)
{
Assert.IsTrue(enumeratedItems.ContainsKey(kvp.Key) && enumeratedItems[kvp.Key] == kvp.Value,
"Missing or incorrect item in enumeration.");
}
}

[TestMethod]
public void Add_ItemsExceedSingleChunkCapacity_ItemsDistributedAcrossMultipleChunks()
{
var chunkSize = 3;
var itemCount = chunkSize * 2; // Ensure we need at least two chunks
var dictionary = new ChunkedDictionary<int, string>(itemCount, chunkSize);

for (int i = 0; i < itemCount; i++)
{
dictionary.Add(i, $"Item{i}");
}

var chunkCount = dictionary.GetChunkCount();
Assert.IsTrue(chunkCount > 1, "Items should be distributed across more than one chunk.");
}

[TestMethod]
[ExpectedException(typeof(KeyNotFoundException))]
public void Indexer_GetMissingKey_ThrowsKeyNotFoundException()
{
var dictionary = new ChunkedDictionary<int, string>(10);
var value = dictionary[999]; // This should throw
}

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Add_DuplicateKey_ThrowsArgumentException()
{
var dictionary = new ChunkedDictionary<int, string>(10);
dictionary.Add(1, "One");
dictionary.Add(1, "One again"); // This should throw
}

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Init_InvalidChunkSize_ThrowsArgumentException()
{
var dictionary = new ChunkedDictionary<int, string>(10, 11);
}

}
}
Loading

0 comments on commit 7871032

Please sign in to comment.