diff --git a/src/FluentNHibernate.Testing/Cfg/MappingConfigurationTests.cs b/src/FluentNHibernate.Testing/Cfg/MappingConfigurationTests.cs
index 6a337d448..608a3c752 100644
--- a/src/FluentNHibernate.Testing/Cfg/MappingConfigurationTests.cs
+++ b/src/FluentNHibernate.Testing/Cfg/MappingConfigurationTests.cs
@@ -4,6 +4,7 @@
using FluentNHibernate.Cfg;
using FluentNHibernate.Conventions.Helpers;
using FluentNHibernate.Diagnostics;
+using FluentNHibernate.MappingModel.Output;
using FluentNHibernate.Testing.DomainModel;
using FluentNHibernate.Testing.Fixtures;
using NHibernate.Cfg;
@@ -180,5 +181,31 @@ public void MergeOutputShouldSetFlagOnFluentPersistenceModelsOnApply()
model.MergeMappings.ShouldBeTrue();
}
+
+ [Test]
+ public void MappingApplicationStrategyShouldPropagateToPersistenceModelIfSet()
+ {
+ var model = new PersistenceModel();
+ var strategy = new ValidatingHbmMappingApplicationStrategy();
+
+ mapping.UsePersistenceModel(model);
+ mapping.UseMappingApplicationStrategy(strategy);
+ mapping.Apply(new Configuration());
+
+ model.MappingApplicationStrategy.ShouldBeTheSameAs(strategy);
+ }
+
+ [Test]
+ public void MappingApplicationStrategyShouldNotPropagateToPersistenceModelIfEmpty()
+ {
+ var model = new PersistenceModel();
+ var origStrategy = model.MappingApplicationStrategy;
+
+ mapping.UsePersistenceModel(model);
+ mapping.UseMappingApplicationStrategy(null);
+ mapping.Apply(new Configuration());
+
+ model.MappingApplicationStrategy.ShouldBeTheSameAs(origStrategy);
+ }
}
}
\ No newline at end of file
diff --git a/src/FluentNHibernate.Testing/Hbm/HbmConverterTestHelper.cs b/src/FluentNHibernate.Testing/Hbm/HbmConverterTestHelper.cs
new file mode 100644
index 000000000..b592de160
--- /dev/null
+++ b/src/FluentNHibernate.Testing/Hbm/HbmConverterTestHelper.cs
@@ -0,0 +1,661 @@
+using System;
+using System.Collections.Generic;
+using FakeItEasy;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.ClassBased;
+using FluentNHibernate.MappingModel.Collections;
+using FluentNHibernate.MappingModel.Identity;
+using FluentNHibernate.MappingModel.Output;
+using NHibernate.Cfg.MappingSchema;
+
+namespace FluentNHibernate.Testing.Hbm
+{
+ public class HbmConverterTestHelper
+ {
+ ///
+ /// Variant of
+ /// which supports custom construction of both FMain and FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the array which stores the translated subobjects
+ /// is used to construct a new instance of FMain
+ /// is used to construct a new instance of FSub
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsLooselyTypedArray(Func newFMain,
+ Func newFSub, Action addFSubToFMain, Func getHSubSuperFromHMain)
+ where HSub : HSubSuper, new()
+ {
+ // Set up a fake converter that registers any HSub instances it generates and returns in a list
+ var generatedHSubs = new List();
+ var fakeConverter = A.Fake>();
+ A.CallTo(() => fakeConverter.Convert(A.Ignored)).ReturnsLazily(fSub =>
+ {
+ var hSub = new HSub();
+ generatedHSubs.Add(hSub);
+ return hSub;
+ });
+
+ // Set up a custom container with the fake FSub->HSub converter registered, and obtain our main converter from it (so
+ // that it will use the fake implementation). Note that we do the resolution _before_ we register the fake, so that
+ // in cases where we are doing recursive types and FMain == FSub + HMain == HSub (e.g., subclasses-of-subclasses) we
+ // get the real converter for the "outer" call but the fake for any "inner" calls.
+ var container = new HbmConverterContainer();
+ IHbmConverter converter = container.Resolve>();
+ container.Register>(cnvrt => fakeConverter);
+
+ // Allocate a new fluent main object instance, and add a subobject instance to it
+ var fMain = newFMain();
+ addFSubToFMain(fMain, newFSub());
+
+ // Now try to convert it
+ var convertedHMain = converter.Convert(fMain);
+
+ // Finally, check that the array on the converted HMain instance which we expect to contain our converted HSub
+ // instances actually does, and that the converter was called the correct number of times
+ getHSubSuperFromHMain(convertedHMain).ShouldEqual(generatedHSubs.ToArray());
+ A.CallTo(() => fakeConverter.Convert(A.Ignored)).MustHaveHappened(Repeated.Exactly.Once);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FMain.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the array which stores the translated subobjects
+ /// is used to construct a new instance of FMain
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsLooselyTypedArray(Func newFMain, Action addFSubToFMain,
+ Func getHSubSuperFromHMain)
+ where FSub : new()
+ where HSub : HSubSuper, new()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(newFMain, () => new FSub(),
+ addFSubToFMain, getHSubSuperFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the array which stores the translated subobjects
+ /// is used to construct a new instance of FSub
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsLooselyTypedArray(Func newFSub, Action addFSubToFMain,
+ Func getHSubSuperFromHMain)
+ where FMain : new()
+ where HSub : HSubSuper, new()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(() => new FMain(), newFSub,
+ addFSubToFMain, getHSubSuperFromHMain);
+ }
+
+ ///
+ /// Test that a converter correctly handles the translation of a group of subobjects as an array which is broadly typed and can hold those subobjects.
+ ///
+ ///
+ ///
+ /// If the array of translated subobjects is narrowly typed for the subobjects (that is, the type stored in the array is
+ /// exactly the subobject type, not an ancestor of it) then the
+ ///
+ /// method should be used in preference to this one.
+ ///
+ ///
+ ///
+ ///
+ /// converts from to . The following test checks that the
+ /// class subobjects are converted to correctly:
+ ///
+ ///
+ /// ShouldConvertSubobjectsAsLooselyTypedArray<HibernateMapping, ImportMapping, HbmMapping, HbmImport, object>(
+ /// (hibernateMapping, importMapping) => hibernateMapping.AddImport(importMapping),
+ /// hbmMapping => hbmMapping.import
+ /// );
+ ///
+ ///
+ /// Specifically, because the conversion target is typed as object[] rather than HbmClass[] (in this case because the mapping XML specification
+ /// allows other entries to be included), this method must be used rather than
+ /// .
+ ///
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the array which stores the translated subobjects
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsLooselyTypedArray(Action addFSubToFMain,
+ Func getHSubSuperFromHMain)
+ where FMain : new()
+ where FSub : new()
+ where HSub : HSubSuper, new()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(() => new FSub(), addFSubToFMain, getHSubSuperFromHMain);
+ }
+
+ ///
+ /// Test that a converter correctly handles the translation of a group of subobjects as an array which is narrowly typed to hold those subobjects.
+ ///
+ ///
+ ///
+ /// If the array of translated subobjects is broadly typed for the subobjects (that is, the type stored in the array is
+ /// an ancestor of the subobject type, rather than an exact match) then the
+ ///
+ /// method must be used, rather than this one.
+ ///
+ ///
+ ///
+ ///
+ /// converts from to . The following test checks that the
+ /// import subobjects are converted to correctly:
+ ///
+ ///
+ /// ShouldConvertSubobjectsAsStrictlyTypedArray<HibernateMapping, ImportMapping, HbmMapping, HbmImport>(
+ /// (hibernateMapping, importMapping) => hibernateMapping.AddImport(importMapping),
+ /// hbmMapping => hbmMapping.import
+ /// );
+ ///
+ ///
+ /// Specifically, because the conversion target is typed as HbmImport[] rather than some broader type (for example, object[]), this method should
+ /// be used in preference to .
+ ///
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsStrictlyTypedArray(Action addFSubToFMain, Func getHSubFromHMain)
+ where FMain : new()
+ where FSub : new()
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectsAsLooselyTypedArray(addFSubToFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FMain.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// is used to construct a new instance of FMain
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsStrictlyTypedArray(Func newFMain,
+ Action addFSubToFMain, Func getHSubFromHMain)
+ where FSub : new()
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectsAsLooselyTypedArray(newFMain, addFSubToFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// is used to construct a new instance of FSub
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsStrictlyTypedArray(Func newFSub,
+ Action addFSubToFMain, Func getHSubFromHMain)
+ where FMain : new()
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectsAsLooselyTypedArray(newFSub, addFSubToFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of both FMain and FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// is used to construct a new instance of FMain
+ /// is used to construct a new instance of FSub
+ /// A handler which will add a fluent subobject to a fluent main object
+ /// A handler which will retrieve an array of translated subobjects from the translated main object
+ public static void ShouldConvertSubobjectsAsStrictlyTypedArray(Func newFMain, Func newFSub,
+ Action addFSubToFMain, Func getHSubFromHMain)
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectsAsLooselyTypedArray(newFMain, newFSub, addFSubToFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of both FMain and FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the field which stores the translated subobject
+ /// is used to construct a new instance of FMain
+ /// is used to construct a new instance of FSub
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsLooselyTypedField(Func newFMain, Func newFSub,
+ Action setFSubOnFMain, Func getHSubSuperFromHMain)
+ where HSub : HSubSuper, new()
+ {
+ // Set up a fake converter that registers any HSub instances it generates and returns in a list
+ var generatedHSubs = new List();
+ var fakeConverter = A.Fake>();
+ A.CallTo(() => fakeConverter.Convert(A.Ignored)).ReturnsLazily(fSub =>
+ {
+ var hSub = new HSub();
+ generatedHSubs.Add(hSub);
+ return hSub;
+ });
+
+ // Set up a custom container with the fake FSub->HSub converter registered, and obtain our main converter from it (so
+ // that it will use the fake implementation). Note that we do the resolution _before_ we register the fake, so that
+ // in cases where we are doing recursive types and FMain == FSub + HMain == HSub (e.g., subclasses-of-subclasses) we
+ // get the real converter for the "outer" call but the fake for any "inner" calls.
+ var container = new HbmConverterContainer();
+ IHbmConverter converter = container.Resolve>();
+ container.Register>(cnvrt => fakeConverter);
+
+ // Allocate a new fluent main object instance, and add a subobject instance to it
+ var fMain = newFMain();
+ setFSubOnFMain(fMain, newFSub());
+
+ // Now try to convert it
+ var convertedHMain = converter.Convert(fMain);
+
+ // Finally, check that the array on the converted HMain instance which we expect to contain our converted HSub
+ // instances actually does, and that the converter was called the correct number of times
+ A.CallTo(() => fakeConverter.Convert(A.Ignored)).MustHaveHappened(Repeated.Exactly.Once); // Do this first since it guarantees the list should have exactly one item
+ getHSubSuperFromHMain(convertedHMain).ShouldEqual(generatedHSubs[0]);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FMain.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the field which stores the translated subobject
+ /// is used to construct a new instance of FMain
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsLooselyTypedField(Func newFMain, Action setFSubOnFMain,
+ Func getHSubSuperFromHMain)
+ where FSub : new()
+ where HSub : HSubSuper, new()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(newFMain, () => new FSub(), setFSubOnFMain, getHSubSuperFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the field which stores the translated subobject
+ /// is used to construct a new instance of FSub
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsLooselyTypedField(Func newFSub, Action setFSubOnFMain,
+ Func getHSubSuperFromHMain)
+ where FMain : new()
+ where HSub : HSubSuper, new()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(() => new FMain(), newFSub, setFSubOnFMain, getHSubSuperFromHMain);
+ }
+
+ ///
+ /// Test that a converter correctly handles the translation of a single subobject as a field which is broadly typed and can hold the subobject.
+ ///
+ ///
+ ///
+ /// If the translated subobject field is narrowly typed for the subobject (that is, the type stored in the field is exactly
+ /// the subobject type, not an ancestor of it) then the
+ ///
+ /// method should be used in preference to this one.
+ ///
+ ///
+ ///
+ ///
+ /// converts from to . The following tests
+ /// checks that the Id or subobjects are converted
+ /// to correctly:
+ ///
+ ///
+ /// ShouldConvertSubobjectAsLooselyTypedField<ClassMapping, IIdentityMapping, HbmClass, object, object>(
+ /// () => new IdMapping(),
+ /// (classMapping, iidMapping) => classMapping.Set(fluent => fluent.Id, Layer.Conventions, iidMapping),
+ /// hbmClass => hbmClass.Item
+ /// );
+ ///
+ /// ShouldConvertSubobjectAsLooselyTypedField<ClassMapping, IIdentityMapping, HbmClass, object, object>(
+ /// () => new CompositeIdMapping(),
+ /// (classMapping, iidMapping) => classMapping.Set(fluent => fluent.Id, Layer.Conventions, iidMapping),
+ /// hbmClass => hbmClass.Item
+ /// );
+ ///
+ ///
+ /// Specifically, because the conversion target is typed as object rather than HbmCache (in this case because the mapping XML specification
+ /// allows other entries to be included), this method must be used rather than
+ /// .
+ ///
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// the type of the field which stores the translated subobject
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsLooselyTypedField(Action setFSubOnFMain,
+ Func getHSubSuperFromHMain)
+ where FMain : new()
+ where FSub : new()
+ where HSub : HSubSuper, new()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(() => new FSub(), setFSubOnFMain, getHSubSuperFromHMain);
+ }
+
+ ///
+ /// Test that a converter correctly handles the translation of a single subobject as a field which is narrowly typed to hold the subobject.
+ ///
+ ///
+ ///
+ /// If the translated subobject field is broadly typed for the subobject (that is, the type stored in the field is exactly
+ /// the subobject type, not an ancestor of it) then the
+ ///
+ /// method must be used, rather than this one.
+ ///
+ ///
+ ///
+ ///
+ /// converts from to . The following test
+ /// checks that the cache subobject is converted to correctly:
+ ///
+ ///
+ /// ShouldConvertSubobjectAsStrictlyTypedField<ClassMapping, CacheMapping, HbmClass, HbmCache>(
+ /// (classMapping, cacheMapping) => classMapping.Set(fluent => fluent.Cache, Layer.Conventions, cacheMapping),
+ /// hbmClass => hbmClass.cache
+ /// );
+ ///
+ ///
+ /// Specifically, because the conversion target is typed as HbmImport rather than some broader type (for example, object), this method should
+ /// be used in preference to
+ /// .
+ ///
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsStrictlyTypedField(Action setFSubOnFMain, Func getHSubFromHMain)
+ where FMain : new()
+ where FSub : new()
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectAsLooselyTypedField(setFSubOnFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FMain.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// is used to construct a new instance of FMain
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsStrictlyTypedField(Func newFMain, Action setFSubOnFMain,
+ Func getHSubFromHMain)
+ where FSub : new()
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectAsLooselyTypedField(newFMain, setFSubOnFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// is used to construct a new instance of FSub
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsStrictlyTypedField(Func newFSub, Action setFSubOnFMain,
+ Func getHSubFromHMain)
+ where FMain : new()
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectAsLooselyTypedField(newFSub, setFSubOnFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Variant of
+ /// which supports custom construction of both FMain and FSub.
+ ///
+ ///
+ /// the fluent type under test
+ /// the fluent subobject type under test
+ /// the translated (Hibernate) type
+ /// the translated (Hibernate) subobject type
+ /// is used to construct a new instance of FMain
+ /// is used to construct a new instance of FSub
+ /// A handler which will set a fluent subobject on a fluent main object
+ /// A handler which will retrieve a translated subobject from the translated main object
+ public static void ShouldConvertSubobjectAsStrictlyTypedField(Func newFMain, Func newFSub,
+ Action setFSubOnFMain, Func getHSubFromHMain)
+ where HSub : new()
+ {
+ // Strictly typed is just loosely typed with HSubSuper == HSub to restrict it to being exactly HSub
+ ShouldConvertSubobjectAsLooselyTypedField(newFMain, newFSub, setFSubOnFMain, getHSubFromHMain);
+ }
+
+ ///
+ /// Test that a converter which handles a fluent type with children correctly converts a particular input type to the expected output type.
+ ///
+ ///
+ ///
+ /// If the translated object type is determined by an aspect of the mapping object, rather than by inheritance-based logic,
+ /// then should be used instead of this
+ /// method.
+ ///
+ ///
+ ///
+ ///
+ /// converts from to . The
+ /// following calls check that the descendants and are converted
+ /// to and , respectively.
+ ///
+ ///
+ /// ShouldConvertSpecificHbmForMappingChild();
+ /// ShouldConvertSpecificHbmForMappingChild();
+ ///
+ ///
+ ///
+ /// the shared ancestor type under test
+ /// the specific fluent type under test
+ /// the translated (Hibernate) shared ancestor type
+ /// the translated (Hibernate) target type
+ public static void ShouldConvertSpecificHbmForMappingChild()
+ where F : FSuper, new()
+ where H : HSuper, new()
+ {
+ ShouldConvertSpecificHbmForMapping(() => new F());
+ }
+
+ ///
+ /// Test that a converter which handles a fluent type with subtypes correctly converts a particular input subtype to the expected output type.
+ ///
+ ///
+ ///
+ /// If the translated object type is determined by inheritance-based logic rather than by an aspect of the mapping
+ /// object itself, then should be used
+ /// instead of this method.
+ ///
+ ///
+ ///
+ ///
+ /// converts from to , based on the
+ /// value of . The following calls check that the subtypes , , and
+ /// are converted to , , and ,
+ /// respectively.
+ ///
+ ///
+ /// ShouldConvertSpecificHbmForMappingSubtype(
+ /// () => new SubclassMapping(SubclassType.Subclass)
+ /// );
+ /// ShouldConvertSpecificHbmForMappingSubtype(
+ /// () => new SubclassMapping(SubclassType.JoinedSubclass)
+ /// );
+ /// ShouldConvertSpecificHbmForMappingSubtype(
+ /// () => new SubclassMapping(SubclassType.UnionSubclass)
+ /// );
+ ///
+ ///
+ ///
+ /// the specific fluent type under test
+ /// the translated (Hibernate) shared ancestor type
+ /// the translated (Hibernate) target type
+ /// is used to construct a new instance of F (which is generally expected to select the specific
+ /// subtype for the test)
+ public static void ShouldConvertSpecificHbmForMappingSubtype(Func newF)
+ where H : HSuper, new()
+ {
+ /* NOTE: Ideally, this test would register additional converters for all of the other targets and ensure that they
+ * were not called. However, while it is possible to detect all such converters, C#'s generic model combined with
+ * the limitations of FakeItEasy do not provide any straightforward way to register fakes for types that would have
+ * to be dynamically detected at runtime. So at least for now, we don't do that.
+ */
+
+ ShouldConvertSpecificHbmForMapping(newF);
+ }
+
+ ///
+ /// Test that a converter which handles a fluent type with both children and subtypes correctly converts a particular
+ /// input child or subtype to the expected output type.
+ ///
+ ///
+ ///
+ /// This method combines the logic for and
+ /// . Tests that only need to address one
+ /// of the two output selection modes should use one of those methods in preference to this one.
+ ///
+ ///
+ ///
+ ///
+ /// converts from to , based on the
+ /// whether the input is an or an , and in the case of
+ /// whether is specified. The following calls check that all
+ /// three situations are handled correctly.
+ ///
+ ///
+ ///
+ /// ShouldConvertSpecificHbmForMapping(
+ /// () => NewIndexMappingWithNoOffset()
+ /// );
+ /// ShouldConvertSpecificHbmForMapping(
+ /// () => NewIndexMappingWithOffset()
+ /// );
+ /// ShouldConvertSpecificHbmForMappingChild();
+ ///
+ ///
+ ///
+ ///
+ /// the shared ancestor type under test
+ /// the specific fluent type under test
+ /// the translated (Hibernate) shared ancestor type
+ /// the translated (Hibernate) target type
+ /// is used to construct a new instance of F (which is generally expected to select the
+ /// specific subtype for the test, where relevant)
+ public static void ShouldConvertSpecificHbmForMapping(Func newF)
+ where F: FSuper
+ where H : HSuper, new()
+ {
+ // Set up a fake converter that registers any H instances it generates and returns in a list
+ var generatedHbms = new List();
+ var fakeConverter = A.Fake>();
+ A.CallTo(() => fakeConverter.Convert(A.Ignored)).ReturnsLazily(fSub =>
+ {
+ var hbm = new H();
+ generatedHbms.Add(hbm);
+ return hbm;
+ });
+
+ // Set up a custom container with the fake F->H converter registered, and obtain our main converter from it (so
+ // that it will use the fake implementation). Note that we do the resolution _before_ we register the fake, so that
+ // in cases where we are doing recursive types and HSuper == H we get the real converter for the "outer" call but the
+ // fake for any "inner" calls.
+ var container = new HbmConverterContainer();
+ IHbmConverter converter = container.Resolve>();
+ container.Register>(cnvrt => fakeConverter);
+
+ // Allocate an instance of the descendant type, but explicitly label it as the ancestor type to ensure that we pass it correctly
+ F mapping = newF();
+
+ // Now try to convert it
+ var convertedHbm = converter.Convert(mapping);
+
+ // Check that the converter for the specific descendant was invoked the correct number of times and that the returned value is the converted instance.
+ A.CallTo(() => fakeConverter.Convert(A.Ignored)).MustHaveHappened(Repeated.Exactly.Once); // Do this first since it guarantees the list should have exactly one item
+ convertedHbm.ShouldEqual(generatedHbms[0]);
+ }
+ }
+}
diff --git a/src/FluentNHibernate.Testing/Mapping/TypeMappingTests.cs.backup b/src/FluentNHibernate.Testing/Mapping/TypeMappingTests.cs.backup
new file mode 100644
index 000000000..75a78fe01
--- /dev/null
+++ b/src/FluentNHibernate.Testing/Mapping/TypeMappingTests.cs.backup
@@ -0,0 +1,21 @@
+using FluentNHibernate.Mapping;
+using NUnit.Framework;
+
+namespace FluentNHibernate.Testing.Mapping
+{
+ [TestFixture]
+ public class TypeMappingTests
+ {
+ [Test]
+ public void GetStringTypeDoesNotDuplicateStrings()
+ {
+ var typeString1 = TypeMapping.GetTypeString(typeof(TypeMappingTests));
+ var typeString2 = TypeMapping.GetTypeString(typeof(TypeMappingTests));
+
+ // First make sure that the strings are equal, otherwise something has gone _very_ strong
+ typeString1.ShouldEqual(typeString2);
+ // Now make sure that they are the same object, not just equal
+ typeString1.ShouldBeTheSameAs(typeString2);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FluentNHibernate.Testing/MappingModel/ClassMappingTester.cs b/src/FluentNHibernate.Testing/MappingModel/ClassMappingTester.cs
index d9229da65..cb86bfa38 100644
--- a/src/FluentNHibernate.Testing/MappingModel/ClassMappingTester.cs
+++ b/src/FluentNHibernate.Testing/MappingModel/ClassMappingTester.cs
@@ -1,9 +1,11 @@
-using System.Linq;
+using System;
+using System.Linq;
using FluentNHibernate.MappingModel;
using FluentNHibernate.MappingModel.ClassBased;
using FluentNHibernate.MappingModel.Identity;
using FluentNHibernate.Visitors;
using FakeItEasy;
+using FluentNHibernate.MappingModel.Collections;
using NUnit.Framework;
namespace FluentNHibernate.Testing.MappingModel
@@ -47,6 +49,46 @@ public void CanAddProperty()
mapping.Properties.ShouldContain(property);
}
+ [Test]
+ public void ShouldFailToAddDuplicateProperty()
+ {
+ var property1 = new PropertyMapping();
+ property1.Set(x => x.Name, Layer.Defaults, "Property1");
+ mapping.AddProperty(property1);
+
+ var property2 = new PropertyMapping();
+ property2.Set(x => x.Name, Layer.Defaults, property1.Name);
+ Assert.Throws(() => mapping.AddProperty(property2));
+
+ mapping.Properties.ShouldContain(property => ReferenceEquals(property, property1));
+ mapping.Properties.ShouldNotContain(property => ReferenceEquals(property, property2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewProperty()
+ {
+ var property1 = new PropertyMapping();
+ property1.Set(x => x.Name, Layer.Defaults, "Property1");
+ mapping.AddOrReplaceProperty(property1);
+
+ mapping.Properties.ShouldContain(property => ReferenceEquals(property, property1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingProperty()
+ {
+ var property1 = new PropertyMapping();
+ property1.Set(x => x.Name, Layer.Defaults, "Property1");
+ mapping.AddOrReplaceProperty(property1);
+
+ var property2 = new PropertyMapping();
+ property2.Set(x => x.Name, Layer.Defaults, property1.Name);
+ mapping.AddOrReplaceProperty(property2);
+
+ mapping.Properties.ShouldNotContain(property => ReferenceEquals(property, property1));
+ mapping.Properties.ShouldContain(property => ReferenceEquals(property, property2));
+ }
+
[Test]
public void CanAddReference()
{
@@ -57,6 +99,46 @@ public void CanAddReference()
mapping.References.ShouldContain(reference);
}
+ [Test]
+ public void ShouldFailToAddDuplicateReference()
+ {
+ var reference1 = new ManyToOneMapping();
+ reference1.Set(x => x.Name, Layer.Defaults, "parent");
+ mapping.AddReference(reference1);
+
+ var reference2 = new ManyToOneMapping();
+ reference2.Set(x => x.Name, Layer.Defaults, reference1.Name);
+ Assert.Throws(() => mapping.AddReference(reference2));
+
+ mapping.References.ShouldContain(reference => ReferenceEquals(reference, reference1));
+ mapping.References.ShouldNotContain(reference => ReferenceEquals(reference, reference2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewReference()
+ {
+ var reference1 = new ManyToOneMapping();
+ reference1.Set(x => x.Name, Layer.Defaults, "Reference1");
+ mapping.AddOrReplaceReference(reference1);
+
+ mapping.References.ShouldContain(reference => ReferenceEquals(reference, reference1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingReference()
+ {
+ var reference1 = new ManyToOneMapping();
+ reference1.Set(x => x.Name, Layer.Defaults, "Reference1");
+ mapping.AddOrReplaceReference(reference1);
+
+ var reference2 = new ManyToOneMapping();
+ reference2.Set(x => x.Name, Layer.Defaults, reference1.Name);
+ mapping.AddOrReplaceReference(reference2);
+
+ mapping.References.ShouldNotContain(reference => ReferenceEquals(reference, reference1));
+ mapping.References.ShouldContain(reference => ReferenceEquals(reference, reference2));
+ }
+
[Test]
public void Should_pass_id_to_the_visitor()
{
@@ -86,11 +168,12 @@ public void Should_not_pass_null_id_to_the_visitor()
}
[Test]
- public void Can_add_subclass()
+ public void CanAddSubclass()
{
- var joinedSubclass = new SubclassMapping(SubclassType.JoinedSubclass);
- mapping.AddSubclass(joinedSubclass);
- mapping.Subclasses.ShouldContain(joinedSubclass);
+ var subclass = new SubclassMapping(SubclassType.JoinedSubclass);
+ mapping.AddSubclass(subclass);
+
+ mapping.Subclasses.ShouldContain(subclass);
}
[Test]
@@ -113,13 +196,37 @@ public void Should_pass_subclasses_to_the_visitor()
}
[Test]
- public void Can_add_stored_procedure()
+ public void CanAddStoredProcedure()
{
var storedProcedure = new StoredProcedureMapping();
mapping.AddStoredProcedure(storedProcedure);
+
mapping.StoredProcedures.ShouldContain(storedProcedure);
}
+ [Test]
+ public void CanAddDuplicateStoredProcedure()
+ {
+ // Unlike other types, stored procedures do allow duplicate entries. However, in order to distinguish
+ // whether two entries are identical or not, we need to set some non-identity attribute on them to be
+ // a different value. For this test, "Query" is easy enough.
+ var storedProcedure1 = new StoredProcedureMapping();
+ storedProcedure1.Set(x => x.Name, Layer.Defaults, "storedProcedure1");
+ storedProcedure1.Set(x => x.Query, Layer.Defaults, "x=y");
+ mapping.AddStoredProcedure(storedProcedure1);
+
+ var storedProcedure2 = new StoredProcedureMapping();
+ storedProcedure1.Set(x => x.Name, Layer.Defaults, storedProcedure1.Name);
+ storedProcedure1.Set(x => x.Query, Layer.Defaults, "x=y");
+
+ // Check that adding the duplicate does _not_ throw
+ mapping.AddStoredProcedure(storedProcedure2);
+
+ // Now check that both are present in stored procedures tracker
+ mapping.StoredProcedures.ShouldContain(storedProcedure => ReferenceEquals(storedProcedure, storedProcedure1));
+ mapping.StoredProcedures.ShouldContain(storedProcedure => ReferenceEquals(storedProcedure, storedProcedure2));
+ }
+
[Test]
public void Should_pass_stored_procedure_to_the_visitor()
{
@@ -147,5 +254,305 @@ public void Should_pass_the_discriminator_to_the_visitor()
A.CallTo(() => visitor.Visit(classMap.Discriminator)).MustHaveHappened();
}
+
+ [Test]
+ public void CanAddCollection()
+ {
+ var collection = CollectionMapping.Bag();
+ collection.Set(x => x.Name, Layer.Defaults, "Collection1");
+ mapping.AddCollection(collection);
+
+ mapping.Collections.ShouldContain(collection);
+ }
+
+ [Test]
+ public void ShouldFailToAddDuplicateCollection()
+ {
+ var collection1 = CollectionMapping.Bag();
+ collection1.Set(x => x.Name, Layer.Defaults, "Collection1");
+ mapping.AddCollection(collection1);
+
+ var collection2 = CollectionMapping.Bag();
+ collection2.Set(x => x.Name, Layer.Defaults, collection1.Name);
+ Assert.Throws(() => mapping.AddCollection(collection2));
+
+ mapping.Collections.ShouldContain(collection => ReferenceEquals(collection, collection1));
+ mapping.Collections.ShouldNotContain(collection => ReferenceEquals(collection, collection2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewCollection()
+ {
+ var collection1 = CollectionMapping.Bag();
+ collection1.Set(x => x.Name, Layer.Defaults, "Collection1");
+ mapping.AddOrReplaceCollection(collection1);
+
+ mapping.Collections.ShouldContain(collection => ReferenceEquals(collection, collection1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingCollection()
+ {
+ var collection1 = CollectionMapping.Bag();
+ collection1.Set(x => x.Name, Layer.Defaults, "Collection1");
+ mapping.AddOrReplaceCollection(collection1);
+
+ var collection2 = CollectionMapping.Bag();
+ collection2.Set(x => x.Name, Layer.Defaults, collection1.Name);
+ mapping.AddOrReplaceCollection(collection2);
+
+ mapping.Collections.ShouldNotContain(collection => ReferenceEquals(collection, collection1));
+ mapping.Collections.ShouldContain(collection => ReferenceEquals(collection, collection2));
+ }
+
+ [Test]
+ public void CanAddComponent()
+ {
+ var component = new ComponentMapping(ComponentType.Component);
+ component.Set(x => x.Name, Layer.Defaults, "Component1");
+ mapping.AddComponent(component);
+
+ mapping.Components.ShouldContain(component);
+ }
+
+ [Test]
+ public void ShouldFailToAddDuplicateComponent()
+ {
+ var component1 = new ComponentMapping(ComponentType.Component);
+ component1.Set(x => x.Name, Layer.Defaults, "Component1");
+ mapping.AddComponent(component1);
+
+ var component2 = new ComponentMapping(ComponentType.Component);
+ component2.Set(x => x.Name, Layer.Defaults, component1.Name);
+ Assert.Throws(() => mapping.AddComponent(component2));
+
+ mapping.Components.ShouldContain(component => ReferenceEquals(component, component1));
+ mapping.Components.ShouldNotContain(component => ReferenceEquals(component, component2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewComponent()
+ {
+ var component1 = new ComponentMapping(ComponentType.Component);
+ component1.Set(x => x.Name, Layer.Defaults, "Component1");
+ mapping.AddOrReplaceComponent(component1);
+
+ mapping.Components.ShouldContain(component => ReferenceEquals(component, component1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingComponent()
+ {
+ var component1 = new ComponentMapping(ComponentType.Component);
+ component1.Set(x => x.Name, Layer.Defaults, "Component1");
+ mapping.AddOrReplaceComponent(component1);
+
+ var component2 = new ComponentMapping(ComponentType.Component);
+ component2.Set(x => x.Name, Layer.Defaults, component1.Name);
+ mapping.AddOrReplaceComponent(component2);
+
+ mapping.Components.ShouldNotContain(component => ReferenceEquals(component, component1));
+ mapping.Components.ShouldContain(component => ReferenceEquals(component, component2));
+ }
+
+ [Test]
+ public void CanAddOneToOne()
+ {
+ var oneToOne = new OneToOneMapping();
+ oneToOne.Set(x => x.Name, Layer.Defaults, "OneToOne1");
+ mapping.AddOneToOne(oneToOne);
+
+ mapping.OneToOnes.ShouldContain(oneToOne);
+ }
+
+ [Test]
+ public void ShouldFailToAddDuplicateOneToOne()
+ {
+ var oneToOne1 = new OneToOneMapping();
+ oneToOne1.Set(x => x.Name, Layer.Defaults, "OneToOne1");
+ mapping.AddOneToOne(oneToOne1);
+
+ var oneToOne2 = new OneToOneMapping();
+ oneToOne2.Set(x => x.Name, Layer.Defaults, oneToOne1.Name);
+ Assert.Throws(() => mapping.AddOneToOne(oneToOne2));
+
+ mapping.OneToOnes.ShouldContain(oneToOne => ReferenceEquals(oneToOne, oneToOne1));
+ mapping.OneToOnes.ShouldNotContain(oneToOne => ReferenceEquals(oneToOne, oneToOne2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewOneToOne()
+ {
+ var oneToOne1 = new OneToOneMapping();
+ oneToOne1.Set(x => x.Name, Layer.Defaults, "OneToOne1");
+ mapping.AddOrReplaceOneToOne(oneToOne1);
+
+ mapping.OneToOnes.ShouldContain(oneToOne => ReferenceEquals(oneToOne, oneToOne1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingOneToOne()
+ {
+ var oneToOne1 = new OneToOneMapping();
+ oneToOne1.Set(x => x.Name, Layer.Defaults, "OneToOne1");
+ mapping.AddOrReplaceOneToOne(oneToOne1);
+
+ var oneToOne2 = new OneToOneMapping();
+ oneToOne2.Set(x => x.Name, Layer.Defaults, oneToOne1.Name);
+ mapping.AddOrReplaceOneToOne(oneToOne2);
+
+ mapping.OneToOnes.ShouldNotContain(oneToOne => ReferenceEquals(oneToOne, oneToOne1));
+ mapping.OneToOnes.ShouldContain(oneToOne => ReferenceEquals(oneToOne, oneToOne2));
+ }
+
+ [Test]
+ public void CanAddAny()
+ {
+ var any = new AnyMapping();
+ any.Set(x => x.Name, Layer.Defaults, "Any1");
+ mapping.AddAny(any);
+
+ mapping.Anys.ShouldContain(any);
+ }
+
+ [Test]
+ public void ShouldFailToAddDuplicateAny()
+ {
+ var any1 = new AnyMapping();
+ any1.Set(x => x.Name, Layer.Defaults, "Any1");
+ mapping.AddAny(any1);
+
+ var any2 = new AnyMapping();
+ any2.Set(x => x.Name, Layer.Defaults, any1.Name);
+ Assert.Throws(() => mapping.AddAny(any2));
+
+ mapping.Anys.ShouldContain(any => ReferenceEquals(any, any1));
+ mapping.Anys.ShouldNotContain(any => ReferenceEquals(any, any2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewAny()
+ {
+ var any1 = new AnyMapping();
+ any1.Set(x => x.Name, Layer.Defaults, "Any1");
+ mapping.AddOrReplaceAny(any1);
+
+ mapping.Anys.ShouldContain(any => ReferenceEquals(any, any1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingAny()
+ {
+ var any1 = new AnyMapping();
+ any1.Set(x => x.Name, Layer.Defaults, "Any1");
+ mapping.AddOrReplaceAny(any1);
+
+ var any2 = new AnyMapping();
+ any2.Set(x => x.Name, Layer.Defaults, any1.Name);
+ mapping.AddOrReplaceAny(any2);
+
+ mapping.Anys.ShouldNotContain(any => ReferenceEquals(any, any1));
+ mapping.Anys.ShouldContain(any => ReferenceEquals(any, any2));
+ }
+
+ [Test]
+ public void CanAddJoin()
+ {
+ var join = new JoinMapping();
+ join.Set(x => x.TableName, Layer.Defaults, "TableName1");
+ mapping.AddJoin(join);
+
+ mapping.Joins.ShouldContain(join);
+ }
+
+ [Test]
+ public void ShouldFailToAddDuplicateJoin()
+ {
+ var join1 = new JoinMapping();
+ join1.Set(x => x.TableName, Layer.Defaults, "TableName1");
+ mapping.AddJoin(join1);
+
+ var join2 = new JoinMapping();
+ join2.Set(x => x.TableName, Layer.Defaults, join1.TableName);
+ Assert.Throws(() => mapping.AddJoin(join2));
+
+ mapping.Joins.ShouldContain(join => ReferenceEquals(join, join1));
+ mapping.Joins.ShouldNotContain(join => ReferenceEquals(join, join2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewJoin()
+ {
+ var join1 = new JoinMapping();
+ join1.Set(x => x.TableName, Layer.Defaults, "Join1");
+ mapping.AddOrReplaceJoin(join1);
+
+ mapping.Joins.ShouldContain(join => ReferenceEquals(join, join1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingJoin()
+ {
+ var join1 = new JoinMapping();
+ join1.Set(x => x.TableName, Layer.Defaults, "Join1");
+ mapping.AddOrReplaceJoin(join1);
+
+ var join2 = new JoinMapping();
+ join2.Set(x => x.TableName, Layer.Defaults, join1.TableName);
+ mapping.AddOrReplaceJoin(join2);
+
+ mapping.Joins.ShouldNotContain(join => ReferenceEquals(join, join1));
+ mapping.Joins.ShouldContain(join => ReferenceEquals(join, join2));
+ }
+
+ [Test]
+ public void CanAddFilter()
+ {
+ var filter = new FilterMapping();
+ filter.Set(x => x.Name, Layer.Defaults, "Filter1");
+ mapping.AddFilter(filter);
+
+ mapping.Filters.ShouldContain(filter);
+ }
+
+ [Test]
+ public void ShouldFailToAddDuplicateFilter()
+ {
+ var filter1 = new FilterMapping();
+ filter1.Set(x => x.Name, Layer.Defaults, "Filter1");
+ mapping.AddFilter(filter1);
+
+ var filter2 = new FilterMapping();
+ filter2.Set(x => x.Name, Layer.Defaults, filter1.Name);
+ Assert.Throws(() => mapping.AddFilter(filter2));
+
+ mapping.Filters.ShouldContain(filter => ReferenceEquals(filter, filter1));
+ mapping.Filters.ShouldNotContain(filter => ReferenceEquals(filter, filter2));
+ }
+
+ [Test]
+ public void CanAddOrReplaceNewFilter()
+ {
+ var filter1 = new FilterMapping();
+ filter1.Set(x => x.Name, Layer.Defaults, "Filter1");
+ mapping.AddOrReplaceFilter(filter1);
+
+ mapping.Filters.ShouldContain(filter => ReferenceEquals(filter, filter1));
+ }
+
+ [Test]
+ public void CanAddOrReplaceExistingFilter()
+ {
+ var filter1 = new FilterMapping();
+ filter1.Set(x => x.Name, Layer.Defaults, "Filter1");
+ mapping.AddOrReplaceFilter(filter1);
+
+ var filter2 = new FilterMapping();
+ filter2.Set(x => x.Name, Layer.Defaults, filter1.Name);
+ mapping.AddOrReplaceFilter(filter2);
+
+ mapping.Filters.ShouldNotContain(filter => ReferenceEquals(filter, filter1));
+ mapping.Filters.ShouldContain(filter => ReferenceEquals(filter, filter2));
+ }
}
}
\ No newline at end of file
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmAnyConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmAnyConverterTester.cs
new file mode 100644
index 000000000..9e16c08f9
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmAnyConverterTester.cs
@@ -0,0 +1,247 @@
+using System.Collections.Generic;
+using System.Linq;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.Output;
+using NUnit.Framework;
+using NHibernate.Cfg.MappingSchema;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmAnyConverterTester
+ {
+ private IHbmConverter converter;
+
+ [SetUp]
+ public void GetConverterFromContainer()
+ {
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ }
+
+ [Test]
+ public void ShouldConvertIdTypeIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.IdType, Layer.Conventions, "id");
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.idtype.ShouldEqual(anyMapping.IdType);
+ }
+
+ [Test]
+ public void ShouldNotConvertIdTypeIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.idtype.ShouldEqual(blankHbmAny.idtype);
+ }
+
+ [Test]
+ public void ShouldConvertMetaTypeIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.MetaType, Layer.Conventions, new TypeReference("meta"));
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.metatype.ShouldEqual(anyMapping.MetaType.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertMetaTypeIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.metatype.ShouldEqual(blankHbmAny.metatype);
+ }
+
+ [Test]
+ public void ShouldConvertNameIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.Name, Layer.Conventions, "nm");
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.name.ShouldEqual(anyMapping.Name);
+ }
+
+ [Test]
+ public void ShouldNotConvertNameIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.name.ShouldEqual(blankHbmAny.name);
+ }
+
+ [Test]
+ public void ShouldConvertAccessIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.Access, Layer.Conventions, "access");
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.access.ShouldEqual(anyMapping.Access);
+ }
+
+ [Test]
+ public void ShouldNotConvertAccessIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.access.ShouldEqual(blankHbmAny.access);
+ }
+
+ [Test]
+ public void ShouldConvertInsertIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.Insert, Layer.Conventions, false); // Defaults to true, so use this to ensure that we can detect changes
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.insert.ShouldEqual(anyMapping.Insert);
+ }
+
+ [Test]
+ public void ShouldNotConvertInsertIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.insert.ShouldEqual(blankHbmAny.insert);
+ }
+
+ [Test]
+ public void ShouldConvertUpdateIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.Update, Layer.Conventions, false); // Defaults to true, so use this to ensure that we can detect changes
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.update.ShouldEqual(anyMapping.Update);
+ }
+
+ [Test]
+ public void ShouldNotConvertUpdateIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.update.ShouldEqual(blankHbmAny.update);
+ }
+
+ [Test]
+ public void ShouldConvertCascadeIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.Cascade, Layer.Conventions, "all");
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.cascade.ShouldEqual(anyMapping.Cascade);
+ }
+
+ [Test]
+ public void ShouldNotConvertCascadeIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.cascade.ShouldEqual(blankHbmAny.cascade);
+ }
+
+ [Test]
+ public void ShouldConvertLazyIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.Lazy, Layer.Conventions, true); // Defaults to false, so use this to ensure that we can detect changes
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.lazy.ShouldEqual(anyMapping.Lazy);
+ }
+
+ [Test]
+ public void ShouldNotConvertLazyIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.lazy.ShouldEqual(blankHbmAny.lazy);
+ }
+
+ [Test]
+ public void ShouldConvertOptimisticLockIfPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ anyMapping.Set(fluent => fluent.OptimisticLock, Layer.Conventions, false); // Defaults to true, so use this to ensure that we can detect changes
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.optimisticlock.ShouldEqual(anyMapping.OptimisticLock);
+ }
+
+ [Test]
+ public void ShouldNotConvertOptimisticLockIfNotPopulated()
+ {
+ var anyMapping = new AnyMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmAny = converter.Convert(anyMapping);
+ var blankHbmAny = new HbmAny();
+ convertedHbmAny.optimisticlock.ShouldEqual(blankHbmAny.optimisticlock);
+ }
+
+ [Test]
+ public void ShouldConvertTypeColumns()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new ColumnMapping("typeColumn"),
+ (anyMapping, columnMapping) => anyMapping.AddTypeColumn(Layer.Conventions, columnMapping),
+ hbmAny => hbmAny.column);
+ }
+
+ [Test]
+ public void ShouldConvertIdentifierColumns()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new ColumnMapping("idColumn"),
+ (anyMapping, columnMapping) => anyMapping.AddIdentifierColumn(Layer.Conventions, columnMapping),
+ hbmAny => hbmAny.column);
+ }
+
+ [Test]
+ public void ShouldConvertTypeColumnsBeforeIdentifierColumns()
+ {
+ // NOTE: Unlike most subobject conversion tests, this test needs to use a real column converter rather than a mock,
+ // so that the converted columns can be properly identified from their names (in order to determine whether they
+ // come through in the correct order). While it would be possible to write a fake converter that would do that, it
+ // wouldn't really be significantly simpler than the real implementation, so it isn't really worth the bother.
+ //
+ // NOTE: This test probably really doesn't belong in this location, since the logic involved is actually controlled
+ // by the visiting order (when visited, the column mapping handler has no idea whether any given column is from the
+ // identifier column or type column sets). Keeping it here for now largely because it exists on the XML variant and
+ // we need to be sure that _something_ is testing for it.
+
+ var anyMapping = new AnyMapping();
+
+ // Set up interleaved ID and type columns and list at least one ID column first, to make it as difficult as possible to get right.
+ // Note that we don't mis-order the "1"/"2" variants because there is no natural ordering to either type of column, so simply
+ // preserving the order they were added in is the best option.
+ anyMapping.AddIdentifierColumn(Layer.Defaults, new ColumnMapping("idColumn1"));
+ anyMapping.AddTypeColumn(Layer.Defaults, new ColumnMapping("typeColumn1"));
+ anyMapping.AddIdentifierColumn(Layer.Defaults, new ColumnMapping("idColumn2"));
+ anyMapping.AddTypeColumn(Layer.Defaults, new ColumnMapping("typeColumn2"));
+
+ var convertedHbmAny = converter.Convert(anyMapping);
+ convertedHbmAny.column.Select(any => any.name).ToList().ShouldEqual(new List { "typeColumn1", "typeColumn2", "idColumn1", "idColumn2" });
+ }
+
+ [Test]
+ public void ShouldConvertMetaValues()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ (anyMapping, metaValueMapping) => anyMapping.AddMetaValue(metaValueMapping),
+ hbmAny => hbmAny.metavalue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmArrayConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmArrayConverterTester.cs
new file mode 100644
index 000000000..6ef618180
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmArrayConverterTester.cs
@@ -0,0 +1,494 @@
+using System;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.Collections;
+using FluentNHibernate.MappingModel.Output;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmArrayConverterTester
+ {
+ private IHbmConverter converter;
+
+ [SetUp]
+ public void GetConverterFromContainer()
+ {
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ }
+
+ #region Base collection attribute value field tests
+
+ [Test]
+ public void ShouldConvertAccessIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Access, Layer.Conventions, "acc");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.access.ShouldEqual(arrayMapping.Access);
+ }
+
+ [Test]
+ public void ShouldNotConvertAccessIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.access.ShouldEqual(blankHbmArray.access);
+ }
+
+ [Test]
+ public void ShouldConvertBatchSizeIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.BatchSize, Layer.Conventions, 10);
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.batchsize.ShouldEqual(arrayMapping.BatchSize);
+ Assert.That(convertedHbmArray.batchsizeSpecified.Equals(true), "Batch size was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertBatchSizeIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.batchsize.ShouldEqual(blankHbmArray.batchsize);
+ Assert.That(convertedHbmArray.batchsizeSpecified.Equals(false), "Batch size was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertCascadeIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Cascade, Layer.Conventions, "all");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.cascade.ShouldEqual(arrayMapping.Cascade);
+ }
+
+ [Test]
+ public void ShouldNotConvertCascadeIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.cascade.ShouldEqual(blankHbmArray.cascade);
+ }
+
+ [Test]
+ public void ShouldConvertCheckIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Check, Layer.Conventions, "chk");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.check.ShouldEqual(arrayMapping.Check);
+ }
+
+ [Test]
+ public void ShouldNotConvertCheckIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.check.ShouldEqual(blankHbmArray.check);
+ }
+
+ [Test]
+ public void ShouldConvertCollectionTypeIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.CollectionType, Layer.Conventions, new TypeReference("type"));
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.collectiontype.ShouldEqual(arrayMapping.CollectionType.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertCollectionTypeIfEmpty()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Array an explicitly empty type reference
+ arrayMapping.Set(fluent => fluent.CollectionType, Layer.Conventions, TypeReference.Empty);
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.collectiontype.ShouldEqual(blankHbmArray.collectiontype);
+ }
+
+ [Test]
+ public void ShouldNotConvertCollectionTypeIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.collectiontype.ShouldEqual(blankHbmArray.collectiontype);
+ }
+
+ [Test]
+ public void ShouldConvertFetchIfPopulatedWithValidValue()
+ {
+ var fetch = HbmCollectionFetchMode.Subselect; // Defaults to Select, so use something else to properly detect that it changes
+
+ var arrayMapping = CollectionMapping.Array();
+ var fetchDict = new XmlLinkedEnumBiDictionary();
+ arrayMapping.Set(fluent => fluent.Fetch, Layer.Conventions, fetchDict[fetch]);
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.fetch.ShouldEqual(fetch);
+ Assert.That(convertedHbmArray.fetchSpecified.Equals(true), "Fetch was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldFailToConvertFetchIfPopulatedWithInvalidValue()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Fetch, Layer.Conventions, "invalid_value");
+ Assert.Throws(() => converter.Convert(arrayMapping));
+ }
+
+ [Test]
+ public void ShouldNotConvertFetchIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.fetch.ShouldEqual(blankHbmArray.fetch);
+ Assert.That(convertedHbmArray.fetchSpecified.Equals(false), "Fetch was marked as specified");
+ }
+
+ // HbmArray, unlike HbmList, doesn't support the generic attribute
+ /*
+ [Test]
+ public void ShouldConvertGenericIfPopulated_True()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Generic, Layer.Conventions, true);
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.generic.ShouldEqual(arrayMapping.Generic);
+ Assert.That(convertedHbmArray.genericSpecified.Equals(true), "Generic was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertGenericIfPopulated_False()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Generic, Layer.Conventions, false);
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.generic.ShouldEqual(arrayMapping.Generic);
+ Assert.That(convertedHbmArray.genericSpecified.Equals(true), "Generic was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertGenericIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.generic.ShouldEqual(blankHbmArray.generic);
+ Assert.That(convertedHbmArray.genericSpecified.Equals(false), "Generic was marked as specified");
+ }
+ */
+
+ [Test]
+ public void ShouldConvertInverseIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Inverse, Layer.Conventions, true); // Defaults to false, so use this to ensure that we can detect changes
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.inverse.ShouldEqual(arrayMapping.Inverse);
+ }
+
+ [Test]
+ public void ShouldNotConvertInverseIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.inverse.ShouldEqual(blankHbmArray.inverse);
+ }
+
+ // HbmArray, unlike HbmList, doesn't support the lazy attribute
+ /*
+ [Test]
+ public void ShouldConvertLazyIfPopulated()
+ {
+ var hbmLazy = HbmCollectionLazy.False; // Defaults to True, so use something else to properly detect that it changes
+
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Lazy, Layer.Conventions, HbmCollectionConverter.FluentHbmLazyBiDict[hbmLazy]);
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.lazy.ShouldEqual(hbmLazy);
+ Assert.That(convertedHbmArray.lazySpecified.Equals(true), "Lazy was not marked as specified");
+ }
+
+ // Since it is enum-based, Lazy cannot contain any invalid values, so no need to test for that here
+
+ [Test]
+ public void ShouldNotConvertLazyIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.lazy.ShouldEqual(blankHbmArray.lazy);
+ Assert.That(convertedHbmArray.lazySpecified.Equals(false), "Lazy was marked as specified");
+ }
+ */
+
+ [Test]
+ public void ShouldConvertNameIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Name, Layer.Conventions, "name");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.name.ShouldEqual(arrayMapping.Name);
+ }
+
+ [Test]
+ public void ShouldNotConvertNameIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.name.ShouldEqual(blankHbmArray.name);
+ }
+
+ [Test]
+ public void ShouldConvertOptimisticLockIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.OptimisticLock, Layer.Conventions, false); // Defaults to true, so use this to ensure that we can detect changes
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.optimisticlock.ShouldEqual(arrayMapping.OptimisticLock);
+ }
+
+ [Test]
+ public void ShouldNotConvertOptimisticLockIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.optimisticlock.ShouldEqual(blankHbmArray.optimisticlock);
+ }
+
+ [Test]
+ public void ShouldConvertPersisterIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Persister, Layer.Conventions, new TypeReference(typeof(string)));
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.persister.ShouldEqual(arrayMapping.Persister.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertPersisterIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.persister.ShouldEqual(blankHbmArray.persister);
+ }
+
+ [Test]
+ public void ShouldConvertSchemaIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Schema, Layer.Conventions, "dbo");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.schema.ShouldEqual(arrayMapping.Schema);
+ }
+
+ [Test]
+ public void ShouldNotConvertSchemaIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.schema.ShouldEqual(blankHbmArray.schema);
+ }
+
+ [Test]
+ public void ShouldConvertTableNameIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.TableName, Layer.Conventions, "tbl");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.table.ShouldEqual(arrayMapping.TableName);
+ }
+
+ [Test]
+ public void ShouldNotConvertTableNameIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.table.ShouldEqual(blankHbmArray.table);
+ }
+
+ [Test]
+ public void ShouldConvertWhereIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Where, Layer.Conventions, "x = 1");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.where.ShouldEqual(arrayMapping.Where);
+ }
+
+ [Test]
+ public void ShouldNotConvertWhereIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.where.ShouldEqual(blankHbmArray.where);
+ }
+
+ [Test]
+ public void ShouldConvertSubselectIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Subselect, Layer.Conventions, "val");
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.subselect.Text.ShouldEqual(new string[] { arrayMapping.Subselect });
+ }
+
+ [Test]
+ public void ShouldNotConvertSubselectIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't array anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.subselect.ShouldEqual(blankHbmArray.subselect);
+ }
+
+ [Test]
+ public void ShouldConvertMutableIfPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ arrayMapping.Set(fluent => fluent.Mutable, Layer.Conventions, false); // Defaults to true, so use this to ensure that we can detect changes
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ convertedHbmArray.mutable.ShouldEqual(arrayMapping.Mutable);
+ }
+
+ [Test]
+ public void ShouldNotConvertMutableIfNotPopulated()
+ {
+ var arrayMapping = CollectionMapping.Array();
+ // Don't set anything on the original mapping
+ var convertedHbmArray = converter.Convert(arrayMapping);
+ var blankHbmArray = new HbmArray();
+ convertedHbmArray.mutable.ShouldEqual(blankHbmArray.mutable);
+ }
+
+ #endregion Base collection attribute value field tests
+
+ #region Type-specific collection attribute value field tests
+
+ // No tests for this type
+
+ #endregion Type-specific collection attribute value field tests
+
+ #region Base collection converter-based subobject tests
+
+ [Test]
+ public void ShouldConvertKey()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => CollectionMapping.Array(),
+ (arrayMapping, keyMapping) => arrayMapping.Set(fluent => fluent.Key, Layer.Defaults, keyMapping),
+ hbmArray => hbmArray.key);
+ }
+
+ [Test]
+ public void ShouldConvertICollectionRelationship_OneToMany()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Array(),
+ () => new OneToManyMapping(),
+ (arrayMapping, icrMapping) => arrayMapping.Set(fluent => fluent.Relationship, Layer.Defaults, icrMapping),
+ hbmArray => hbmArray.Item1);
+ }
+
+ [Test]
+ public void ShouldConvertICollectionRelationship_ManyToMany()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Array(),
+ () => new ManyToManyMapping(),
+ (arrayMapping, icrMapping) => arrayMapping.Set(fluent => fluent.Relationship, Layer.Defaults, icrMapping),
+ hbmArray => hbmArray.Item1);
+ }
+
+ [Test]
+ public void ShouldConvertCache()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => CollectionMapping.Array(),
+ (arrayMapping, cacheMapping) => arrayMapping.Set(fluent => fluent.Cache, Layer.Defaults, cacheMapping),
+ hbmArray => hbmArray.cache);
+ }
+
+ [Test]
+ public void ShouldConvertCompositeElement()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Array(),
+ (arrayMapping, compositeElementMapping) => arrayMapping.Set(fluent => fluent.CompositeElement, Layer.Defaults, compositeElementMapping),
+ hbmArray => hbmArray.Item1);
+ }
+
+ [Test]
+ public void ShouldConvertElement()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Array(),
+ (arrayMapping, elementMapping) => arrayMapping.Set(fluent => fluent.Element, Layer.Defaults, elementMapping),
+ hbmArray => hbmArray.Item1);
+ }
+
+ // HbmArray, unlike HbmList, doesn't support filters
+ /*
+ [Test]
+ public void ShouldConvertFilters()
+ {
+ ShouldConvertSubobjectsAsStrictlyTypedArray(
+ () => CollectionMapping.Array(),
+ (arrayMapping, filterMapping) => arrayMapping.AddFilter(filterMapping),
+ hbmArray => hbmArray.filter);
+ }
+ */
+
+ #endregion Base collection converter-based subobject tests
+
+ #region Type-specific collection converter-based subobject tests
+
+ [Test]
+ public void ShouldConvertIIndex_Index()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Array(),
+ () => new IndexMapping(),
+ (arrayMapping, indexMapping) => arrayMapping.Set(fluent => fluent.Index, Layer.Defaults, indexMapping),
+ hbmArray => hbmArray.Item);
+ }
+
+ // No other index type allowed by HbmArray has a fluent mapping at this point
+
+ #endregion Type-specific collection converter-based subobject tests
+ }
+}
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmBagConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmBagConverterTester.cs
new file mode 100644
index 000000000..f74e86269
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmBagConverterTester.cs
@@ -0,0 +1,492 @@
+using System;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.Collections;
+using FluentNHibernate.MappingModel.Output;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmBagConverterTester
+ {
+ private IHbmConverter converter;
+
+ [SetUp]
+ public void GetConverterFromContainer()
+ {
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ }
+
+ #region Base collection attribute value field tests
+
+ [Test]
+ public void ShouldConvertAccessIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Access, Layer.Conventions, "acc");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.access.ShouldEqual(bagMapping.Access);
+ }
+
+ [Test]
+ public void ShouldNotConvertAccessIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.access.ShouldEqual(blankHbmBag.access);
+ }
+
+ [Test]
+ public void ShouldConvertBatchSizeIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.BatchSize, Layer.Conventions, 10);
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.batchsize.ShouldEqual(bagMapping.BatchSize);
+ Assert.That(convertedHbmBag.batchsizeSpecified.Equals(true), "Batch size was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertBatchSizeIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.batchsize.ShouldEqual(blankHbmBag.batchsize);
+ Assert.That(convertedHbmBag.batchsizeSpecified.Equals(false), "Batch size was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertCascadeIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Cascade, Layer.Conventions, "all");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.cascade.ShouldEqual(bagMapping.Cascade);
+ }
+
+ [Test]
+ public void ShouldNotConvertCascadeIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.cascade.ShouldEqual(blankHbmBag.cascade);
+ }
+
+ [Test]
+ public void ShouldConvertCheckIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Check, Layer.Conventions, "chk");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.check.ShouldEqual(bagMapping.Check);
+ }
+
+ [Test]
+ public void ShouldNotConvertCheckIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.check.ShouldEqual(blankHbmBag.check);
+ }
+
+ [Test]
+ public void ShouldConvertCollectionTypeIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.CollectionType, Layer.Conventions, new TypeReference("type"));
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.collectiontype.ShouldEqual(bagMapping.CollectionType.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertCollectionTypeIfEmpty()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Set an explicitly empty type reference
+ bagMapping.Set(fluent => fluent.CollectionType, Layer.Conventions, TypeReference.Empty);
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.collectiontype.ShouldEqual(blankHbmBag.collectiontype);
+ }
+
+ [Test]
+ public void ShouldNotConvertCollectionTypeIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.collectiontype.ShouldEqual(blankHbmBag.collectiontype);
+ }
+
+ [Test]
+ public void ShouldConvertFetchIfPopulatedWithValidValue()
+ {
+ var fetch = HbmCollectionFetchMode.Subselect; // Defaults to Select, so use something else to properly detect that it changes
+
+ var bagMapping = CollectionMapping.Bag();
+ var fetchDict = new XmlLinkedEnumBiDictionary();
+ bagMapping.Set(fluent => fluent.Fetch, Layer.Conventions, fetchDict[fetch]);
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.fetch.ShouldEqual(fetch);
+ Assert.That(convertedHbmBag.fetchSpecified.Equals(true), "Fetch was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldFailToConvertFetchIfPopulatedWithInvalidValue()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Fetch, Layer.Conventions, "invalid_value");
+ Assert.Throws(() => converter.Convert(bagMapping));
+ }
+
+ [Test]
+ public void ShouldNotConvertFetchIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.fetch.ShouldEqual(blankHbmBag.fetch);
+ Assert.That(convertedHbmBag.fetchSpecified.Equals(false), "Fetch was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertGenericIfPopulated_True()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Generic, Layer.Conventions, true);
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.generic.ShouldEqual(bagMapping.Generic);
+ Assert.That(convertedHbmBag.genericSpecified.Equals(true), "Generic was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertGenericIfPopulated_False()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Generic, Layer.Conventions, false);
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.generic.ShouldEqual(bagMapping.Generic);
+ Assert.That(convertedHbmBag.genericSpecified.Equals(true), "Generic was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertGenericIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.generic.ShouldEqual(blankHbmBag.generic);
+ Assert.That(convertedHbmBag.genericSpecified.Equals(false), "Generic was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertInverseIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Inverse, Layer.Conventions, true); // Defaults to false, so use this to ensure that we can detect changes
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.inverse.ShouldEqual(bagMapping.Inverse);
+ }
+
+ [Test]
+ public void ShouldNotConvertInverseIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.inverse.ShouldEqual(blankHbmBag.inverse);
+ }
+
+ [Test]
+ public void ShouldConvertLazyIfPopulated()
+ {
+ var hbmLazy = HbmCollectionLazy.False; // Defaults to True, so use something else to properly detect that it changes
+
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Lazy, Layer.Conventions, HbmCollectionConverter.FluentHbmLazyBiDict[hbmLazy]);
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.lazy.ShouldEqual(hbmLazy);
+ Assert.That(convertedHbmBag.lazySpecified.Equals(true), "Lazy was not marked as specified");
+ }
+
+ // Since it is enum-based, Lazy cannot contain any invalid values, so no need to test for that here
+
+ [Test]
+ public void ShouldNotConvertLazyIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.lazy.ShouldEqual(blankHbmBag.lazy);
+ Assert.That(convertedHbmBag.lazySpecified.Equals(false), "Lazy was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertNameIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Name, Layer.Conventions, "name");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.name.ShouldEqual(bagMapping.Name);
+ }
+
+ [Test]
+ public void ShouldNotConvertNameIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.name.ShouldEqual(blankHbmBag.name);
+ }
+
+ [Test]
+ public void ShouldConvertOptimisticLockIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.OptimisticLock, Layer.Conventions, false); // Defaults to true, so use this to ensure that we can detect changes
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.optimisticlock.ShouldEqual(bagMapping.OptimisticLock);
+ }
+
+ [Test]
+ public void ShouldNotConvertOptimisticLockIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.optimisticlock.ShouldEqual(blankHbmBag.optimisticlock);
+ }
+
+ [Test]
+ public void ShouldConvertPersisterIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Persister, Layer.Conventions, new TypeReference(typeof(string)));
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.persister.ShouldEqual(bagMapping.Persister.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertPersisterIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.persister.ShouldEqual(blankHbmBag.persister);
+ }
+
+ [Test]
+ public void ShouldConvertSchemaIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Schema, Layer.Conventions, "dbo");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.schema.ShouldEqual(bagMapping.Schema);
+ }
+
+ [Test]
+ public void ShouldNotConvertSchemaIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.schema.ShouldEqual(blankHbmBag.schema);
+ }
+
+ [Test]
+ public void ShouldConvertTableNameIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.TableName, Layer.Conventions, "tbl");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.table.ShouldEqual(bagMapping.TableName);
+ }
+
+ [Test]
+ public void ShouldNotConvertTableNameIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.table.ShouldEqual(blankHbmBag.table);
+ }
+
+ [Test]
+ public void ShouldConvertWhereIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Where, Layer.Conventions, "x = 1");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.where.ShouldEqual(bagMapping.Where);
+ }
+
+ [Test]
+ public void ShouldNotConvertWhereIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.where.ShouldEqual(blankHbmBag.where);
+ }
+
+ [Test]
+ public void ShouldConvertSubselectIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Subselect, Layer.Conventions, "val");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.subselect.Text.ShouldEqual(new string[] { bagMapping.Subselect });
+ }
+
+ [Test]
+ public void ShouldNotConvertSubselectIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.subselect.ShouldEqual(blankHbmBag.subselect);
+ }
+
+ [Test]
+ public void ShouldConvertMutableIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.Mutable, Layer.Conventions, false); // Defaults to true, so use this to ensure that we can detect changes
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.mutable.ShouldEqual(bagMapping.Mutable);
+ }
+
+ [Test]
+ public void ShouldNotConvertMutableIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.mutable.ShouldEqual(blankHbmBag.mutable);
+ }
+
+ #endregion Base collection attribute value field tests
+
+ #region Type-specific collection attribute value field tests
+
+ [Test]
+ public void ShouldConvertOrderByIfPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ bagMapping.Set(fluent => fluent.OrderBy, Layer.Conventions, "ord");
+ var convertedHbmBag = converter.Convert(bagMapping);
+ convertedHbmBag.orderby.ShouldEqual(bagMapping.OrderBy);
+ }
+
+ [Test]
+ public void ShouldNotConvertOrderByIfNotPopulated()
+ {
+ var bagMapping = CollectionMapping.Bag();
+ // Don't set anything on the original mapping
+ var convertedHbmBag = converter.Convert(bagMapping);
+ var blankHbmBag = new HbmBag();
+ convertedHbmBag.orderby.ShouldEqual(blankHbmBag.orderby);
+ }
+
+ #endregion Type-specific collection attribute value field tests
+
+ #region Base collection converter-based subobject tests
+
+ [Test]
+ public void ShouldConvertKey()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => CollectionMapping.Bag(),
+ (bagMapping, keyMapping) => bagMapping.Set(fluent => fluent.Key, Layer.Defaults, keyMapping),
+ hbmBag => hbmBag.key);
+ }
+
+ [Test]
+ public void ShouldConvertICollectionRelationship_OneToMany()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Bag(),
+ () => new OneToManyMapping(),
+ (bagMapping, icrMapping) => bagMapping.Set(fluent => fluent.Relationship, Layer.Defaults, icrMapping),
+ hbmBag => hbmBag.Item);
+ }
+
+ [Test]
+ public void ShouldConvertICollectionRelationship_ManyToMany()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Bag(),
+ () => new ManyToManyMapping(),
+ (bagMapping, icrMapping) => bagMapping.Set(fluent => fluent.Relationship, Layer.Defaults, icrMapping),
+ hbmBag => hbmBag.Item);
+ }
+
+ [Test]
+ public void ShouldConvertCache()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => CollectionMapping.Bag(),
+ (bagMapping, cacheMapping) => bagMapping.Set(fluent => fluent.Cache, Layer.Defaults, cacheMapping),
+ hbmBag => hbmBag.cache);
+ }
+
+ [Test]
+ public void ShouldConvertCompositeElement()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Bag(),
+ (bagMapping, compositeElementMapping) => bagMapping.Set(fluent => fluent.CompositeElement, Layer.Defaults, compositeElementMapping),
+ hbmBag => hbmBag.Item);
+ }
+
+ [Test]
+ public void ShouldConvertElement()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => CollectionMapping.Bag(),
+ (bagMapping, elementMapping) => bagMapping.Set(fluent => fluent.Element, Layer.Defaults, elementMapping),
+ hbmBag => hbmBag.Item);
+ }
+
+ [Test]
+ public void ShouldConvertFilters()
+ {
+ ShouldConvertSubobjectsAsStrictlyTypedArray(
+ () => CollectionMapping.Bag(),
+ (bagMapping, filterMapping) => bagMapping.AddFilter(filterMapping),
+ hbmBag => hbmBag.filter);
+ }
+
+ #endregion Base collection converter-based subobject tests
+
+ #region Type-specific collection converter-based subobject tests
+
+ // No tests for this type
+
+ #endregion Type-specific collection converter-based subobject tests
+ }
+}
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmBasicSubclassConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmBasicSubclassConverterTester.cs
new file mode 100644
index 000000000..af4ebf381
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmBasicSubclassConverterTester.cs
@@ -0,0 +1,466 @@
+using System;
+using FakeItEasy;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.ClassBased;
+using FluentNHibernate.MappingModel.Collections;
+using FluentNHibernate.MappingModel.Output;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+using IComponentMapping = FluentNHibernate.MappingModel.ClassBased.IComponentMapping;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmBasicSubclassConverterTester
+ {
+ private IHbmConverter converter;
+
+ [SetUp]
+ public void GetConverterFromContainer()
+ {
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ }
+
+ #region Value field tests
+
+ [Test]
+ public void ShouldConvertDiscriminatorValueIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.DiscriminatorValue, Layer.Conventions, "val");
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.discriminatorvalue.ShouldEqual(subclassMapping.DiscriminatorValue.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertDiscriminatorValueIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.discriminatorvalue.ShouldEqual(blankHbmSubclass.discriminatorvalue);
+ }
+
+ [Test]
+ public void ShouldConvertNameIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.Name, Layer.Conventions, "name");
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.name.ShouldEqual(subclassMapping.Name);
+ }
+
+ [Test]
+ public void ShouldNotConvertNameIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.name.ShouldEqual(blankHbmSubclass.name);
+ }
+
+ [Test]
+ public void ShouldConvertProxyIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.Proxy, Layer.Conventions, "p");
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.proxy.ShouldEqual(subclassMapping.Proxy);
+ }
+
+ [Test]
+ public void ShouldNotConvertProxyIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.proxy.ShouldEqual(blankHbmSubclass.proxy);
+ }
+
+ [Test]
+ public void ShouldConvertLazyIfPopulated_True()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.Lazy, Layer.Conventions, true);
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.lazy.ShouldEqual(subclassMapping.Lazy);
+ Assert.That(convertedHbmSubclass.lazySpecified.Equals(true), "Lazy was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertLazyIfPopulated_False()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.Lazy, Layer.Conventions, false);
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.lazy.ShouldEqual(subclassMapping.Lazy);
+ Assert.That(convertedHbmSubclass.lazySpecified.Equals(true), "Lazy was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertLazyIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.lazy.ShouldEqual(blankHbmSubclass.lazy);
+ Assert.That(convertedHbmSubclass.lazySpecified.Equals(false), "Batch size was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertDynamicUpdateIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.DynamicUpdate, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.dynamicupdate.ShouldEqual(subclassMapping.DynamicUpdate);
+ }
+
+ [Test]
+ public void ShouldNotConvertDynamicUpdateIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.dynamicupdate.ShouldEqual(blankHbmSubclass.dynamicupdate);
+ }
+
+ [Test]
+ public void ShouldConvertDynamicInsertIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.DynamicInsert, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.dynamicinsert.ShouldEqual(subclassMapping.DynamicInsert);
+ }
+
+ [Test]
+ public void ShouldNotConvertDynamicInsertIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.dynamicinsert.ShouldEqual(blankHbmSubclass.dynamicinsert);
+ }
+
+ [Test]
+ public void ShouldConvertSelectBeforeUpdateIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.SelectBeforeUpdate, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.selectbeforeupdate.ShouldEqual(subclassMapping.SelectBeforeUpdate);
+ }
+
+ [Test]
+ public void ShouldNotConvertSelectBeforeUpdateIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.selectbeforeupdate.ShouldEqual(blankHbmSubclass.selectbeforeupdate);
+ }
+
+ [Test]
+ public void ShouldConvertAbstractIfPopulated_True()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.Abstract, Layer.Conventions, true);
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.@abstract.ShouldEqual(subclassMapping.Abstract);
+ Assert.That(convertedHbmSubclass.abstractSpecified.Equals(true), "Abstract was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertAbstractIfPopulated_False()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.Abstract, Layer.Conventions, false);
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.@abstract.ShouldEqual(subclassMapping.Abstract);
+ Assert.That(convertedHbmSubclass.abstractSpecified.Equals(true), "Abstract was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertAbstractIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.@abstract.ShouldEqual(blankHbmSubclass.@abstract);
+ Assert.That(convertedHbmSubclass.abstractSpecified.Equals(false), "Abstract was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertEntityNameIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.EntityName, Layer.Conventions, "entity1");
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.entityname.ShouldEqual(subclassMapping.EntityName);
+ }
+
+ [Test]
+ public void ShouldNotConvertEntityNameIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.entityname.ShouldEqual(blankHbmSubclass.entityname);
+ }
+
+ [Test]
+ public void ShouldConvertBatchSizeIfPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.Set(fluent => fluent.BatchSize, Layer.Conventions, 10);
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ convertedHbmSubclass.batchsize.ShouldEqual(subclassMapping.BatchSize.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertBatchSizeIfNotPopulated()
+ {
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ // Don't set anything on the original mapping
+ var convertedHbmSubclass = converter.Convert(subclassMapping);
+ var blankHbmSubclass = new HbmSubclass();
+ convertedHbmSubclass.batchsize.ShouldEqual(blankHbmSubclass.batchsize);
+ }
+
+ #endregion Value field tests
+
+ #region Converter-based subobject tests
+
+ [Test]
+ public void ShouldConvertProperties()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ (subclassMapping, propertyMapping) => subclassMapping.AddProperty(propertyMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertManyToOnes()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ (subclassMapping, manyToOneMapping) => subclassMapping.AddReference(manyToOneMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertOneToOnes()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ (subclassMapping, oneToOneMapping) => subclassMapping.AddOneToOne(oneToOneMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertComponents_Component()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new ComponentMapping(ComponentType.Component),
+ (subclassMapping, componentMapping) => subclassMapping.AddComponent(componentMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertComponents_DynamicComponent()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new ComponentMapping(ComponentType.DynamicComponent),
+ (subclassMapping, componentMapping) => subclassMapping.AddComponent(componentMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertAnys()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ (subclassMapping, anyMapping) => subclassMapping.AddAny(anyMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Map()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => CollectionMapping.Map(),
+ (subclassMapping, mapMapping) => subclassMapping.AddCollection(mapMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Set()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => CollectionMapping.Set(),
+ (subclassMapping, setMapping) => subclassMapping.AddCollection(setMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_List()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => CollectionMapping.List(),
+ (subclassMapping, listMapping) => subclassMapping.AddCollection(listMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Bag()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => CollectionMapping.Bag(),
+ (subclassMapping, bagMapping) => subclassMapping.AddCollection(bagMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test, Ignore("ShouldConvertCollections_IdBag")]
+ public void ShouldConvertCollections_IdBag()
+ {
+ Assert.Fail("Target logic not yet available");
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Array()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => CollectionMapping.Array(),
+ (subclassMapping, bagMapping) => subclassMapping.AddCollection(bagMapping),
+ hbmSubclass => hbmSubclass.Items);
+ }
+
+ [Test, Ignore("ShouldConvertCollections_PrimitiveArray")]
+ public void ShouldConvertCollections_PrimitiveArray()
+ {
+ Assert.Fail("Target logic not yet available");
+ }
+
+ [Test]
+ public void ShouldConvertJoins()
+ {
+ ShouldConvertSubobjectsAsStrictlyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ (subclassMapping, joinMapping) => subclassMapping.AddJoin(joinMapping),
+ hbmSubclass => hbmSubclass.join);
+ }
+
+ [Test]
+ public void ShouldConvertSubclasses_Subclass()
+ {
+ ShouldConvertSubobjectsAsStrictlyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new SubclassMapping(SubclassType.Subclass),
+ (subclassMapping1, subclassMapping2) => subclassMapping1.AddSubclass(subclassMapping2),
+ hbmSubclass => hbmSubclass.subclass1);
+ }
+
+ [Test]
+ public void ShouldConvertSubclasses_JoinedSubclass()
+ {
+ Assert.Throws(() =>
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new SubclassMapping(SubclassType.JoinedSubclass),
+ (subclassMapping, joinedSubclassMapping) => subclassMapping.AddSubclass(joinedSubclassMapping),
+ hbmSubclass => hbmSubclass.subclass1)
+ );
+ }
+
+ [Test]
+ public void ShouldConvertSubclasses_UnionSubclass()
+ {
+ Assert.Throws(() =>
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new SubclassMapping(SubclassType.UnionSubclass),
+ (subclassMapping, unionSubclassMapping) => subclassMapping.AddSubclass(unionSubclassMapping),
+ hbmSubclass => hbmSubclass.subclass1)
+ );
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_SqlInsert()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new StoredProcedureMapping("sql-insert", ""),
+ (subclassMapping, storedProcedureMapping) => subclassMapping.AddStoredProcedure(storedProcedureMapping),
+ hbmSubclass => hbmSubclass.sqlinsert);
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_SqlUpdate()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new StoredProcedureMapping("sql-update", ""),
+ (subclassMapping, storedProcedureMapping) => subclassMapping.AddStoredProcedure(storedProcedureMapping),
+ hbmSubclass => hbmSubclass.sqlupdate);
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_SqlDelete()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => new SubclassMapping(SubclassType.Subclass),
+ () => new StoredProcedureMapping("sql-delete", ""),
+ (subclassMapping, storedProcedureMapping) => subclassMapping.AddStoredProcedure(storedProcedureMapping),
+ hbmSubclass => hbmSubclass.sqldelete);
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_Unsupported()
+ {
+ var unsupportedSPType = "invalid";
+
+ // Set up a fake converter
+ var fakeConverter = A.Fake>();
+
+ // Set up a custom container with the fake FSub->HSub converter registered, and obtain our main converter from it (so
+ // that it will use the fake implementation). Note that we do the resolution _before_ we register the fake, so that
+ // in cases where we are doing recursive types and FMain == FSub + HMain == HSub (e.g., subclasses-of-subclasses) we
+ // get the real converter for the "outer" call but the fake for any "inner" calls.
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ container.Register>(cnvrt => fakeConverter);
+
+ // Allocate the subclass mapping and a stored procedure submapping with an unsupported sptype
+ var subclassMapping = new SubclassMapping(SubclassType.Subclass);
+ subclassMapping.AddStoredProcedure(new StoredProcedureMapping(unsupportedSPType, ""));
+
+ // This should throw
+ Assert.Throws(() => converter.Convert(subclassMapping));
+
+ // We don't care if it made a call to the subobject conversion logic or not (it is low enough cost that it doesn't
+ // really matter in the case of failure, and some implementation approaches that uses this may be simpler).
+ }
+
+ #endregion Converter-based subobject tests
+ }
+}
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmCacheConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmCacheConverterTester.cs
new file mode 100644
index 000000000..6900d115a
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmCacheConverterTester.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.Identity;
+using FluentNHibernate.MappingModel.Output;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmCacheConverterTester
+ {
+ private IHbmConverter converter;
+
+ [SetUp]
+ public void GetConverterFromContainer()
+ {
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ }
+
+ [Test]
+ public void ShouldConvertRegionIfPopulatedWithValidValue()
+ {
+ var cacheMapping = new CacheMapping();
+ cacheMapping.Set(fluent => fluent.Region, Layer.Conventions, "region");
+ var convertedHbmCache = converter.Convert(cacheMapping);
+ convertedHbmCache.region.ShouldEqual(cacheMapping.Region);
+ }
+
+ [Test]
+ public void ShouldNotConvertRegionIfNotPopulated()
+ {
+ var cacheMapping = new CacheMapping();
+ // Don't set the anything on the original mapping
+ var convertedHbmCache = converter.Convert(cacheMapping);
+ var blankHbmCache = new HbmCache();
+ convertedHbmCache.region.ShouldEqual(blankHbmCache.region);
+ }
+
+ [Test]
+ public void ShouldConvertUsageIfPopulatedWithValidValue()
+ {
+ var usage = HbmCacheUsage.Transactional; // Defaults to ReadOnly, so use this to ensure that we can detect changes
+
+ var cacheMapping = new CacheMapping();
+ var usageDict = new XmlLinkedEnumBiDictionary();
+ cacheMapping.Set(fluent => fluent.Usage, Layer.Conventions, usageDict[usage]);
+ var convertedHbmCache = converter.Convert(cacheMapping);
+ convertedHbmCache.usage.ShouldEqual(usage);
+ }
+
+ [Test]
+ public void ShouldFailToConvertUsageIfPopulatedWithInvalidValue()
+ {
+ var cacheMapping = new CacheMapping();
+ cacheMapping.Set(fluent => fluent.Usage, Layer.Conventions, "invalid_value");
+ Assert.Throws(() => converter.Convert(cacheMapping));
+ }
+
+ [Test]
+ public void ShouldNotConvertUsageIfNotPopulated()
+ {
+ var cacheMapping = new CacheMapping();
+ // Don't set the anything on the original mapping
+ var convertedHbmCache = converter.Convert(cacheMapping);
+ var blankHbmCache = new HbmCache();
+ convertedHbmCache.usage.ShouldEqual(blankHbmCache.usage);
+ }
+
+ [Test]
+ public void ShouldConvertIncludeIfPopulatedWithValidValue()
+ {
+ var include = HbmCacheInclude.NonLazy; // Defaults to All, so use this to ensure that we can detect changes
+
+ var cacheMapping = new CacheMapping();
+ var includeDict = new XmlLinkedEnumBiDictionary();
+ cacheMapping.Set(fluent => fluent.Include, Layer.Conventions, includeDict[include]);
+ var convertedHbmCache = converter.Convert(cacheMapping);
+ convertedHbmCache.include.ShouldEqual(include);
+ }
+
+ [Test]
+ public void ShouldFailToConvertIncludeIfPopulatedWithInvalidValue()
+ {
+ var cacheMapping = new CacheMapping();
+ cacheMapping.Set(fluent => fluent.Include, Layer.Conventions, "invalid_value");
+ Assert.Throws(() => converter.Convert(cacheMapping));
+ }
+
+ [Test]
+ public void ShouldNotConvertIncludeIfNotPopulated()
+ {
+ var cacheMapping = new CacheMapping();
+ // Don't set the anything on the original mapping
+ var convertedHbmCache = converter.Convert(cacheMapping);
+ var blankHbmCache = new HbmCache();
+ convertedHbmCache.include.ShouldEqual(blankHbmCache.include);
+ }
+ }
+}
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmClassConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmClassConverterTester.cs
new file mode 100644
index 000000000..666fdeda5
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmClassConverterTester.cs
@@ -0,0 +1,705 @@
+using System;
+using FakeItEasy;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.ClassBased;
+using FluentNHibernate.MappingModel.Collections;
+using FluentNHibernate.MappingModel.Identity;
+using FluentNHibernate.MappingModel.Output;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+using IComponentMapping = FluentNHibernate.MappingModel.ClassBased.IComponentMapping;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmClassConverterTester
+ {
+ private IHbmConverter converter;
+
+ [SetUp]
+ public void GetConverterFromContainer()
+ {
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ }
+
+ #region Value field tests
+
+ [Test]
+ public void ShouldConvertTableNameIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.TableName, Layer.Conventions, "tbl");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.table.ShouldEqual(classMapping.TableName);
+ }
+
+ [Test]
+ public void ShouldNotConvertTableNameIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.table.ShouldEqual(blankHbmClass.table);
+ }
+
+ [Test]
+ public void ShouldConvertSchemaIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Schema, Layer.Conventions, "dbo");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.schema.ShouldEqual(classMapping.Schema);
+ }
+
+ [Test]
+ public void ShouldNotConvertSchemaIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.schema.ShouldEqual(blankHbmClass.schema);
+ }
+
+ [Test]
+ public void ShouldConvertDiscriminatorValueIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.DiscriminatorValue, Layer.Conventions, 0);
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.discriminatorvalue.ShouldEqual(classMapping.DiscriminatorValue.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertDiscriminatorValueIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.discriminatorvalue.ShouldEqual(blankHbmClass.discriminatorvalue);
+ }
+
+ [Test]
+ public void ShouldConvertMutableIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Mutable, Layer.Conventions, false); // Defaults to true, so we have to set it false here in order to tell if it actually changed
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.mutable.ShouldEqual(classMapping.Mutable);
+ }
+
+ [Test]
+ public void ShouldNotConvertMutableIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.mutable.ShouldEqual(blankHbmClass.mutable);
+ }
+
+ [Test]
+ public void ShouldConvertPolymorphismIfPopulatedWithValidValue()
+ {
+ var polymorphism = HbmPolymorphismType.Explicit; // Defaults to Implicit, so use something else to properly detect that it changes
+
+ var classMapping = new ClassMapping();
+ var polyDict = new XmlLinkedEnumBiDictionary();
+ classMapping.Set(fluent => fluent.Polymorphism, Layer.Conventions, polyDict[polymorphism]);
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.polymorphism.ShouldEqual(polymorphism);
+ }
+
+ [Test]
+ public void ShouldFailToConvertPolymorphismIfPopulatedWithInvalidValue()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Polymorphism, Layer.Conventions, "invalid_value");
+ Assert.Throws(() => converter.Convert(classMapping));
+ }
+
+ [Test]
+ public void ShouldNotConvertPolymorphismIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.polymorphism.ShouldEqual(blankHbmClass.polymorphism);
+ }
+
+ [Test]
+ public void ShouldConvertPersisterIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Persister, Layer.Conventions, "p");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.persister.ShouldEqual(classMapping.Persister);
+ }
+
+ [Test]
+ public void ShouldNotConvertPersisterIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.persister.ShouldEqual(blankHbmClass.persister);
+ }
+
+ [Test]
+ public void ShouldConvertWhereIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Where, Layer.Conventions, "x = 1");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.where.ShouldEqual(classMapping.Where);
+ }
+
+ [Test]
+ public void ShouldNotConvertWhereIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.where.ShouldEqual(blankHbmClass.where);
+ }
+
+ [Test]
+ public void ShouldConvertBatchSizeIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.BatchSize, Layer.Conventions, 10);
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.batchsize.ShouldEqual(classMapping.BatchSize);
+ Assert.That(convertedHbmClass.batchsizeSpecified.Equals(true), "Batch size was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertBatchSizeIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.batchsize.ShouldEqual(blankHbmClass.batchsize);
+ Assert.That(convertedHbmClass.batchsizeSpecified.Equals(false), "Batch size was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertOptimisticLockIfPopulatedWithValidValue()
+ {
+ var optimisticlock = HbmOptimisticLockMode.Dirty; // Defaults to Version, so use something else to properly detect that it changes
+
+ var classMapping = new ClassMapping();
+ var polyDict = new XmlLinkedEnumBiDictionary();
+ classMapping.Set(fluent => fluent.OptimisticLock, Layer.Conventions, polyDict[optimisticlock]);
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.optimisticlock.ShouldEqual(optimisticlock);
+ }
+
+ [Test]
+ public void ShouldFailToConvertOptimisticLockIfPopulatedWithInvalidValue()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.OptimisticLock, Layer.Conventions, "invalid_value");
+ Assert.Throws(() => converter.Convert(classMapping));
+ }
+
+ [Test]
+ public void ShouldNotConvertOptimisticLockIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.optimisticlock.ShouldEqual(blankHbmClass.optimisticlock);
+ }
+
+ [Test]
+ public void ShouldConvertCheckIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Check, Layer.Conventions, "chk");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.check.ShouldEqual(classMapping.Check);
+ }
+
+ [Test]
+ public void ShouldNotConvertCheckIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.check.ShouldEqual(blankHbmClass.check);
+ }
+
+ [Test]
+ public void ShouldConvertNameIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Name, Layer.Conventions, "name");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.name.ShouldEqual(classMapping.Name);
+ }
+
+ [Test]
+ public void ShouldNotConvertNameIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.name.ShouldEqual(blankHbmClass.name);
+ }
+
+ [Test]
+ public void ShouldConvertProxyIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Proxy, Layer.Conventions, "p");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.proxy.ShouldEqual(classMapping.Proxy);
+ }
+
+ [Test]
+ public void ShouldNotConvertProxyIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.proxy.ShouldEqual(blankHbmClass.proxy);
+ }
+
+ [Test]
+ public void ShouldConvertLazyIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Lazy, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.lazy.ShouldEqual(classMapping.Lazy);
+ }
+
+ [Test]
+ public void ShouldNotConvertLazyIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.lazy.ShouldEqual(blankHbmClass.lazy);
+ }
+
+ [Test]
+ public void ShouldConvertDynamicUpdateIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.DynamicUpdate, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.dynamicupdate.ShouldEqual(classMapping.DynamicUpdate);
+ }
+
+ [Test]
+ public void ShouldNotConvertDynamicUpdateIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.dynamicupdate.ShouldEqual(blankHbmClass.dynamicupdate);
+ }
+
+ [Test]
+ public void ShouldConvertDynamicInsertIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.DynamicInsert, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.dynamicinsert.ShouldEqual(classMapping.DynamicInsert);
+ }
+
+ [Test]
+ public void ShouldNotConvertDynamicInsertIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.dynamicinsert.ShouldEqual(blankHbmClass.dynamicinsert);
+ }
+
+ [Test]
+ public void ShouldConvertSelectBeforeUpdateIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.SelectBeforeUpdate, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.selectbeforeupdate.ShouldEqual(classMapping.SelectBeforeUpdate);
+ }
+
+ [Test]
+ public void ShouldNotConvertSelectBeforeUpdateIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.selectbeforeupdate.ShouldEqual(blankHbmClass.selectbeforeupdate);
+ }
+
+ [Test]
+ public void ShouldConvertAbstractIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Abstract, Layer.Conventions, true); // Defaults to false, so we have to set it true here in order to tell if it actually changed
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.@abstract.ShouldEqual(classMapping.Abstract);
+ Assert.That(convertedHbmClass.abstractSpecified.Equals(true), "Abstract was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertAbstractIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.@abstract.ShouldEqual(blankHbmClass.@abstract);
+ Assert.That(convertedHbmClass.abstractSpecified.Equals(false), "Abstract was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertSchemaActionIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.SchemaAction, Layer.Conventions, "none");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.schemaaction.ShouldEqual(classMapping.SchemaAction);
+ }
+
+ [Test]
+ public void ShouldNotConvertSchemaActionIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.schemaaction.ShouldEqual(blankHbmClass.schemaaction);
+ }
+
+ [Test]
+ public void ShouldConvertEntityNameIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.EntityName, Layer.Conventions, "entity1");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.entityname.ShouldEqual(classMapping.EntityName);
+ }
+
+ [Test]
+ public void ShouldNotConvertEntityNameIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.entityname.ShouldEqual(blankHbmClass.entityname);
+ }
+
+ #endregion Value field tests
+
+ #region Non-converter-based subobject tests
+
+ [Test]
+ public void ShouldConvertSubselectIfPopulated()
+ {
+ var classMapping = new ClassMapping();
+ classMapping.Set(fluent => fluent.Subselect, Layer.Conventions, "val");
+ var convertedHbmClass = converter.Convert(classMapping);
+ convertedHbmClass.subselect.Text.ShouldEqual(new string[] { classMapping.Subselect });
+ }
+
+ [Test]
+ public void ShouldNotConvertSubselectIfNotPopulated()
+ {
+ var classMapping = new ClassMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmClass = converter.Convert(classMapping);
+ var blankHbmClass = new HbmClass();
+ convertedHbmClass.subselect.ShouldEqual(blankHbmClass.subselect);
+ }
+
+ #endregion Non-converter-based subobject tests
+
+ #region Converter-based subobject tests
+
+ [Test]
+ public void ShouldConvertCache()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ (classMapping, cacheMapping) => classMapping.Set(fluent => fluent.Cache, Layer.Conventions, cacheMapping),
+ hbmClass => hbmClass.cache);
+ }
+
+ [Test]
+ public void ShouldConvertIIdentity_Id()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => new IdMapping(),
+ (classMapping, iidMapping) => classMapping.Set(fluent => fluent.Id, Layer.Conventions, iidMapping),
+ hbmClass => hbmClass.Item);
+ }
+
+ [Test]
+ public void ShouldConvertIIdentity_CompositeId()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ () => new CompositeIdMapping(),
+ (classMapping, iidMapping) => classMapping.Set(fluent => fluent.Id, Layer.Conventions, iidMapping),
+ hbmClass => hbmClass.Item);
+ }
+
+ [Test]
+ public void ShouldConvertNaturalId()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ (classMapping, naturalIdMapping) => classMapping.Set(fluent => fluent.NaturalId, Layer.Conventions, naturalIdMapping),
+ hbmClass => hbmClass.naturalid);
+ }
+
+ [Test]
+ public void ShouldConvertVersion()
+ {
+ ShouldConvertSubobjectAsLooselyTypedField(
+ (classMapping, versionMapping) => classMapping.Set(fluent => fluent.Version, Layer.Conventions, versionMapping),
+ hbmClass => hbmClass.Item1);
+ }
+
+ [Test]
+ public void ShouldConvertProperties()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ (classMapping, propertyMapping) => classMapping.AddProperty(propertyMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertManyToOnes()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ (classMapping, manyToOneMapping) => classMapping.AddReference(manyToOneMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertOneToOnes()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ (classMapping, oneToOneMapping) => classMapping.AddOneToOne(oneToOneMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertComponents_Component()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new ComponentMapping(ComponentType.Component),
+ (classMapping, componentMapping) => classMapping.AddComponent(componentMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertComponents_DynamicComponent()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new ComponentMapping(ComponentType.DynamicComponent),
+ (classMapping, componentMapping) => classMapping.AddComponent(componentMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertAnys()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ (classMapping, anyMapping) => classMapping.AddAny(anyMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Map()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => CollectionMapping.Map(),
+ (classMapping, mapMapping) => classMapping.AddCollection(mapMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Set()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => CollectionMapping.Set(),
+ (classMapping, setMapping) => classMapping.AddCollection(setMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_List()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => CollectionMapping.List(),
+ (classMapping, listMapping) => classMapping.AddCollection(listMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Bag()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => CollectionMapping.Bag(),
+ (classMapping, bagMapping) => classMapping.AddCollection(bagMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test, Ignore("ShouldConvertCollections_IdBag")]
+ public void ShouldConvertCollections_IdBag()
+ {
+ Assert.Fail("Target logic not yet available");
+ }
+
+ [Test]
+ public void ShouldConvertCollections_Array()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => CollectionMapping.Array(),
+ (classMapping, bagMapping) => classMapping.AddCollection(bagMapping),
+ hbmClass => hbmClass.Items);
+ }
+
+ [Test, Ignore("ShouldConvertCollections_PrimitiveArray")]
+ public void ShouldConvertCollections_PrimitiveArray()
+ {
+ Assert.Fail("Target logic not yet available");
+ }
+
+ [Test]
+ public void ShouldConvertJoins()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ (classMapping, joinMapping) => classMapping.AddJoin(joinMapping),
+ hbmClass => hbmClass.Items1);
+ }
+
+ [Test]
+ public void ShouldConvertSubclasses_Subclass()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.Subclass),
+ (classMapping, subclassMapping) => classMapping.AddSubclass(subclassMapping),
+ hbmClass => hbmClass.Items1);
+ }
+
+ [Test]
+ public void ShouldConvertSubclasses_JoinedSubclass()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.JoinedSubclass),
+ (classMapping, joinedSubclassMapping) => classMapping.AddSubclass(joinedSubclassMapping),
+ hbmClass => hbmClass.Items1);
+ }
+
+ [Test]
+ public void ShouldConvertSubclasses_UnionSubclass()
+ {
+ ShouldConvertSubobjectsAsLooselyTypedArray(
+ () => new SubclassMapping(SubclassType.UnionSubclass),
+ (classMapping, unionSubclassMapping) => classMapping.AddSubclass(unionSubclassMapping),
+ hbmClass => hbmClass.Items1);
+ }
+
+ [Test]
+ public void ShouldConvertDiscriminator()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ (classMapping, discriminatorMapping) => classMapping.Set(fluent => fluent.Discriminator, Layer.Conventions, discriminatorMapping),
+ hbmClass => hbmClass.discriminator);
+ }
+
+ [Test]
+ public void ShouldConvertFilters()
+ {
+ ShouldConvertSubobjectsAsStrictlyTypedArray(
+ (classMapping, filterMapping) => classMapping.AddFilter(filterMapping),
+ hbmClass => hbmClass.filter);
+ }
+
+ [Test]
+ public void ShouldConvertTuplizers()
+ {
+ // Note that this is singular on the source but plural on the target
+ ShouldConvertSubobjectsAsStrictlyTypedArray(
+ (classMapping, tuplizerMapping) => classMapping.Set(fluent => fluent.Tuplizer, Layer.Conventions, tuplizerMapping),
+ hbmClass => hbmClass.tuplizer);
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_SqlInsert()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => new StoredProcedureMapping("sql-insert", ""),
+ (classMapping, storedProcedureMapping) => classMapping.AddStoredProcedure(storedProcedureMapping),
+ hbmClass => hbmClass.sqlinsert);
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_SqlUpdate()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => new StoredProcedureMapping("sql-update", ""),
+ (classMapping, storedProcedureMapping) => classMapping.AddStoredProcedure(storedProcedureMapping),
+ hbmClass => hbmClass.sqlupdate);
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_SqlDelete()
+ {
+ ShouldConvertSubobjectAsStrictlyTypedField(
+ () => new StoredProcedureMapping("sql-delete", ""),
+ (classMapping, storedProcedureMapping) => classMapping.AddStoredProcedure(storedProcedureMapping),
+ hbmClass => hbmClass.sqldelete);
+ }
+
+ [Test]
+ public void ShouldConvertStoredProcedure_Unsupported()
+ {
+ var unsupportedSPType = "invalid";
+
+ // Set up a fake converter
+ var fakeConverter = A.Fake>();
+
+ // Set up a custom container with the fake FSub->HSub converter registered, and obtain our main converter from it (so that it will use the fake implementation)
+ var container = new HbmConverterContainer();
+ container.Register>(cnvrt => fakeConverter);
+ converter = container.Resolve>();
+
+ // Allocate the class mapping and a stored procedure submapping with an unsupported sptype
+ var classMapping = new ClassMapping();
+ classMapping.AddStoredProcedure(new StoredProcedureMapping(unsupportedSPType, ""));
+
+ // This should throw
+ Assert.Throws(() => converter.Convert(classMapping));
+
+ // We don't care if it made a call to the subobject conversion logic or not (it is low enough cost that it doesn't
+ // really matter in the case of failure, and some implementation approaches that uses this may be simpler).
+ }
+
+ #endregion Converter-based subobject tests
+ }
+}
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmCollectionConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmCollectionConverterTester.cs
new file mode 100644
index 000000000..29fe1d72d
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmCollectionConverterTester.cs
@@ -0,0 +1,57 @@
+using FluentNHibernate.MappingModel.Collections;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmCollectionConverterTester
+ {
+ // No use for the normal setup method to populate the container, since no test uses it
+
+ [Test]
+ public void ShouldProcessBagAsBag()
+ {
+ ShouldConvertSpecificHbmForMappingSubtype(
+ () => CollectionMapping.Bag()
+ );
+ }
+
+ [Test]
+ public void ShouldProcessListAsList()
+ {
+ ShouldConvertSpecificHbmForMappingSubtype(
+ () => CollectionMapping.List()
+ );
+ }
+
+ [Test]
+ public void ShouldProcessSetAsSet()
+ {
+ ShouldConvertSpecificHbmForMappingSubtype(
+ () => CollectionMapping.Set()
+ );
+ }
+
+ [Test]
+ public void ShouldProcessMapAsMap()
+ {
+ ShouldConvertSpecificHbmForMappingSubtype(
+ () => CollectionMapping.Map()
+ );
+ }
+
+ [Test]
+ public void ShouldProcessArrayAsArray()
+ {
+ ShouldConvertSpecificHbmForMappingSubtype(
+ () => CollectionMapping.Array()
+ );
+ }
+
+ /* Unfortunately, there is no good way to test the "fail if passed something unsupported" logic, because we cannot
+ * generate a "bad" Collection type (it is an enum) and we want to support all of the real ones.
+ */
+ }
+}
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmCollectionRelationshipConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmCollectionRelationshipConverterTester.cs
new file mode 100644
index 000000000..7cf161572
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmCollectionRelationshipConverterTester.cs
@@ -0,0 +1,25 @@
+using FluentNHibernate.MappingModel.Collections;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmCollectionRelationshipConverterTester
+ {
+ // No use for the normal setup method to populate the container, since no test uses it
+
+ [Test]
+ public void ShouldProcessOneToManyAsOneToMany()
+ {
+ ShouldConvertSpecificHbmForMappingChild();
+ }
+
+ [Test]
+ public void ShouldProcessManyToManyAsManyToMany()
+ {
+ ShouldConvertSpecificHbmForMappingChild();
+ }
+ }
+}
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmColumnConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmColumnConverterTester.cs
new file mode 100644
index 000000000..189895262
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmColumnConverterTester.cs
@@ -0,0 +1,233 @@
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.Output;
+using NUnit.Framework;
+using NHibernate.Cfg.MappingSchema;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmColumnConverterTester
+ {
+ private IHbmConverter converter;
+
+ [SetUp]
+ public void GetConverterFromContainer()
+ {
+ var container = new HbmConverterContainer();
+ converter = container.Resolve>();
+ }
+
+ [Test]
+ public void ShouldConvertCheckIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Check, Layer.Conventions, "ck");
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.check.ShouldEqual(columnMapping.Check);
+ }
+
+ [Test]
+ public void ShouldNotConvertCheckIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.check.ShouldEqual(blankHbmColumn.check);
+ }
+
+ [Test]
+ public void ShouldConvertDefaultIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Default, Layer.Conventions, "df");
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.@default.ShouldEqual(columnMapping.Default);
+ }
+
+ [Test]
+ public void ShouldNotConvertDefaultIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.@default.ShouldEqual(blankHbmColumn.@default);
+ }
+
+ [Test]
+ public void ShouldConvertIndexIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Index, Layer.Conventions, "ix");
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.index.ShouldEqual(columnMapping.Index);
+ }
+
+ [Test]
+ public void ShouldNotConvertIndexIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.index.ShouldEqual(blankHbmColumn.index);
+ }
+
+ [Test]
+ public void ShouldConvertLengthIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Length, Layer.Conventions, 10);
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.length.ShouldEqual(columnMapping.Length.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertLengthIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.length.ShouldEqual(blankHbmColumn.length);
+ }
+
+ [Test]
+ public void ShouldConvertNameIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Name, Layer.Conventions, "name");
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.name.ShouldEqual(columnMapping.Name);
+ }
+
+ [Test]
+ public void ShouldNotConvertNameIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.name.ShouldEqual(blankHbmColumn.name);
+ }
+
+ [Test]
+ public void ShouldConvertNotNullIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.NotNull, Layer.Conventions, true); // Defaults to false, so use true so that we can detect changes
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.notnull.ShouldEqual(columnMapping.NotNull);
+ Assert.That(convertedHbmColumn.notnullSpecified.Equals(true), "NotNull was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertNotNullIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.notnull.ShouldEqual(blankHbmColumn.notnull);
+ Assert.That(convertedHbmColumn.notnullSpecified.Equals(false), "NotNull was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertSqlTypeIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.SqlType, Layer.Conventions, "type"); // FIXME: Is this a realistic value?
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.sqltype.ShouldEqual(columnMapping.SqlType);
+ }
+
+ [Test]
+ public void ShouldNotConvertSqlTypeIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.sqltype.ShouldEqual(blankHbmColumn.sqltype);
+ }
+
+ [Test]
+ public void ShouldConvertUniqueIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Unique, Layer.Conventions, true); // Defaults to false, so use true so that we can detect changes
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.unique.ShouldEqual(columnMapping.Unique);
+ Assert.That(convertedHbmColumn.uniqueSpecified.Equals(true), "Unique was not marked as specified");
+ }
+
+ [Test]
+ public void ShouldNotConvertUniqueIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.unique.ShouldEqual(blankHbmColumn.unique);
+ Assert.That(convertedHbmColumn.uniqueSpecified.Equals(false), "Unique was marked as specified");
+ }
+
+ [Test]
+ public void ShouldConvertUniqueKeyIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.UniqueKey, Layer.Conventions, "uk");
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.uniquekey.ShouldEqual(columnMapping.UniqueKey);
+ }
+
+ [Test]
+ public void ShouldNotConvertUniqueKeyIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.uniquekey.ShouldEqual(blankHbmColumn.uniquekey);
+ }
+
+ [Test]
+ public void ShouldConvertPrecisionIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Precision, Layer.Conventions, 10);
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.precision.ShouldEqual(columnMapping.Precision.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertPrecisionIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.precision.ShouldEqual(blankHbmColumn.precision);
+ }
+
+ [Test]
+ public void ShouldConvertScaleIfPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ columnMapping.Set(fluent => fluent.Scale, Layer.Conventions, 10);
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ convertedHbmColumn.scale.ShouldEqual(columnMapping.Scale.ToString());
+ }
+
+ [Test]
+ public void ShouldNotConvertScaleIfNotPopulated()
+ {
+ var columnMapping = new ColumnMapping();
+ // Don't set anything on the original mapping
+ var convertedHbmColumn = converter.Convert(columnMapping);
+ var blankHbmColumn = new HbmColumn();
+ convertedHbmColumn.scale.ShouldEqual(blankHbmColumn.scale);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FluentNHibernate.Testing/MappingModel/Output/HbmComponentBasedConverterTester.cs b/src/FluentNHibernate.Testing/MappingModel/Output/HbmComponentBasedConverterTester.cs
new file mode 100644
index 000000000..57fc6f3f4
--- /dev/null
+++ b/src/FluentNHibernate.Testing/MappingModel/Output/HbmComponentBasedConverterTester.cs
@@ -0,0 +1,111 @@
+using System.Collections.Generic;
+using FakeItEasy;
+using FluentNHibernate.MappingModel.ClassBased;
+using FluentNHibernate.MappingModel.Output;
+using NHibernate.Cfg.MappingSchema;
+using NUnit.Framework;
+using static FluentNHibernate.Testing.Hbm.HbmConverterTestHelper;
+using IComponentMapping = FluentNHibernate.MappingModel.ClassBased.IComponentMapping;
+
+namespace FluentNHibernate.Testing.MappingModel.Output
+{
+ [TestFixture]
+ public class HbmComponentBasedConverterTester
+ {
+ [Test]
+ public void ShouldConvertComponentAsComponent()
+ {
+ ShouldConvertSpecificHbmForMapping(
+ () => new ComponentMapping(ComponentType.Component)
+ );
+ }
+
+ [Test]
+ public void ShouldConvertDynamicComponentAsDynamicComponent()
+ {
+ ShouldConvertSpecificHbmForMapping(
+ () => new ComponentMapping(ComponentType.DynamicComponent)
+ );
+ }
+
+ [Test]
+ public void ShouldConvertExternalComponentAsComponent()
+ {
+ ShouldConvertSpecificHbmForMapping(
+ () => new ExternalComponentMapping(ComponentType.Component)
+ );
+ }
+
+ [Test]
+ public void ShouldConvertDynamicExternalComponentAsDynamicComponent()
+ {
+ ShouldConvertSpecificHbmForMapping(
+ () => new ExternalComponentMapping(ComponentType.DynamicComponent)
+ );
+ }
+
+ /* Unfortunately, there is no good way to test the "fail if passed something unsupported" logic, because we cannot
+ * generate a "bad" ComponentType and we want to support all of the real ones.
+ */
+
+ [Test]
+ public void ShouldConvertReferenceComponentAsComponent()
+ {
+ /* This test requires somewhat specialized setup. Specifically, rather than looking for a handoff of the entire
+ * mapping to an IHbmConverter for some specific X, we look for a handoff of the merged
+ * model to an IHbmConverter. This is due to how the existing code structures things.
+ */
+
+ // Set up a fake converter that registers any instances it generates and returns in a list
+ var generatedHbms = new List