bool FArchiveTest::RunTest( const FString& Parameters )
{
    // Key metadata
    TSharedPtr< FLocMetadataObject > KeyMetadataA = MakeShareable( new FLocMetadataObject );
    TSharedPtr< FLocMetadataObject > KeyMetadataB = MakeShareable( new FLocMetadataObject );

    // Source metadata
    TSharedPtr< FLocMetadataObject > SourceMetadataA = MakeShareable( new FLocMetadataObject );
    TSharedPtr< FLocMetadataObject > SourceMetadataB = MakeShareable( new FLocMetadataObject );


    // Setup KeyMetadataA
    KeyMetadataA->SetStringField( TEXT("Gender"			),	TEXT("Masculine")	);
    KeyMetadataA->SetStringField( TEXT("Plurality"		),	TEXT("Singular")	);
    KeyMetadataA->SetStringField( TEXT("TargetGender"	),	TEXT("Masculine")	);
    KeyMetadataA->SetStringField( TEXT("TargetPlurality"),	TEXT("Singular")	);

    // Setup KeyMetadataB
    KeyMetadataB->SetStringField( TEXT("Gender"			),	TEXT("Masculine")	);
    KeyMetadataB->SetStringField( TEXT("Plurality"		),	TEXT("Singular")	);
    KeyMetadataB->SetStringField( TEXT("TargetGender"	),	TEXT("Feminine")	);
    KeyMetadataB->SetStringField( TEXT("TargetPlurality"),	TEXT("Singular")	);

    // Setup source metadata
    SourceMetadataA->SetBoolField( TEXT("*IsMature"), false );
    SourceMetadataB->SetBoolField( TEXT("*IsMature"), true );

    // Setup source item
    FLocItem SourceA( TEXT("TextA") );
    SourceA.MetadataObj = MakeShareable( new FLocMetadataObject(*SourceMetadataA) );

    FLocItem Translation = SourceA;
    Translation.Text = TEXT("TranslatedTextA");

    FString TestNamespace = TEXT("TestNamespace");
    FString SourceAKey = TEXT("TextA");

    // Test entry add
    {
        bool TestOptionalTrue = true;
        bool TestOptionalFalse = false;

        // bIsOptional is not used as a key.  We ensure adding entries where the bIsOptional member is the only difference works as expected.
        FInternationalizationArchive TestArchive;
        TestArchive.AddEntry( TestNamespace, SourceAKey, SourceA, Translation, NULL, TestOptionalTrue );
        // Add duplicate entry that differs in bIsOptional flag.  This add should report success because we already have an entry with matching
        //  namespace/source/keymetadata.  Differences in bIsOptional are not taken into consideration.
        bool bResult = TestArchive.AddEntry( TestNamespace, SourceAKey, SourceA, Translation, NULL, TestOptionalFalse );
        TestTrue( TEXT("AddEntry result = true"), bResult);

        // We should only have one entry in the archive
        int32 EntryCount = 0;
        for(auto Iter = TestArchive.GetEntriesBySourceTextIterator(); Iter; ++Iter, ++EntryCount);
        TestEqual( TEXT("EntryCount == 1"), EntryCount, 1 );

        // Make sure the original bIsOptional value is not overwritten by our second add.
        TSharedPtr< FArchiveEntry > FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, NULL );
        if( !FoundEntry.IsValid() )
        {
            AddError(TEXT("FArchiveEntry could not find entry using FindEntryByKey."));
        }
        else
        {
            TestTrue( TEXT("FoundEntry->Namespace == Namespace"), FoundEntry->bIsOptional == TestOptionalTrue );
        }
    }

    // Test lookup an entry
    {
        {
            FInternationalizationArchive TestArchive;
            TestArchive.AddEntry( TestNamespace, SourceAKey, SourceA, Translation, KeyMetadataA, false );

            TSharedPtr< FArchiveEntry > FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, KeyMetadataA );
            if( !FoundEntry.IsValid() )
            {
                AddError(TEXT("FArchiveEntry could not find entry using FindEntryByKey."));
            }
            else
            {
                TestTrue( TEXT("FoundEntry->Namespace == Namespace"), FoundEntry->Namespace == TestNamespace );
                TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == SourceA );
                TestTrue( TEXT("FoundEntry->Translation == Translation"), FoundEntry->Translation == Translation );
                if( FoundEntry->KeyMetadataObj == KeyMetadataA )
                {
                    AddError(TEXT("FArchiveEntry KeyMetadataObj is not a unique object."));
                }
                TestTrue( TEXT("FoundEntry->KeyMetadataObj == KeyMetadataA"), *(FoundEntry->KeyMetadataObj) == *(KeyMetadataA) );
            }

            // Passing in a mismatched key metadata will fail to find the entry.  Any fallback logic is intended to happen at runtime
            FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, NULL );
            TestInvalid( TEXT("!FoundEntry.IsValid()"), FoundEntry);

            FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, MakeShareable( new FLocMetadataObject() ) );
            TestInvalid( TEXT("!FoundEntry.IsValid()"), FoundEntry);

            FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, KeyMetadataB );
            TestInvalid( TEXT("!FoundEntry.IsValid()"), FoundEntry);
        }

        // Ensure we can properly lookup entries with non-null but empty key metadata
        {
            FInternationalizationArchive TestArchive;
            TestArchive.AddEntry( TestNamespace, SourceAKey, SourceA, Translation, MakeShareable( new FLocMetadataObject() ), false );

            TSharedPtr< FArchiveEntry > FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, NULL );
            if( !FoundEntry.IsValid() )
            {
                AddError(TEXT("FArchiveEntry could not find entry using FindEntryByKey."));
            }
            else
            {
                TestTrue( TEXT("FoundEntry->Namespace == Namespace"), FoundEntry->Namespace == TestNamespace );
                TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == SourceA );
            }
        }

        // Ensure we can properly lookup entries with null key metadata
        {
            FInternationalizationArchive TestArchive;
            TestArchive.AddEntry( TestNamespace, SourceAKey, SourceA, Translation, NULL, false );

            TSharedPtr< FArchiveEntry > FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, NULL );
            if( !FoundEntry.IsValid() )
            {
                AddError(TEXT("FArchiveEntry could not find entry using FindEntryByKey."));
            }
            else
            {
                TestTrue( TEXT("FoundEntry->Namespace == Namespace"), FoundEntry->Namespace == TestNamespace );
                TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == SourceA );
            }

            // Test lookup with non-null but empty key metadata
            FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, MakeShareable( new FLocMetadataObject() ) );
            if( !FoundEntry.IsValid() )
            {
                AddError(TEXT("FArchiveEntry could not find entry using FindEntryByKey."));
            }
            else
            {
                TestTrue( TEXT("FoundEntry->Namespace == Namespace"), FoundEntry->Namespace == TestNamespace );
                TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == SourceA );
            }
        }

        // Ensure lookup where source metadata has * prefixed entries work as expected.  Note: The source metadata object
        //   supports a * prefix on metadata names which modifies the way we perform metadata comparison(ignores entry type and value, only name is checked)
        {
            FLocItem SourceCompare( TEXT("TextA") );
            SourceCompare.MetadataObj = MakeShareable( new FLocMetadataObject() );
            SourceCompare.MetadataObj->SetStringField( TEXT("*IsMature"), TEXT("") );

            FInternationalizationArchive TestArchive;
            // Added entry with String *IsMature entry
            TestArchive.AddEntry( TestNamespace, SourceAKey, SourceCompare, Translation, KeyMetadataA, false );

            // Finding entry using a source that has Boolean *IsMature
            TSharedPtr< FArchiveEntry > FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, KeyMetadataA );
            if( !FoundEntry.IsValid() )
            {
                AddError(TEXT("FArchiveEntry could not find entry using FindEntryByKey."));
            }
            else
            {
                TestTrue( TEXT("FoundEntry->Namespace == Namespace"), FoundEntry->Namespace == TestNamespace );
                TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == SourceA );
            }

            // Attempting to add an entry that only differs by a * prefexed source metadata entry value or type will result in success since
            //  a 'matching' entry already exists in the archive.  We should, however, only have one entry in the archive
            bool bResult = TestArchive.AddEntry( TestNamespace, SourceAKey, SourceA, Translation, KeyMetadataA, false );

            TestTrue( TEXT("AddEntry result = true"), bResult);

            // We should only have one entry in the archive
            int32 EntryCount = 0;
            for(auto Iter = TestArchive.GetEntriesBySourceTextIterator(); Iter; ++Iter, ++EntryCount);
            TestEqual( TEXT("EntryCount == 1"), EntryCount, 1 );

            // Check to see that the original type/value of the * prefixed entry did not get modified
            FoundEntry = TestArchive.FindEntryByKey( TestNamespace, SourceAKey, KeyMetadataA );
            if( !FoundEntry.IsValid() )
            {
                AddError(TEXT("FArchiveEntry could not find entry using FindEntryByKey."));
            }
            else
            {
                if( FoundEntry->Source.MetadataObj->HasTypedField< ELocMetadataType::String >( TEXT("*IsMature") ) )
                {
                    TestTrue( TEXT("Metadata Type == String and Value == Empty string"), FoundEntry->Source.MetadataObj->GetStringField( TEXT("*IsMature") ) == TEXT("") );
                }
                else
                {
                    AddError(TEXT("FArchiveEntry * prefixed metadata entry on source object was modified unexpectedly."));
                }
            }
        }
    }

    return true;
}
Esempio n. 2
0
bool FManifestTest::RunTest( const FString& Parameters )
{
	// Key metadata
	TSharedPtr< FLocMetadataObject > KeyMetadataA = MakeShareable( new FLocMetadataObject );
	TSharedPtr< FLocMetadataObject > KeyMetadataB = MakeShareable( new FLocMetadataObject );

	// Info metadata
	TSharedPtr< FLocMetadataObject > InfoMetadataA = MakeShareable( new FLocMetadataObject );
	TSharedPtr< FLocMetadataObject > InfoMetadataB = MakeShareable( new FLocMetadataObject );

	// Source metadata
	TSharedPtr< FLocMetadataObject > SourceMetadataA = MakeShareable( new FLocMetadataObject );
	TSharedPtr< FLocMetadataObject > SourceMetadataB = MakeShareable( new FLocMetadataObject );


	// Setup KeyMetadataA
	KeyMetadataA->SetStringField( TEXT("Gender"			),	TEXT("Masculine")	);
	KeyMetadataA->SetStringField( TEXT("Plurality"		),	TEXT("Singular")	);
	KeyMetadataA->SetStringField( TEXT("TargetGender"	),	TEXT("Masculine")	);
	KeyMetadataA->SetStringField( TEXT("TargetPlurality"),	TEXT("Singular")	);

	// Setup KeyMetadataB
	KeyMetadataB->SetStringField( TEXT("Gender"			),	TEXT("Masculine")	);
	KeyMetadataB->SetStringField( TEXT("Plurality"		),	TEXT("Singular")	);
	KeyMetadataB->SetStringField( TEXT("TargetGender"	),	TEXT("Feminine")	);
	KeyMetadataB->SetStringField( TEXT("TargetPlurality"),	TEXT("Singular")	);

	// Setup source metadata
	SourceMetadataA->SetBoolField( TEXT("*IsMature"), false );
	SourceMetadataB->SetBoolField( TEXT("*IsMature"), true );

	// Set InfoMetadataA
	InfoMetadataA->SetStringField( TEXT("VoiceActorDirection"), TEXT("Go big or go home!") );

	// Test FInternationalizationManifest
	{
		FContext ContextA;
		ContextA.Key			= TEXT("KeyA");
		ContextA.SourceLocation = TEXT("SourceLocationA");
		ContextA.InfoMetadataObj = MakeShareable( new FLocMetadataObject(*InfoMetadataA) );
		ContextA.KeyMetadataObj = MakeShareable( new FLocMetadataObject(*KeyMetadataA) );

		FContext ContextB;
		ContextB.Key			= TEXT("KeyB");
		ContextB.SourceLocation = TEXT("SourceLocationB");
		ContextB.InfoMetadataObj = MakeShareable( new FLocMetadataObject(*InfoMetadataB) );
		ContextB.KeyMetadataObj = MakeShareable( new FLocMetadataObject(*KeyMetadataB) );

		FLocItem Source( TEXT("TestText") );
		Source.MetadataObj = MakeShareable( new FLocMetadataObject(*SourceMetadataA) );

		FString TestNamespace = TEXT("TestNamespace");


		// Adding entries with exactly matching Source and matching Context
		{
			FInternationalizationManifest TestManifest;

			TestManifest.AddSource( TestNamespace, Source, ContextA );
			bool bResult = TestManifest.AddSource( TestNamespace, Source, ContextA );

			// Adding a duplicate entry reports success but our entry count is not increased after the first entry is added
			TestTrue( TEXT("AddSource result == true"), bResult);
			TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );
		}

		// Adding entries with exactly matching Source but different Contexts
		{
			FInternationalizationManifest TestManifest;

			TestManifest.AddSource( TestNamespace, Source, ContextA );
			TestManifest.AddSource( TestNamespace, Source, ContextB );

			TestEqual( TEXT("ManifestCount == 2"), CountManifestEntries(TestManifest), 2 );

			// Test find by context
			TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryByContext( TestNamespace, ContextA );
			if( !FoundEntry1.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryByContext."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry1->Source == Source );
				TestEqual( TEXT("FoundEntry->Context.Num() == 2"), FoundEntry1->Contexts.Num(), 2);
			}

			TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryByContext( TestNamespace, ContextB );
			if( !FoundEntry2.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryByContext."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry2->Source == Source );
				TestEqual( TEXT("FoundEntry->Context.Num() == 2"), FoundEntry2->Contexts.Num(), 2);
			}

			// Test find by source
			TSharedPtr< FManifestEntry > FoundEntry3 = TestManifest.FindEntryBySource( TestNamespace, Source );
			if( !FoundEntry3.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryBySource."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry3->Source == Source );
				TestEqual( TEXT("FoundEntry->Context.Num() == 2"), FoundEntry3->Contexts.Num(), 2);
			}

			TestTrue( TEXT("FoundEntry1 == FoundEntry2 == FoundEntry3"), (FoundEntry1 == FoundEntry2) && (FoundEntry1 == FoundEntry3) );

		}



		// Adding entries with Source that is NOT an exact match and matching context
		{
			// Source mismatched by Source Text.
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceA( TEXT("Conflicting TestTextA") );
				FLocItem ConflictingSourceB( TEXT("Conflicting TestTextB") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceA, ContextA );
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceB, ContextA );

				// Adding the second entry reports failure and entry count is not increased
				TestFalse( TEXT("AddSource result == false"), bResult);
				TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );	
			}

			// Source mismatched by standard metadata type (not * prefixed) 
			{
				FInternationalizationManifest ManifestTypeConflict;

				FLocItem ConflictingSourceMetadataTypeA = Source;
				FLocItem ConflictingSourceMetadataTypeB = Source;

				// Set metadata with the same name but different type
				ConflictingSourceMetadataTypeA.MetadataObj->SetBoolField( TEXT("ConflictingType"), true );
				ConflictingSourceMetadataTypeB.MetadataObj->SetStringField( TEXT("ConflictingType"), TEXT("true") );

				ManifestTypeConflict.AddSource( TestNamespace, ConflictingSourceMetadataTypeA, ContextA );
				bool bResult = ManifestTypeConflict.AddSource( TestNamespace, ConflictingSourceMetadataTypeB, ContextA );

				TestFalse( TEXT("AddSource result == false"), bResult);
				TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(ManifestTypeConflict), 1 );

				// Should not find a match when searching with the second Source we tried to add
				TSharedPtr< FManifestEntry > FoundEntry = ManifestTypeConflict.FindEntryBySource( TestNamespace, ConflictingSourceMetadataTypeB );
				TestInvalid( TEXT("FoundEntry is not valid"), FoundEntry);

			}

			// Source mismatched by standard metadata value (not * prefixed)
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceMetadataValueA = Source;
				FLocItem ConflictingSourceMetadataValueB = Source;

				// Set metadata with the same name and type but different value
				ConflictingSourceMetadataValueA.MetadataObj->SetStringField( TEXT("ConflictingValue"), TEXT("A") );
				ConflictingSourceMetadataValueB.MetadataObj->SetStringField( TEXT("ConflictingValue"), TEXT("B") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueA, ContextA );
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueB, ContextA );

				TestFalse( TEXT("AddSource result == false"), bResult);
				TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );

				// Should not find a match when searching with the second Source we tried to add
				TSharedPtr< FManifestEntry > FoundEntry = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataValueB );
				TestInvalid( TEXT("FoundEntry is not valid"), FoundEntry);

			}

			// Source mismatched by * prefixed metadata type
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceMetadataTypeA = Source;
				FLocItem ConflictingSourceMetadataTypeB = Source;

				// Set metadata with the same name but different type
				ConflictingSourceMetadataTypeA.MetadataObj->SetBoolField( TEXT("*ConflictingType"), true );
				ConflictingSourceMetadataTypeB.MetadataObj->SetStringField( TEXT("*ConflictingType"), TEXT("true") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataTypeA, ContextA );

				// Though the Source items are considered to be a match(they are equal) in this case, the manifest reports this as a conflict
				//  and should not add an entry.  The reason is that AddSource does an 'exact' match check on the metadata object
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataTypeB, ContextA );

				TestFalse( TEXT("AddSource result == false"), bResult);
				TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );

				// We should be able to find the entry using either Source FLocItem
				TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataTypeA );
				TestValid( TEXT("FoundEntry1 is valid"), FoundEntry1);

				TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataTypeB );
				TestValid( TEXT("FoundEntry2 is valid"), FoundEntry2);


			}

			// Source mismatched by * prefixed metadata value
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceMetadataValueA = Source;
				FLocItem ConflictingSourceMetadataValueB = Source;

				// Set metadata with the same name and type but different value
				ConflictingSourceMetadataValueA.MetadataObj->SetStringField( TEXT("*ConflictingValue"), TEXT("A") );
				ConflictingSourceMetadataValueB.MetadataObj->SetStringField( TEXT("*ConflictingValue"), TEXT("B") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueA, ContextA );

				// Thought the Source items are considered to be a match(they are equal) in this case, the manifest reports this as a conflict
				//  and should not add an entry.  The reason is that AddSource does an 'exact' match check on the metadata object
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueB, ContextA );

				TestFalse( TEXT("AddSource result == false"), bResult);
				TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );

				// We should be able to find the entry using either Source FLocItem
				TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataValueA );
				TestValid( TEXT("FoundEntry1 is valid"), FoundEntry1);

				TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataValueB );
				TestValid( TEXT("FoundEntry2 is valid"), FoundEntry2);

			}

		}

		// Adding entries with Source that is NOT an exact match and different context
		{
			// Source mismatched by Source Text.
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceA( TEXT("Conflicting TestTextA") );
				FLocItem ConflictingSourceB( TEXT("Conflicting TestTextB") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceA, ContextA );
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceB, ContextB );

				TestTrue( TEXT("AddSource result == true"), bResult);
				TestEqual( TEXT("ManifestCount == 2"), CountManifestEntries(TestManifest), 2 );

				// We should be able to find two unique entries by source
				TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceA );
				TestValid( TEXT("FoundEntry1 is valid"), FoundEntry1);

				TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceB );
				TestValid( TEXT("FoundEntry2 is valid"), FoundEntry2);

				TestTrue( TEXT("FoundEntry1 != FoundEntry2"), FoundEntry1 != FoundEntry2 );

				// We should be able to find two unique entries by context
				TSharedPtr< FManifestEntry > FoundEntry3 = TestManifest.FindEntryByContext( TestNamespace, ContextA );
				TestValid( TEXT("FoundEntry3 is valid"), FoundEntry3);

				TSharedPtr< FManifestEntry > FoundEntry4 = TestManifest.FindEntryByContext( TestNamespace, ContextB );
				TestValid( TEXT("FoundEntry3 is valid"), FoundEntry4);

				TestTrue( TEXT("FoundEntry3 != FoundEntry4"), FoundEntry3 != FoundEntry4 );

				// Found entry looked up by source A should match entry looked up by context A
				TestTrue( TEXT("FoundEntry1 == FoundEntry3"), FoundEntry1 == FoundEntry3 );

				// Found entry looked up by source B should match entry looked up by context B
				TestTrue( TEXT("FoundEntry2 == FoundEntry4"), FoundEntry2 == FoundEntry4 );

			}

			// Source mismatched by standard metadata type (not * prefixed)
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceMetadataTypeA = Source;
				FLocItem ConflictingSourceMetadataTypeB = Source;

				// Set metadata with the same name but different type
				ConflictingSourceMetadataTypeA.MetadataObj->SetBoolField( TEXT("ConflictingType"), true );
				ConflictingSourceMetadataTypeB.MetadataObj->SetStringField( TEXT("ConflictingType"), TEXT("true") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataTypeA, ContextA );
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataTypeB, ContextB );

				TestTrue( TEXT("AddSource result == true"), bResult);
				TestEqual( TEXT("ManifestCount == 2"), CountManifestEntries(TestManifest), 2 );

				// We should be able to find two unique entries by source
				TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataTypeA );
				TestValid( TEXT("FoundEntry1 is valid"), FoundEntry1);

				TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataTypeB );
				TestValid( TEXT("FoundEntry2 is valid"), FoundEntry2);

				TestTrue( TEXT("FoundEntry1 != FoundEntry2"), FoundEntry1 != FoundEntry2 );

				// We should be able to find two unique entries by context
				TSharedPtr< FManifestEntry > FoundEntry3 = TestManifest.FindEntryByContext( TestNamespace, ContextA );
				TestValid( TEXT("FoundEntry3 is valid"), FoundEntry3);

				TSharedPtr< FManifestEntry > FoundEntry4 = TestManifest.FindEntryByContext( TestNamespace, ContextB );
				TestValid( TEXT("FoundEntry3 is valid"), FoundEntry4);

				TestTrue( TEXT("FoundEntry3 != FoundEntry4"), FoundEntry3 != FoundEntry4 );

				// Found entry looked up by source A should match entry looked up by context A
				TestTrue( TEXT("FoundEntry1 == FoundEntry3"), FoundEntry1 == FoundEntry3 );

				// Found entry looked up by source B should match entry looked up by context B
				TestTrue( TEXT("FoundEntry2 == FoundEntry4"), FoundEntry2 == FoundEntry4 );

			}

			// Source mismatched by standard metadata value (not * prefixed)
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceMetadataValueA = Source;
				FLocItem ConflictingSourceMetadataValueB = Source;

				// Set metadata with the same name and type but different value
				ConflictingSourceMetadataValueA.MetadataObj->SetStringField( TEXT("ConflictingValue"), TEXT("A") );
				ConflictingSourceMetadataValueB.MetadataObj->SetStringField( TEXT("ConflictingValue"), TEXT("B") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueA, ContextA );
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueB, ContextB );

				TestTrue( TEXT("AddSource result == true"), bResult);
				TestEqual( TEXT("ManifestCount == 2"), CountManifestEntries(TestManifest), 2 );

				// We should be able to find two unique entries by source
				TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataValueA );
				TestValid( TEXT("FoundEntry1 is valid"), FoundEntry1);

				TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataValueB );
				TestValid( TEXT("FoundEntry2 is valid"), FoundEntry2);

				TestTrue( TEXT("FoundEntry1 != FoundEntry2"), FoundEntry1 != FoundEntry2 );

				// We should be able to find two unique entries by context
				TSharedPtr< FManifestEntry > FoundEntry3 = TestManifest.FindEntryByContext( TestNamespace, ContextA );
				TestValid( TEXT("FoundEntry3 is valid"), FoundEntry3);

				TSharedPtr< FManifestEntry > FoundEntry4 = TestManifest.FindEntryByContext( TestNamespace, ContextB );
				TestValid( TEXT("FoundEntry3 is valid"), FoundEntry4);

				TestTrue( TEXT("FoundEntry3 != FoundEntry4"), FoundEntry3 != FoundEntry4 );

				// Found entry looked up by source A should match entry looked up by context A
				TestTrue( TEXT("FoundEntry1 == FoundEntry3"), FoundEntry1 == FoundEntry3 );

				// Found entry looked up by source B should match entry looked up by context B
				TestTrue( TEXT("FoundEntry2 == FoundEntry4"), FoundEntry2 == FoundEntry4 );

			}

			// Source mismatched by * prefixed metadata type
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceMetadataTypeA = Source;
				FLocItem ConflictingSourceMetadataTypeB = Source;

				// Set metadata with the same name but different type
				ConflictingSourceMetadataTypeA.MetadataObj->SetBoolField( TEXT("*ConflictingType"), true );
				ConflictingSourceMetadataTypeB.MetadataObj->SetStringField( TEXT("*ConflictingType"), TEXT("true") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataTypeA, ContextA );

				// Though the Source items are considered to be a match(they are equal) in this case, the manifest reports this as a conflict
				//  and should not add an entry.  The reason is that AddSource does an 'exact' match check on the metadata object
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataTypeB, ContextB );

				TestFalse( TEXT("AddSource result == false"), bResult);
				TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );

				// We should be able to find the entry using either Source FLocItem
				TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataTypeA );
				TestValid( TEXT("FoundEntry1 is valid"), FoundEntry1);

				TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataTypeB );
				TestValid( TEXT("FoundEntry2 is valid"), FoundEntry2);
			}

			// Source mismatched by * prefixed metadata value
			{
				FInternationalizationManifest TestManifest;

				FLocItem ConflictingSourceMetadataValueA = Source;
				FLocItem ConflictingSourceMetadataValueB = Source;

				// Set metadata with the same name and type but different value
				ConflictingSourceMetadataValueA.MetadataObj->SetStringField( TEXT("*ConflictingValue"), TEXT("A") );
				ConflictingSourceMetadataValueB.MetadataObj->SetStringField( TEXT("*ConflictingValue"), TEXT("B") );

				TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueA, ContextA );

				// Thought the Source items are considered to be a match(they are equal) in this case, the manifest reports this as a conflict
				//  and should not add an entry.  The reason is that AddSource does an 'exact' match check on the metadata object
				bool bResult = TestManifest.AddSource( TestNamespace, ConflictingSourceMetadataValueB, ContextB );

				TestFalse( TEXT("AddSource result == false"), bResult);
				TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );

				// We should be able to find the entry using either Source FLocItem
				TSharedPtr< FManifestEntry > FoundEntry1 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataValueA );
				TestValid( TEXT("FoundEntry1 is valid"), FoundEntry1);

				TSharedPtr< FManifestEntry > FoundEntry2 = TestManifest.FindEntryBySource( TestNamespace, ConflictingSourceMetadataValueB );
				TestValid( TEXT("FoundEntry2 is valid"), FoundEntry2);

			}

		}

		// Adding an entry that only differs in the optional flag 
		{
			FInternationalizationManifest TestManifest;

			// Reports success and our entry count does not change.  bIsOptional is not a key and is not used during lookup.  When
			//  we AddSource, we find an existing matching entry so AddSource returns true but no new entry is added.  The original
			//  entry's bIsOptional value is not updated.
			FContext ContextConflictingOptionalFlag = ContextA;
			ContextConflictingOptionalFlag.bIsOptional = !ContextA.bIsOptional;

			TestManifest.AddSource( TestNamespace, Source, ContextA );
			bool bResult = TestManifest.AddSource( TestNamespace, Source, ContextConflictingOptionalFlag);

			TestTrue( TEXT("AddSource result == true"), bResult);
			TestEqual( TEXT("ManifestCount == 1"), CountManifestEntries(TestManifest), 1 );

			// We should be able to look up the existing entry using the ContextConflictingOptionalFlag context but the entries bIsOptional flag will match ContextA
			TSharedPtr< FManifestEntry > FoundEntry = TestManifest.FindEntryByContext( TestNamespace, ContextConflictingOptionalFlag );
			if( !FoundEntry.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryByContext."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->bIsOptional == ContextA->bIsOptional"), FoundEntry->Contexts[0].bIsOptional == ContextA.bIsOptional );
			}
		}

		// Add an entry with null key metadata to see if we can retrieve it with non-null but empty key metadata
		{
			FInternationalizationManifest TestManifest;

			FContext ContextC;
			ContextC.Key			= TEXT("KeyC");
			ContextC.SourceLocation = TEXT("SourceLocationC");
			ContextC.InfoMetadataObj = MakeShareable( new FLocMetadataObject(*InfoMetadataB) );
			ContextC.KeyMetadataObj = NULL;

			Source.MetadataObj = NULL;
			TestManifest.AddSource( TestNamespace, Source, ContextC );

			//  Now give our context and source valid but empty metadata
			ContextC.KeyMetadataObj = MakeShareable( new FLocMetadataObject() );
			Source.MetadataObj = MakeShareable( new FLocMetadataObject() );

			// Ensure we find the entry we added by source
			TSharedPtr< FManifestEntry > FoundEntry;
			FoundEntry = TestManifest.FindEntryBySource( TestNamespace, Source );
			if( !FoundEntry.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryBySource."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == Source );
			}

			// Ensure we find the entry we added by context
			FoundEntry = TestManifest.FindEntryByContext( TestNamespace, ContextC );
			if( !FoundEntry.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryByContext."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == Source );
			}
		}

		// Add an entry with non-null but empty key metadata and see if we can retrieve null metadata
		{
			FInternationalizationManifest TestManifest;

			FContext ContextC;
			ContextC.Key			= TEXT("KeyC");
			ContextC.SourceLocation = TEXT("SourceLocationC");
			ContextC.InfoMetadataObj = MakeShareable( new FLocMetadataObject(*InfoMetadataB) );
			ContextC.KeyMetadataObj = MakeShareable( new FLocMetadataObject());

			Source.MetadataObj = MakeShareable( new FLocMetadataObject());
			TestManifest.AddSource( TestNamespace, Source, ContextC );

			//  Now give our context and source Null metadata
			ContextC.KeyMetadataObj = NULL;
			Source.MetadataObj = NULL;

			// Ensure we find the entry we added by source
			TSharedPtr< FManifestEntry > FoundEntry;
			FoundEntry = TestManifest.FindEntryBySource( TestNamespace, Source );
			if( !FoundEntry.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryBySource."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == Source );
			}

			// Ensure we find the entry we added by context
			FoundEntry = TestManifest.FindEntryByContext( TestNamespace, ContextC );
			if( !FoundEntry.IsValid() )
			{
				AddError(TEXT("FManifestEntry could not find entry using FindEntryByContext."));
			}
			else
			{
				TestTrue( TEXT("FoundEntry->Source == Source"), FoundEntry->Source == Source );
			}
		}
	}

	return true;
}