// TestCompiler.cpp
//------------------------------------------------------------------------------

// Includes
//------------------------------------------------------------------------------
#include "FBuildTest.h"

// FBuildCore
#include "Tools/FBuild/FBuildCore/BFF/BFFParser.h"
#include "Tools/FBuild/FBuildCore/FBuild.h"
#include "Tools/FBuild/FBuildCore/Graph/CompilerNode.h"
#include "Tools/FBuild/FBuildCore/Graph/NodeGraph.h"
#include "Tools/FBuild/FBuildCore/Helpers/ToolManifest.h"

//------------------------------------------------------------------------------
TEST_GROUP( TestCompiler, FBuildTest )
{
public:
    uint64_t GetToolId( const FBuildForTest & fBuild ) const;
};

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, BuildCompiler_Explicit )
{
    uint64_t toolIdA;
    uint64_t toolIdB;
    uint64_t toolIdC;

    // Build
    {
        FBuildTestOptions options;
        options.m_ForceCleanBuild = true;
        options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/Explicit/explicit.bff";
        FBuildForTest fBuild( options );
        TEST_ASSERT( fBuild.Initialize() );

        // Build a file generated by a Compiler that we compiled
        TEST_ASSERT( fBuild.Build( "Compiler" ) );

        // Save DB for use by NoRebuild test
        TEST_ASSERT( fBuild.SaveDependencyGraph( "../tmp/Test/TestCompiler/Explicit/explicit.fdb" ) );

        // Check stats: Seen, Built, Type
        CheckStatsNode( 1, 1, Node::COMPILER_NODE );

        // Get the ToolId
        toolIdA = GetToolId( fBuild );
    }

    // Check no rebuild
    {
        FBuildTestOptions options;
        options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/Explicit/explicit.bff";
        FBuildForTest fBuild( options );
        TEST_ASSERT( fBuild.Initialize( "../tmp/Test/TestCompiler/Explicit/explicit.fdb" ) );

        // Build a file generated by a Compiler that we compiled
        TEST_ASSERT( fBuild.Build( "Compiler" ) );

        // Check stats: Seen, Built, Type
        CheckStatsNode( 1, 0, Node::COMPILER_NODE );

        // Get the ToolId
        toolIdB = GetToolId( fBuild );
    }

    // Check no rebuild after Migration
    {
        FBuildTestOptions options;
        options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/Explicit/explicit.bff";
        options.m_ForceDBMigration_Debug = true;
        FBuildForTest fBuild( options );
        TEST_ASSERT( fBuild.Initialize( "../tmp/Test/TestCompiler/Explicit/explicit.fdb" ) );

        // Build a file generated by a Compiler that we compiled
        TEST_ASSERT( fBuild.Build( "Compiler" ) );

        // Check stats: Seen, Built, Type
        CheckStatsNode( 1, 0, Node::COMPILER_NODE );

        // Get the ToolId
        toolIdC = GetToolId( fBuild );
    }

    // ToolId should be identical for each case
    TEST_ASSERT( toolIdA == toolIdB );
    TEST_ASSERT( toolIdA == toolIdC );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, BuildCompiler_Implicit )
{
    uint64_t toolIdA;
    uint64_t toolIdB;
    uint64_t toolIdC;
    AStackString compilerNodeName;

    // Implicit Definition
    {
        FBuildTestOptions options;
        options.m_ForceCleanBuild = true;
        options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/Implicit/implicit.bff";
        FBuildForTest fBuild( options );
        TEST_ASSERT( fBuild.Initialize() );

        // Build a file generated by a Compiler that we compiled
        TEST_ASSERT( fBuild.Build( "ObjectList" ) );

        // Save DB for use by next test
        TEST_ASSERT( fBuild.SaveDependencyGraph( "../tmp/Test/TestCompiler/Implicit/implicit.fdb" ) );

        // Ensure node was implicitly created
        Array<const Node *> compilerNodes;
        fBuild.GetNodesOfType( Node::COMPILER_NODE, compilerNodes );
        TEST_ASSERT( compilerNodes.GetSize() == 1 );
        compilerNodeName = compilerNodes[ 0 ]->GetName();

        // Check stats: Seen, Built, Type
        // - Since we have no object files, the implicitly created Compiler node
        //   will not be built
        CheckStatsNode( 0, 0, Node::COMPILER_NODE );
    }

    // Build
    {
        FBuildTestOptions options;
        options.m_ForceCleanBuild = true;
        options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/Implicit/implicit.bff";
        FBuildForTest fBuild( options );
        TEST_ASSERT( fBuild.Initialize() );

        // Build the compiler so we can test it's behavior
        TEST_ASSERT( fBuild.Build( compilerNodeName ) );

        // Ensure node was implicitly created
        Array<const Node *> compilerNodes;
        fBuild.GetNodesOfType( Node::COMPILER_NODE, compilerNodes );
        TEST_ASSERT( compilerNodes.GetSize() == 1 );

        // Save DB for use by next test
        TEST_ASSERT( fBuild.SaveDependencyGraph( "../tmp/Test/TestCompiler/Implicit/implicit.fdb" ) );

        // Check stats: Seen, Built, Type
        CheckStatsNode( 1, 1, Node::COMPILER_NODE );

        // Get the ToolId
        toolIdA = GetToolId( fBuild );
    }

    // Check No Rebuild
    {
        FBuildTestOptions options;
        options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/Implicit/implicit.bff";
        FBuildForTest fBuild( options );
        TEST_ASSERT( fBuild.Initialize( "../tmp/Test/TestCompiler/Implicit/implicit.fdb" ) );

        // Build a file generated by a Compiler that we compiled
        TEST_ASSERT( fBuild.Build( compilerNodeName ) );

        // Save DB for use by NoRebuild test
        TEST_ASSERT( fBuild.SaveDependencyGraph( "../tmp/Test/TestCompiler/Implicit/implicit.fdb" ) );

        // Check stats: Seen, Built, Type
        CheckStatsNode( 1, 0, Node::COMPILER_NODE );

        // Get the ToolId
        toolIdB = GetToolId( fBuild );
    }

    // Check no rebuild after Migration
    {
        FBuildTestOptions options;
        options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/Implicit/implicit.bff";
        options.m_ForceDBMigration_Debug = true;
        FBuildForTest fBuild( options );
        TEST_ASSERT( fBuild.Initialize( "../tmp/Test/TestCompiler/Implicit/implicit.fdb" ) );

        // Build a file generated by a Compiler that we compiled
        TEST_ASSERT( fBuild.Build( compilerNodeName ) );

        // Save DB for use by NoRebuild test
        TEST_ASSERT( fBuild.SaveDependencyGraph( "../tmp/Test/TestCompiler/Implicit/implicit.fdb" ) );

        // Check stats: Seen, Built, Type
        CheckStatsNode( 1, 0, Node::COMPILER_NODE );

        // Get the ToolId
        toolIdC = GetToolId( fBuild );
    }

    // ToolId should be identical for each case
    TEST_ASSERT( toolIdA == toolIdB );
    TEST_ASSERT( toolIdA == toolIdC );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, ConflictingFiles1 )
{
    Parse( "Tools/FBuild/FBuildTest/Data/TestCompiler/conflict1.bff", true ); // Expect failure
    TEST_ASSERT( GetRecordedOutput().Find( "Error #1100" ) );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, ConflictingFiles2 )
{
    Parse( "Tools/FBuild/FBuildTest/Data/TestCompiler/conflict2.bff", true ); // Expect failure
    TEST_ASSERT( GetRecordedOutput().Find( "Error #1100" ) );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, ConflictingFiles3 )
{
    Parse( "Tools/FBuild/FBuildTest/Data/TestCompiler/conflict3.bff", true ); // Expect failure
    TEST_ASSERT( GetRecordedOutput().Find( "Error #1100" ) );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, ConflictingFiles4 )
{
    Parse( "Tools/FBuild/FBuildTest/Data/TestCompiler/conflict4.bff", true ); // Expect failure
    TEST_ASSERT( GetRecordedOutput().Find( "Error #1100" ) );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, CompilerExecutableAsDependency )
{
    FBuildTestOptions options;
    options.m_ForceCleanBuild = true;
    options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/compile.bff";
    FBuildForTest fBuild( options );
    TEST_ASSERT( fBuild.Initialize() );

    // Build a file generated by a Compiler that we compiled
    TEST_ASSERT( fBuild.Build( "ObjectList" ) );

    // Save DB for use by NoRebuild test
    TEST_ASSERT( fBuild.SaveDependencyGraph( "../tmp/Test/TestCompiler/executableasdependency.fdb" ) );

    // Check stats: Seen, Built, Type
    CheckStatsNode( 1, 1, Node::COMPILER_NODE );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, CompilerExecutableAsDependency_NoRebuild )
{
    FBuildTestOptions options;
    options.m_ConfigFile = "Tools/FBuild/FBuildTest/Data/TestCompiler/compile.bff";
    FBuildForTest fBuild( options );
    TEST_ASSERT( fBuild.Initialize( "../tmp/Test/TestCompiler/executableasdependency.fdb" ) );

    // Build a file generated by a Compiler that we compiled
    TEST_ASSERT( fBuild.Build( "ObjectList" ) );

    // Check stats: Seen, Built, Type
    CheckStatsNode( 1, 0, Node::COMPILER_NODE );
}

//------------------------------------------------------------------------------
TEST_CASE( TestCompiler, MultipleImplicitCompilers )
{
    Parse( "Tools/FBuild/FBuildTest/Data/TestCompiler/multipleimplicitcompilers.bff" );
}

// GetToolId
//------------------------------------------------------------------------------
uint64_t TestCompiler::GetToolId( const FBuildForTest & fBuild ) const
{
    Array<const Node *> nodes;
    fBuild.GetNodesOfType( Node::COMPILER_NODE, nodes );
    TEST_ASSERT( nodes.GetSize() == 1 );
    const uint64_t toolId = nodes[ 0 ]->CastTo<CompilerNode>()->GetManifest().GetToolId();
    TEST_ASSERT( toolId );
    return toolId;
}

//------------------------------------------------------------------------------
