text:
#include "OFAction.h"
#include "OFFlow.h"
#include "PacketInfo.h"
#include "OFDatapath.h"
#include "OvsNetBuffer.h"
#include "WinlPacket.h"
#include "Ipv4.h"
#include "Ipv6.h"
#include "Tcp.h"
#include "Udp.h"
#include "Sctp.h"
#include "Random.h"
#include "Argument.h"
#include "WinlFlow.h"
#include "Upcall.h"
#include "ArgumentType.h"
#include "Sctx_Nic.h"
#include "Message.h"
#include "NblsIngress.h"
#include "Arp.h"
#include "OFPort.h"
#include "ArgToAttribute.h"
#include "Nbls.h"
#include "Vlan.h"
#include "ArgVerification.h"

#define OVS_ACTION_SAMPLE_MAX_DEPTH        3

static BOOLEAN _ExecuteAction_OutToUserspace(OVS_DATAPATH* pDatapath, _In_ NET_BUFFER* pNb, _In_ const OVS_OFPACKET_INFO* pPacketInfo, _In_ const OVS_ARGUMENT_GROUP* pArguments)
{
    OVS_UPCALL_INFO upcallInfo = { 0 };
    BOOLEAN ok = FALSE;

    OVS_CHECK(pPacketInfo);

    upcallInfo.command = OVS_MESSAGE_COMMAND_PACKET_UPCALL_ACTION;
    upcallInfo.pPacketInfo = pPacketInfo;
    upcallInfo.pUserData = NULL;
    upcallInfo.portId = 0;

    for (UINT i = 0; i < pArguments->count; ++i)
    {
        OVS_ARGUMENT* pArg = pArguments->args + i;
        BOOLEAN ok = IsArgumentValid(pArg);
        OVS_ARGTYPE argType = pArg->type;

        if (!ok)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ "packet upcall action: arg of argtype %u is invalid!\n", argType);
            OVS_CHECK(__UNEXPECTED__);
            return FALSE;
        }

        switch (argType)
        {
        case OVS_ARGTYPE_ACTION_UPCALL_DATA:
            //it is released in send packet to userspace
            upcallInfo.pUserData = pArg;
            break;

        case OVS_ARGTYPE_ACTION_UPCALL_PORT_ID:
            upcallInfo.portId = GET_ARG_DATA(pArg, UINT32);
            break;
        }
    }

    ok = QueuePacketToUserspace(pDatapath, pNb, &upcallInfo);

    return ok;
}

static BOOLEAN _ExecuteAction_Set(OVS_NET_BUFFER* pONb, const OVS_ARGUMENT_GROUP* pArgs)
{
    BOOLEAN ok = TRUE;
    OVS_ARGUMENT* pArg = NULL;
    OVS_ARGTYPE argType = OVS_ARGTYPE_INVALID;

    OVS_CHECK(pArgs->count == 1);

    pArg = pArgs->args;
    argType = pArg->type;

    switch (argType)
    {
        //NOTE FOR OVS 2.3:
        //OVS_ARGTYPE_PI_TCP_FLAGS and OVS_ARGTYPE_PI_DATAPATH_HASH and OVS_ARGTYPE_PI_DATAPATH_RECIRCULATION_ID
        //are not settable
    case OVS_ARGTYPE_PI_PACKET_MARK:
        pONb->packetMark = GET_ARG_DATA(pArg, UINT32);
        break;

    case OVS_ARGTYPE_PI_PACKET_PRIORITY:
        pONb->packetPriority = GET_ARG_DATA(pArg, UINT32);
        break;

    case OVS_ARGTYPE_PI_IPV4_TUNNEL:
        pONb->pTunnelInfo = pArg->data;
        break;

    case OVS_ARGTYPE_PI_ETH_ADDRESS:
        ok = ONB_SetEthernetAddress(pONb, pArg->data);
        break;

    case OVS_ARGTYPE_PI_IPV4:
        ok = ONB_SetIpv4(pONb, pArg->data);
        break;

    case OVS_ARGTYPE_PI_IPV6:
        ok = ONB_SetIpv6(pONb, pArg->data);
        break;

    case OVS_ARGTYPE_PI_TCP:
        ok = ONB_SetTcp(pONb, pArg->data);
        break;

    case OVS_ARGTYPE_PI_UDP:
        ok = ONB_SetUdp(pONb, pArg->data);
        break;

    case OVS_ARGTYPE_PI_SCTP:
        ok = ONB_SetSctp(pONb, pArg->data);
        break;

    case OVS_ARGTYPE_PI_MPLS:
        OVS_CHECK(__NOT_IMPLEMENTED__);
        //TODO:
        //ok = ONB_SetMpls(pOvsNb, pArg->data);
        break;
    }

    return ok;
}

//TODO: this function was never tested, and is likely to contain errors
static BOOLEAN _ExecuteAction_Sample(_Inout_ OVS_NET_BUFFER *pOvsNb, _In_ const OVS_ARGUMENT_GROUP* pArguments,
    _In_ const OutputToPortCallback outputToPort)
{
    OVS_ARGUMENT_GROUP* pSampleActionsArgs = NULL;
    OVS_ARGUMENT_GROUP* pOldActionsArgs = NULL;
    BOOLEAN ok = TRUE;

    for (UINT i = 0; i < pArguments->count; ++i)
    {
        OVS_ARGUMENT* pArg = pArguments->args + i;
        OVS_ARGTYPE argType = pArg->type;

        switch (argType)
        {
        case OVS_ARGTYPE_ACTION_SAMPLE_PROBABILITY:
        {
            UINT32 value = GET_ARG_DATA(pArg, UINT32);

            if ((UINT32)QuickRandom(100) >= value)
                return TRUE;
        }
            break;

        case OVS_ARGTYPE_ACTION_SAMPLE_ACTIONS_GROUP:
            pSampleActionsArgs = pArg->data;
            break;
        }
    }

    //we must execute the actions in pSampleActionsArgs, so we must save the original
    //actions arg group, and put it back later (to have a correct cleanup at the end)
    if (pSampleActionsArgs)
    {
        pOldActionsArgs = pOvsNb->pActions->pActionGroup;
        pOvsNb->pActions->pActionGroup = pSampleActionsArgs;
    }

    //TODO: the actions in the sample are expected to be:
    // a) no actions (i.e. pSampleActionsArgs count = 0 and size = 0)
    // b) send to userspace
    //In the cases above, it is safe to use this net buffer.
    //An unexpected case is, if there is an action that modified the ONB. In this case,
    //we should duplicate the ONB and send the ONB. OR, we might be able to implement
    //some copy-on-write functionality for OVS_NET_BUFFER
    ok = ExecuteActions(pOvsNb, outputToPort);

    if (pSampleActionsArgs)
    {
        pOvsNb->pActions->pActionGroup = pOldActionsArgs;
    }

    return ok;
}

static BOOLEAN _ExecuteAction_Hash(_Inout_ OVS_NET_BUFFER *pOvsNb, _In_ const OVS_ARGUMENT_GROUP* pArguments)
{
    UNREFERENCED_PARAMETER(pOvsNb);
    UNREFERENCED_PARAMETER(pArguments);

    OVS_CHECK(__NOT_IMPLEMENTED__);

    return FALSE;
}

static BOOLEAN _ExecuteAction_Recirculation(_Inout_ OVS_NET_BUFFER *pOvsNb, _In_ const OVS_ARGUMENT_GROUP* pArguments)
{
    UNREFERENCED_PARAMETER(pOvsNb);
    UNREFERENCED_PARAMETER(pArguments);

    OVS_CHECK(__NOT_IMPLEMENTED__);

    return FALSE;
}

static OVS_OFPORT* _FindDestPort_Ref(_In_ const OVS_OFPORT* pSourcePort, UINT32 ofPortNumber)
{
    UINT16 validPortNumber = OVS_INVALID_PORT_NUMBER;
    OVS_OFPORT* pDestOFPort = NULL;

    //NOTE: we don't need to lock neither pSourcePort, nor pDestPort, because these fields (id, type, isExternal) never change
    if (ofPortNumber >= OVS_MAX_PORTS)
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " invalid port number from userspace: %u\n", ofPortNumber);
        return NULL;
    }

    validPortNumber = (UINT16)ofPortNumber;

    pDestOFPort = OFPort_FindByNumber_Ref(validPortNumber);
    if (!pDestOFPort)
    {
        DEBUGP(LOG_ERROR, "could not find of port: %u!\n", validPortNumber);
        return NULL;
    }

    //if we know from now that the dest port is not connected (i.e. has no associated NIC), we won't attempt to send through the port
    if (pDestOFPort->portId == NDIS_SWITCH_DEFAULT_PORT_ID &&
        (pDestOFPort->ofPortType == OVS_OFPORT_TYPE_PHYSICAL || pDestOFPort->ofPortType == OVS_OFPORT_TYPE_MANAG_OS))
    {
        OVS_REFCOUNT_DEREFERENCE(pDestOFPort);

        return NULL;
    }

    //if the src port = internal / external, we won't output to gre / vxlan:
    //for external: we would be sending the packet back whence it came
    //for internal: when outputting via NORMAL from hypervisor to hypervisor, we don't want to send to the hypervisor both via external and gre
    if (pDestOFPort->ofPortType == OVS_OFPORT_TYPE_GRE || pDestOFPort->ofPortType == OVS_OFPORT_TYPE_VXLAN)
    {
        if (pSourcePort->portId != NDIS_SWITCH_DEFAULT_PORT_ID &&
            pSourcePort->isExternal ||
            pSourcePort->ofPortType == OVS_OFPORT_TYPE_MANAG_OS)
        {
            OVS_REFCOUNT_DEREFERENCE(pDestOFPort);

            return NULL;
        }
    }

    //if the src port = gre / vxlan, we must not send to internal, nor to external (we would be sending back to the same hyper-v switch port)
    if (pSourcePort->ofPortType == OVS_OFPORT_TYPE_GRE ||
        pSourcePort->ofPortType == OVS_OFPORT_TYPE_VXLAN)
    {
        if (pDestOFPort && pDestOFPort->portId != NDIS_SWITCH_DEFAULT_PORT_ID)
        {
            if (pDestOFPort->isExternal ||
                pDestOFPort->ofPortType == OVS_OFPORT_TYPE_MANAG_OS)
            {
                OVS_REFCOUNT_DEREFERENCE(pDestOFPort);

                return NULL;
            }
        }
    }

    return pDestOFPort;
}

BOOLEAN ExecuteActions(_Inout_ OVS_NET_BUFFER* pOvsNb, _In_ const OutputToPortCallback outputToPort)
{
    BOOLEAN ok = TRUE;
    const OVS_ARGUMENT_GROUP* pActionArgs = pOvsNb->pActions->pActionGroup;
    OVS_OFPORT* pDestOFPort = NULL;
    UINT32 ofPortNumber = (UINT32)-1;

    for (UINT16 i = 0; i < pActionArgs->count; ++i)
    {
        const OVS_ARGUMENT* pArg = pActionArgs->args + i;
        OVS_ARGTYPE argType = pArg->type;

        ok = TRUE;

        if (pDestOFPort)
        {
            OVS_NET_BUFFER* pDuplicateOnb = ONB_Duplicate(pOvsNb);
            OVS_CHECK(pDuplicateOnb);

            if (pDuplicateOnb)
            {
                pDuplicateOnb->pDestinationPort = pDestOFPort;
                pDuplicateOnb->sendToPortNormal = FALSE;

                //output = output packet to port
                ok = (*outputToPort)(pDuplicateOnb);

                if (ok)
                {
                    KFree(pDuplicateOnb);
                }
                else
                {
                    ONB_Destroy(pDuplicateOnb->pSwitchInfo, &pDuplicateOnb);
                }

                ok = TRUE;

                OVS_REFCOUNT_DEREFERENCE(pDestOFPort);
                pDestOFPort = NULL;
            }
        }

        switch (argType)
        {
        case OVS_ARGTYPE_ACTION_OUTPUT_TO_PORT:
            ofPortNumber = GET_ARG_DATA(pArg, UINT32);

            if (ofPortNumber < OVS_MAX_PORTS)
            {
                pDestOFPort = _FindDestPort_Ref(pOvsNb->pSourcePort, ofPortNumber);
            }
            else
            {
                DEBUGP(LOG_ERROR, __FUNCTION__ " invalid port number from userspace: %u\n", ofPortNumber);
                ok = FALSE;
            }

            break;

        case OVS_ARGTYPE_ACTION_UPCALL_GROUP:
        {
            NET_BUFFER* pNb = ONB_GetNetBuffer(pOvsNb);
            OVS_OFPACKET_INFO* pPacketInfo = pOvsNb->pOriginalPacketInfo;

            _ExecuteAction_OutToUserspace(pOvsNb->pDatapath, pNb, pPacketInfo, pArg->data);
        }
            break;

        case OVS_ARGTYPE_ACTION_SETINFO_GROUP:
            ok = _ExecuteAction_Set(pOvsNb, pArg->data);
            break;

        case OVS_ARGTYPE_ACTION_SAMPLE_GROUP:
            ok = _ExecuteAction_Sample(pOvsNb, pArg->data, outputToPort);
            break;

        case OVS_ARGTYPE_ACTION_PUSH_VLAN:
            ok = Vlan_Push(pOvsNb, pArg->data);
            if (!ok)
            {
                goto Cleanup;
            }
            break;

        case OVS_ARGTYPE_ACTION_POP_VLAN:
            ok = Vlan_Pop(pOvsNb);
            break;

        case OVS_ARGTYPE_ACTION_PUSH_MPLS:
            OVS_CHECK(__NOT_IMPLEMENTED__);
            break;

        case OVS_ARGTYPE_ACTION_POP_MPLS:
            OVS_CHECK(__NOT_IMPLEMENTED__);
            break;

        case OVS_ARGTYPE_ACTION_HASH:
            ok = _ExecuteAction_Hash(pOvsNb, pArg->data);
            break;

        case OVS_ARGTYPE_ACTION_RECIRCULATION:
            //TODO: use copy-on-write for OVS_NET_BUFFER, OR
            //copy the ONB only if there are more arguments in pArg->data
            if (i < pActionArgs->count - 1)
            {
                OVS_NET_BUFFER* pDuplicateOnb = ONB_Duplicate(pOvsNb);
                if (pDuplicateOnb)
                {
                    ok = _ExecuteAction_Recirculation(pDuplicateOnb, pArg->data);
                    continue;
                }
            }

            ok = _ExecuteAction_Recirculation(pOvsNb, pArg->data);
            break;
        }

        if (!ok)
        {
            goto Cleanup;
        }
    }

    if (pDestOFPort)
    {
        pOvsNb->pDestinationPort = pDestOFPort;
        pOvsNb->sendToPortNormal = FALSE;

        ok = (*outputToPort)(pOvsNb);

        OVS_REFCOUNT_DEREFERENCE(pDestOFPort);
        pDestOFPort = NULL;
    }
    else
    {
        //i.e. did not send pOvsNb, _ProcessAllNblsIngress will destroy it.
        ok = FALSE;
    }

Cleanup:
    OVS_REFCOUNT_DEREFERENCE(pDestOFPort);

    return ok;
}

/********************************************************************************************/

static BOOLEAN _VerifyAction_Upcall(const OVS_ARGUMENT* pArg)
{
    UINT32 pid = 0;

    OVS_CHECK(IsArgTypeGroup(pArg->type));

    OVS_ARGUMENT* pPidArg = FindArgument(pArg->data, OVS_ARGTYPE_ACTION_UPCALL_PORT_ID);
    if (!pPidArg)
    {
        return FALSE;
    }

    pid = GET_ARG_DATA(pPidArg, UINT32);
    if (pid == 0)
    {
        return FALSE;
    }

    OVS_ARGUMENT* pData = FindArgument(pArg->data, OVS_ARGTYPE_ACTION_UPCALL_DATA);

    //if pData == FALSE, it means we don't have upcall data in upcall group - this is a valid state
    if (pData && ((pData->length && !pData->data) || (!pData->length && pData->data)))
    {
        return FALSE;
    }

    return TRUE;
}

static BOOLEAN _ValidateTransportPort(const OVS_OFPACKET_INFO* pPacketInfo)
{
    if (pPacketInfo->ethInfo.type != RtlUshortByteSwap(OVS_ETHERTYPE_IPV4) &&
        pPacketInfo->ethInfo.type != RtlUshortByteSwap(OVS_ETHERTYPE_IPV6))
    {
        DEBUGP(LOG_ERROR, "packet info's eth type != ipv4 & != ipv6\n");
        return FALSE;
    }

    if (pPacketInfo->tpInfo.sourcePort != OVS_PI_MASK_MATCH_WILDCARD(UINT16) ||
        pPacketInfo->tpInfo.destinationPort != OVS_PI_MASK_MATCH_WILDCARD(UINT16))
    {
        return TRUE;
    }

    else
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " src port == wildcard & dest port == wildcard: invalid\n");
        return FALSE;
    }
}

static BOOLEAN _SetAction_CreateIpv4TunnelFromArg(const OVS_ARGUMENT_GROUP* pArgs, _Inout_ OF_PI_IPV4_TUNNEL* pTunnelInfo)
{
    BE16 tunnelFlags = 0;

    for (UINT i = 0; i < pArgs->count; ++i)
    {
        OVS_ARGUMENT* pArg = pArgs->args + i;
        OVS_ARGTYPE argType = pArg->type;

        switch (argType)
        {
        case OVS_ARGTYPE_PI_TUNNEL_ID:
            pTunnelInfo->tunnelId = GET_ARG_DATA(pArg, BE64);

            tunnelFlags |= OVS_TUNNEL_FLAG_KEY;
            break;

        case OVS_ARGTYPE_PI_TUNNEL_IPV4_SRC:
            pTunnelInfo->ipv4Source = GET_ARG_DATA(pArg, BE32);
            break;

        case OVS_ARGTYPE_PI_TUNNEL_IPV4_DST:
            pTunnelInfo->ipv4Destination = GET_ARG_DATA(pArg, BE32);
            break;

        case OVS_ARGTYPE_PI_TUNNEL_TOS:
            pTunnelInfo->ipv4TypeOfService = GET_ARG_DATA(pArg, UINT8);
            break;

        case OVS_ARGTYPE_PI_TUNNEL_TTL:
            pTunnelInfo->ipv4TimeToLive = GET_ARG_DATA(pArg, UINT8);
            break;

        case OVS_ARGTYPE_PI_TUNNEL_DONT_FRAGMENT:
            tunnelFlags |= OVS_TUNNEL_FLAG_DONT_FRAGMENT;
            break;

        case OVS_ARGTYPE_PI_TUNNEL_CHECKSUM:
            tunnelFlags |= OVS_TUNNEL_FLAG_CHECKSUM;
            break;

        case OVS_ARGTYPE_PI_TUNNEL_OAM:
            OVS_CHECK_RET(__NOT_IMPLEMENTED__, FALSE);
            break;

        case OVS_ARGTYPE_PI_TUNNEL_GENEVE_OPTIONS:
            OVS_CHECK_RET(__NOT_IMPLEMENTED__, FALSE);
            break;

        default:
            return FALSE;
        }
    }

    pTunnelInfo->tunnelFlags = tunnelFlags;

    return TRUE;
}

static BOOLEAN _CreateActionIpv4Tunnel(const OVS_ARGUMENT_GROUP* pTunnelGroup, OVS_ARGUMENT** ppIpv4TunnelArg)
{
    BOOLEAN ok = FALSE;
    OVS_ARGUMENT* pIpv4Tunnel = NULL;
    OF_PI_IPV4_TUNNEL* pTunnelInfo = NULL;

    pTunnelInfo = KZAlloc(sizeof(OF_PI_IPV4_TUNNEL));
    if (!pTunnelInfo)
    {
        return FALSE;
    }

    RtlZeroMemory(pTunnelInfo, sizeof(OF_PI_IPV4_TUNNEL));

    ok = _SetAction_CreateIpv4TunnelFromArg(pTunnelGroup, pTunnelInfo);
    if (!ok)
    {
        KFree(pTunnelInfo);
        return FALSE;
    }

    pIpv4Tunnel = CreateArgument(OVS_ARGTYPE_PI_IPV4_TUNNEL, pTunnelInfo);
    if (!pIpv4Tunnel)
    {
        return FALSE;
    }

    *ppIpv4TunnelArg = pIpv4Tunnel;

    return ok;
}

static BOOLEAN _Action_SetInfo(_Inout_ OVS_ARGUMENT_GROUP* pActionGroup, const OVS_OFPACKET_INFO* pPacketInfo)
{
    OVS_ARGUMENT* pPacketInfoArg = NULL;;
    OVS_ARGTYPE argType = OVS_ARGTYPE_INVALID;
    OVS_ARGUMENT* pTunnelArg = NULL;
    OF_PI_IPV4_TUNNEL* pTunnel;
    const BYTE* pMac = NULL;
    const OVS_PI_IPV4* pIpv4Info = NULL;
    const OVS_PI_IPV6* pIpv6Info = NULL;
    BOOLEAN ok = FALSE;

    if (pActionGroup->count != 1)
    {
        DEBUGP(LOG_ERROR, __FUNCTION__ " pActionGroup->count != 1\n");
        return FALSE;
    }

    pPacketInfoArg = pActionGroup->args;
    argType = pPacketInfoArg->type;

    switch (argType)
    {
    case OVS_ARGTYPE_PI_PACKET_PRIORITY:
    case OVS_ARGTYPE_PI_PACKET_MARK:
    case OVS_ARGTYPE_PI_ETH_ADDRESS:
    case OVS_ARGTYPE_PI_DATAPATH_HASH:
    case OVS_ARGTYPE_PI_DATAPATH_RECIRCULATION_ID:
        //nothing to do here
        break;

    case OVS_ARGTYPE_PI_TUNNEL_GROUP:
        ok = _CreateActionIpv4Tunnel(pPacketInfoArg->data, &pTunnelArg);
        DestroyArgument(pPacketInfoArg);
        pActionGroup->args = pTunnelArg;

        if (!ok)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " _CreateActionIpv4Tunnel failed\n");
            return FALSE;
        }

        pTunnel = pTunnelArg->data;
        pMac = Arp_FindTableEntry((const BYTE*)&pTunnel->ipv4Destination);
        if (!pMac)
        {
            ONB_OriginateArpRequest((const BYTE*)&pTunnel->ipv4Destination);
        }

        break;

    case OVS_ARGTYPE_PI_IPV4:
        if (pPacketInfo->ethInfo.type != RtlUshortByteSwap(OVS_ETHERTYPE_IPV4))
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's eth type != ipv4\n");
            return FALSE;
        }

        if (!pPacketInfo->ipInfo.protocol)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's proto == NULL\n");
            return FALSE;
        }

        pIpv4Info = pPacketInfoArg->data;
        if (pIpv4Info->protocol != pPacketInfo->ipInfo.protocol)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " ipv4 packet info's proto != packet info's proto\n");
            return FALSE;
        }

        if (pIpv4Info->fragmentType != pPacketInfo->ipInfo.fragment)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " ipv4 packet info's frag type != packet info's frag type\n");
            return FALSE;
        }

        break;

    case OVS_ARGTYPE_PI_IPV6:
        if (pPacketInfo->ethInfo.type != RtlUshortByteSwap(OVS_ETHERTYPE_IPV6))
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's eth type != ipv6\n");
            return FALSE;
        }

        if (!pPacketInfo->ipInfo.protocol)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's proto == NULL\n");
            return FALSE;
        }

        pIpv6Info = pPacketInfoArg->data;
        if (pIpv6Info->protocol != pPacketInfo->ipInfo.protocol)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " ipv4 packet info's proto != packet info's proto\n");
            return FALSE;
        }

        if (pIpv6Info->fragmentType != pPacketInfo->ipInfo.fragment){
            DEBUGP(LOG_ERROR, __FUNCTION__ " ipv6 packet info's frag type != packet info's frag type\n");
            return FALSE;
        }

        //the flow label is 20 bits long, so the label in ipv6 PI must be max 0xFFFFF
        if (RtlUshortByteSwap(pIpv6Info->label) > 0x000FFFFF)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " ipv6 packet info's label invalid\n");
            return FALSE;
        }

        break;

    case OVS_ARGTYPE_PI_TCP:
        if (pPacketInfo->ipInfo.protocol != IPPROTO_TCP)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's proto != tcp\n");
            return FALSE;
        }

        return _ValidateTransportPort(pPacketInfo);

    case OVS_ARGTYPE_PI_TCP_FLAGS:
        if (pPacketInfo->ipInfo.protocol != IPPROTO_TCP)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's proto != tcp\n");
            return FALSE;
        }

        return TRUE;

    case OVS_ARGTYPE_PI_UDP:
        if (pPacketInfo->ipInfo.protocol != IPPROTO_UDP)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's proto != udp\n");
            return FALSE;
        }

        return _ValidateTransportPort(pPacketInfo);

    case OVS_ARGTYPE_PI_SCTP:
        if (pPacketInfo->ipInfo.protocol != IPPROTO_SCTP)
        {
            DEBUGP(LOG_ERROR, __FUNCTION__ " packet info's proto != sctp\n");
            return FALSE;
        }

        return _ValidateTransportPort(pPacketInfo);

    case OVS_ARGTYPE_PI_MPLS:
        if (RtlUshortByteSwap(pPacketInfo->ethInfo.type) != OVS_ETHERTYPE_MPLS_UNICAST ||
            RtlUshortByteSwap(pPacketInfo->ethInfo.type) != OVS_ETHERTYPE_MPLS_MULTICAST)
        {
            return FALSE;
        }

        return TRUE;

    default:
        DEBUGP(LOG_ERROR, __FUNCTION__ " invalid PI type to set: 0x%x\n", argType);
        return FALSE;
    }

    return TRUE;
}

BOOLEAN ProcessReceivedActions(_Inout_ OVS_ARGUMENT_GROUP* pActionGroup, const OVS_OFPACKET_INFO* pPacketInfo, int recursivityDepth)
{
    BOOLEAN ok = TRUE;

    if (recursivityDepth >= OVS_ACTION_SAMPLE_MAX_DEPTH)
    {
        return FALSE;
    }

    for (UINT i = 0; i < pActionGroup->count; ++i)
    {
        OVS_ARGUMENT* pArg = pActionGroup->args + i;
        OVS_ARGTYPE argType = pArg->type;

        switch (argType)
        {
        case OVS_ARGTYPE_INVALID:
            return FALSE;

        case OVS_ARGTYPE_ACTION_UPCALL_GROUP:
            ok = _VerifyAction_Upcall(pArg);
            if (!ok)
            {
                return FALSE;
            }
            break;

        case OVS_ARGTYPE_ACTION_SAMPLE_GROUP:
            OVS_CHECK_RET(__NOT_IMPLEMENTED__, FALSE);
            break;

        case OVS_ARGTYPE_ACTION_RECIRCULATION:
            break;

        case OVS_ARGTYPE_ACTION_HASH:
        {
            OVS_ACTION_FLOW_HASH* pHashAction = pArg->data;
            if (pHashAction->hashAlgorithm != OVS_HASH_ALGORITHM_TRANSPORT)
                return FALSE;
        }
            break;

        case OVS_ARGTYPE_ACTION_SETINFO_GROUP:
            OVS_CHECK(IsArgTypeGroup(pArg->type));
            ok = _Action_SetInfo(pArg->data, pPacketInfo);
            if (!ok)
            {
                return FALSE;
            }

            break;

        case OVS_ARGTYPE_ACTION_OUTPUT_TO_PORT:
        {
            UINT32 portNumber = GET_ARG_DATA(pArg, UINT32);

            if (portNumber >= OVS_MAX_PORTS)
            {
                return FALSE;
            }
        }
            break;

        case OVS_ARGTYPE_ACTION_PUSH_VLAN:
        {
            const OVS_ACTION_PUSH_VLAN* pPushVlanAction;
            pPushVlanAction = pArg->data;
            if (pPushVlanAction->protocol != RtlUshortByteSwap(OVS_ETHERTYPE_QTAG))
            {
                return FALSE;
            }

            if (!(pPushVlanAction->vlanTci & RtlUshortByteSwap(OVS_VLAN_TAG_PRESENT)))
            {
                return FALSE;
            }
        }
            break;

        case OVS_ARGTYPE_ACTION_POP_VLAN:
            break;

        case OVS_ARGTYPE_ACTION_PUSH_MPLS:
            OVS_CHECK_RET(__NOT_IMPLEMENTED__, FALSE);
            break;

        case OVS_ARGTYPE_ACTION_POP_MPLS:
            OVS_CHECK_RET(__NOT_IMPLEMENTED__, FALSE);
            break;

        default:
            return FALSE;
        }
    }

    return TRUE;
}

VOID Actions_DestroyNow_Unsafe(_Inout_ OVS_ACTIONS* pActions)
{
    OVS_CHECK(pActions);

    DestroyArgumentGroup(pActions->pActionGroup);

    KFree(pActions);
}

OVS_ACTIONS* Actions_Create()
{
    OVS_ACTIONS* pActions = KZAlloc(sizeof(OVS_ACTIONS));

    if (!pActions)
    {
        return NULL;
    }

    pActions->pActionGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    if (!pActions->pActionGroup)
    {
        KFree(pActions);
        return NULL;
    }

    pActions->refCount.Destroy = Actions_DestroyNow_Unsafe;

    return pActions;
}


text:
/*!
 @file		ZanlungoConfig.h
 @brief		Configures the shared library aspect of the includes.
 */

#ifndef __ZANLUNGO_CONFIG_H__
#define __ZANLUNGO_CONFIG_H__

// Now set up compiler-dependent export/import symbols

#if defined(_MSC_VER)

#if defined(ZANLUNGO_STATICLIB)
#define ZANLUNGO_API
#else
#if defined(ZANLUNGO_EXPORT)
// We are building the DLL, export the symbols tagged like this
#define ZANLUNGO_API __declspec(dllexport)
#else
// If we are consuming the DLL, import the symbols tagged like this
#define ZANLUNGO_API __declspec(dllimport)
#endif
#endif

#elif defined(__GNUC__)

#if defined(ZANLUNGO_STATICLIB)
#define ZANLUNGO_API
#else
#if defined(ZANLUNGO_EXPORT)
#define ZANLUNGO_API __attribute__((visibility("default")))
#else
// If you use -fvisibility=hidden in GCC, exception handling and RTTI
// would break if visibility wasn't set during export _and_ import
// because GCC would immediately forget all type infos encountered.
// See http://gcc.gnu.org/wiki/Visibility
#define ZANLUNGO_API __attribute__((visibility("default")))
#endif
#endif

#else

#error Unknown compiler, please implement shared library macros

#endif  // Compiler-detection

#endif  //__ZANLUNGO_CONFIG_H__

redpajama_set_name: RedPajamaGithub


text:
module BootstrapMultiselect
  module Rails
    VERSION = '0.9.15'
  end
end


text:
{% extends 'layouts/default.html' %}
{% block content %}

NYCitizenry

Know Your Representatives!

          <form class="col-lg-12">

            <input class="form-control input-md" title="Don't worry. Your address data won't be stored anywhere in our database" placeholder="Enter your address" type="text" id="address" value="Geocode" >

                Get My Address

          </form>

                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>

Is this your address?

                        <button class="btn btn-success" id="nytDistricts">Yes, it is!</button>
                        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>

{% endblock %}

{% block bottomscript %}

{% endblock %}


text:
local helpers = require "spec.helpers" -- initializes 'kong' global for vaults
local conf_loader = require "kong.conf_loader"

local is_ref_test_map = {
  ["{vault://x/x}"] = true,
  ["{vault://x/xx}"] = true,
  ["{vault://xx/xx-x}"] = true,
  ["{vault://xxx/xx-x}"] = true,
  ["{vault://;/xx-x}"] = true,
  ["vault:/xx-x}"] = false,
  ["{vault:/xx-x"] = false,
  ["vault:/xx-x"] = false,
  ["{valut:/xx-x}"] = false,
  ["{vault:/xx-x}"] = false,
}

local get_test_map = {
  ["{vault://env/test_secrets}"] = {
    k = "TEST_SECRETS",
    v = "test_value"
  },
  ["{vault://env/test}"] = {
    k = "TEST",
    v = ""
  },
}

describe("Vault PDK", function()
  local vaults
  local is_reference
  local parse_reference
  local dereference
  local try

  before_each(function()
    local conf = assert(conf_loader(nil, {
      vaults = "bundled",
      plugins = "bundled",
    }))

    local kong_global = require "kong.global"
    _G.kong = kong_global.new()
    kong_global.init_pdk(kong, conf)

    is_reference = _G.kong.vault.is_reference
    parse_reference = _G.kong.vault.parse_reference
    dereference = _G.kong.vault.get
    try = _G.kong.vault.try

    vaults = {}

    for vault in pairs(conf.loaded_vaults) do
      local init = require("kong.vaults." .. vault)
      table.insert(vaults, init)
    end
  end)

  it("test init", function()
    local res, err = parse_reference("{vault://env/test}")
    assert.is_nil(err)
    assert.is_nil(res.config)
    assert.is_equal("env", res.name)
    assert.is_equal("test", res.resource)
    assert.is_nil(res.key)
    assert.is_nil(res.version)
  end)

  it("test init nested/path", function()
    local res, err = parse_reference("{vault://env/test-secret/test-key}")
    assert.is_nil(err)
    assert.is_nil(res.config)
    assert.is_equal("env", res.name)
    assert.is_equal("test-secret", res.resource)
    assert.is_equal("test-key", res.key)
    assert.is_nil(res.version)
  end)

  it("test init opts", function()
    local res, err = parse_reference("{vault://env/test?opt1=val1}")
    assert.is_nil(err)
    assert.is_same({ opt1 = "val1" }, res.config)
    assert.is_equal("env", res.name)
    assert.is_equal(res.resource, "test")
    assert.is_nil(res.key)
    assert.is_nil(res.version)
  end)

  it("test init multiple opts", function()
    local res, err = parse_reference("{vault://env/test?opt1=val1&opt2=val2}")
    assert.is_nil(err)
    assert.is_same({ opt1 = "val1", opt2 = "val2" }, res.config)
    assert.is_equal("env", res.name)
    assert.is_equal("test", res.resource)
    assert.is_nil(res.key)
    assert.is_nil(res.version)
  end)

  it("test init version", function()
    local res, err = parse_reference("{vault://env/test#1}")
    assert.is_nil(err)
    assert.is_nil(res.config)
    assert.is_equal("env", res.name)
    assert.is_equal("test", res.resource)
    assert.is_nil(res.key)
    assert.equal(1, res.version)
  end)

  it("ensure that every vault has a VERSION and a `get` field", function()
    for _, vault in ipairs(vaults) do
      assert.not_nil(vault.get)
      assert.not_nil(vault.VERSION)
    end
  end)

  for ref, exp in pairs(is_ref_test_map) do
    it("test is_reference [" .. ref .. "] -> " .. tostring(exp), function()
      assert.is_equal(exp, is_reference(ref))
    end)
  end

  for ref, cfg in pairs(get_test_map) do
    it("test get [" .. ref .. "] -> " .. tostring(cfg.k), function()
      finally(function()
        helpers.unsetenv(cfg.k)
      end)
      helpers.setenv(cfg.k, cfg.v)
      local ret, err = dereference(ref)
      assert.is_nil(err)
      assert.is_equal(cfg.v, ret)
    end)
  end

  describe("try function", function()
    local called

    before_each(function()
      called = 0
    end)

    it("calls the callback only once when successful", function()
      local ok, err = try(function()
        called = called + 1
        return true
      end)

      assert.is_nil(err)
      assert.True(ok)
      assert.equal(1, called)
    end)

    it("calls the callback only once when no options", function()
      local ok, err = try(function()
        called = called + 1
        return nil, "failed"
      end)

      assert.equal("failed", err)
      assert.is_nil(ok)
      assert.equal(1, called)
    end)

    it("calls the callback only once when no refs", function()
      local ok, err = try(function()
        called = called + 1
        return nil, "failed"
      end, {})

      assert.equal("failed", err)
      assert.is_nil(ok)
      assert.equal(1, called)
    end)

    it("calls the callback only once when refs is empty", function()
      local ok, err = try(function()
        called = called + 1
        return nil, "failed"
      end, {
        ["$refs"] = {}
      })

      assert.equal("failed", err)
      assert.is_nil(ok)
      assert.equal(1, called)
    end)

    it("calls the callback twice when current credentials doesn't work", function()
      finally(function()
        helpers.unsetenv("CREDENTIALS")
      end)

      helpers.setenv("CREDENTIALS", '{"username":"jane","password":"qwerty"}')

      local options = {
        username = "john",
        password = "secret",
        ["$refs"] = {
          username = "{vault://env/credentials/username}",
          password = "{vault://env/credentials/password}",
        },
      }

      local callback = function(options)
        called = called + 1
        if options.username ~= "jane" or options.password ~= "qwerty" then
          return nil, "failed"
        end
        return true
      end

      local ok, err = try(callback, options)

      assert.is_nil(err)
      assert.True(ok)
      assert.equal(2, called)

      assert.equal("jane", options.username)
      assert.equal("qwerty", options.password)
      assert.equal("{vault://env/credentials/username}", options["$refs"].username)
      assert.equal("{vault://env/credentials/password}", options["$refs"].password)

      -- has a cache that can be used for rate-limiting

      called = 0
      options = {
        username = "john",
        password = "secret",
        ["$refs"] = {
          username = "{vault://env/credentials/username}",
          password = "{vault://env/credentials/password}",
        },
      }

      helpers.unsetenv("CREDENTIALS")

      -- re-initialize env vault to clear cached values

      local env = require "kong.vaults.env"
      env.init()

      -- if we slept for 10 secs here, the below would fail as rate-limiting
      -- cache would have been cleared

      local ok, err = try(callback, options)

      assert.is_nil(err)
      assert.True(ok)
      assert.equal(2, called)

      assert.equal("jane", options.username)
      assert.equal("qwerty", options.password)
      assert.equal("{vault://env/credentials/username}", options["$refs"].username)
      assert.equal("{vault://env/credentials/password}", options["$refs"].password)
    end)
  end)
end)


text:
namespace impala {

class TupleRow;
class RowDescriptor;
class MemTracker;

/// Simple cache of row batches.
class RowBatchCache {
 public:
  RowBatchCache(const RowDescriptor* row_desc, int batch_size)
    : row_desc_(row_desc), batch_size_(batch_size), next_row_batch_idx_(0) {}

  ~RowBatchCache() { DCHECK_EQ(0, row_batches_.size()); }

  /// Returns the next batch from the cache. Expands the cache if necessary.
  /// If a new batch is created, its memory is tracked against 'mem_tracker'.
  RowBatch* GetNextBatch(MemTracker* mem_tracker) {
    if (next_row_batch_idx_ >= row_batches_.size()) {
      // Expand the cache with a new row batch.
      row_batches_.push_back(
          std::make_unique<RowBatch>(row_desc_, batch_size_, mem_tracker));
    } else {
      // Reset batch from the cache before returning it.
      row_batches_[next_row_batch_idx_]->Reset();
    }
    return row_batches_[next_row_batch_idx_++].get();
  }

  /// Resets the cache such that subsequent calls to GetNextBatch() return batches from
  /// the beginning of the cache. All batches previously returned by GetNextBatch()
  /// are invalid.
  void Reset() { next_row_batch_idx_ = 0; }

  /// Delete and free resources associated with all cached batch objects. Must be called
  /// before the RowBatchCache is destroyed.
  void Clear() {
    row_batches_.clear(); // unique_ptr automatically calls all destructors.
  }

  /// Return the last batch returned from GetNextBatch() since Reset(), or NULL if
  /// GetNextBatch() has not yet been called.
  RowBatch* GetLastBatchReturned() {
    return next_row_batch_idx_ == 0 ? NULL : row_batches_[next_row_batch_idx_ - 1].get();
  }

 private:
  /// Parameters needed for creating row batches.
  const RowDescriptor* row_desc_;
  int batch_size_;

  /// List of cached row-batch objects. The row-batch objects are owned by this cache.
  std::vector<std::unique_ptr<RowBatch>> row_batches_;

  /// Index of next row batch to return in GetRowBatch().
  int next_row_batch_idx_;
};

}

#endif


text:
using blink::mojom::PresentationConnectionCloseReason;
using blink::mojom::PresentationConnectionState;
using media_router::mojom::RouteMessagePtr;
using testing::_;
using testing::AtMost;
using testing::Eq;
using testing::Invoke;
using testing::InvokeWithoutArgs;
using testing::IsEmpty;
using testing::Mock;
using testing::NiceMock;
using testing::Not;
using testing::Pointee;
using testing::Return;
using testing::ReturnRef;
using testing::SaveArg;
using testing::Sequence;
using testing::SizeIs;
using testing::StrictMock;
using testing::UnorderedElementsAre;
using testing::Unused;
using testing::WithArg;

namespace media_router {

namespace {

const char kDescription[] = "description";
const char kError[] = "error";
const char kSource[] = "source1";
const char kSource2[] = "source2";
const char kTabSourceOne[] = "urn:x-org.chromium.media:source:tab:1";
const char kTabSourceTwo[] = "urn:x-org.chromium.media:source:tab:2";
const char kRouteId[] = "routeId";
const char kRouteId2[] = "routeId2";
const char kSinkId[] = "sink";
const char kSinkId2[] = "sink2";
const char kSinkName[] = "sinkName";
const char kPresentationId[] = "presentationId";
const char kOrigin[] = "http://origin/";
const int kInvalidFrameNodeId = -1;
const int kTimeoutMillis = 5 * 1000;

IssueInfo CreateIssueInfo(const std::string& title) {
  IssueInfo issue_info;
  issue_info.title = title;
  issue_info.message = std::string("msg");
  issue_info.default_action = IssueInfo::Action::DISMISS;
  issue_info.severity = IssueInfo::Severity::WARNING;
  return issue_info;
}

// Creates a media route whose ID is |kRouteId|.
MediaRoute CreateMediaRoute() {
  MediaRoute route(kRouteId, MediaSource(kSource), kSinkId, kDescription, true);
  route.set_presentation_id(kPresentationId);
  route.set_controller_type(RouteControllerType::kGeneric);
  return route;
}

// Creates a media route whose ID is |kRouteId2|.
MediaRoute CreateMediaRoute2() {
  MediaRoute route(kRouteId2, MediaSource(kSource), kSinkId, kDescription,
                   true);
  route.set_presentation_id(kPresentationId);
  route.set_controller_type(RouteControllerType::kGeneric);
  return route;
}

std::string RouteMessageToString(const RouteMessagePtr& message) {
  if (!message->message && !message->data)
    return "null";
  std::string result;
  if (message->message) {
    result = "text=";
    base::EscapeJSONString(message->message.value(), true, &result);
  } else {
    const base::StringPiece src(
        reinterpret_cast<const char*>(message->data.value().data()),
        message->data.value().size());
    base::Base64Encode(src, &result);
    result = "binary=" + result;
  }
  return result;
}

std::vector<MediaSinkInternal> ToInternalSinks(
    const std::vector<MediaSink>& sinks) {
  std::vector<MediaSinkInternal> internal_sinks;
  internal_sinks.reserve(sinks.size());
  for (const auto& sink : sinks) {
    MediaSinkInternal internal_sink;
    internal_sink.set_sink(sink);
    internal_sinks.emplace_back(std::move(internal_sink));
  }
  return internal_sinks;
}

class StubMediaRouterMojoImpl : public MediaRouterMojoImpl {
 public:
  explicit StubMediaRouterMojoImpl(content::BrowserContext* context)
      : MediaRouterMojoImpl(context) {}
  ~StubMediaRouterMojoImpl() override = default;

  // media_router::MediaRouter:
  base::Value GetState() const override {
    NOTIMPLEMENTED();
    return base::Value();
  }

  void GetProviderState(
      mojom::MediaRouteProviderId provider_id,
      mojom::MediaRouteProvider::GetStateCallback callback) const override {
    NOTIMPLEMENTED();
  }
};

}  // namespace

class MediaRouterMojoImplTest : public MediaRouterMojoTest {
 public:
  MediaRouterMojoImplTest() = default;
  ~MediaRouterMojoImplTest() override = default;

 protected:
  void ExpectCastResultBucketCount(const std::string& operation,
                                   mojom::RouteRequestResultCode result_code,
                                   int expected_count) {
    ExpectBucketCount("MediaRouter.Provider." + operation + ".Result.Cast",
                      result_code, expected_count);
  }

  void ExpectResultBucketCount(const std::string& operation,
                               mojom::RouteRequestResultCode result_code,
                               int expected_count) {
    ExpectBucketCount("MediaRouter.Provider." + operation + ".Result",
                      result_code, expected_count);
  }

  std::unique_ptr<MediaRouterMojoImpl> CreateMediaRouter() override {
    return std::unique_ptr<MediaRouterMojoImpl>(
        new StubMediaRouterMojoImpl(profile()));
  }

  void ReceiveSinks(mojom::MediaRouteProviderId provider_id,
                    const std::string& media_source,
                    const std::vector<MediaSinkInternal>& sinks) {
    router()->OnSinksReceived(
        provider_id, media_source, sinks,
        std::vector<url::Origin>{1, url::Origin::Create(GURL(kOrigin))});
  }

  void ReceiveRouteMessages(const std::string& route_id,
                            std::vector<mojom::RouteMessagePtr> messages) {
    router()->OnRouteMessagesReceived(route_id, std::move(messages));
  }

  void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) {
    router()->RegisterMediaRoutesObserver(observer);
  }

  void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) {
    router()->UnregisterMediaRoutesObserver(observer);
  }

  void UpdateRoutes(mojom::MediaRouteProviderId provider_id,
                    const std::vector<MediaRoute>& routes) {
    router()->OnRoutesUpdated(provider_id, routes);
  }

  void RecordPresentationRequestUrlBySink(
      const media_router::MediaSource& source,
      media_router::mojom::MediaRouteProviderId provider_id,
      int times) {
    for (int i = 0; i < times; i++) {
      MediaRouterMojoImpl::RecordPresentationRequestUrlBySink(source,
                                                              provider_id);
    }
  }

 private:
  void ExpectBucketCount(const std::string& histogram_name,
                         mojom::RouteRequestResultCode result_code,
                         int expected_count) {
    histogram_tester_.ExpectBucketCount(histogram_name, result_code,
                                        expected_count);
  }

  base::HistogramTester histogram_tester_;
};

TEST_F(MediaRouterMojoImplTest, CreateRoute) {
  TestCreateRoute();
  ExpectCastResultBucketCount("CreateRoute", mojom::RouteRequestResultCode::OK,
                              1);
}

// Tests that MediaRouter is aware when a route is created, even if
// MediaRouteProvider doesn't call OnRoutesUpdated().
TEST_F(MediaRouterMojoImplTest, RouteRecognizedAfterCreation) {
  MockMediaRoutesObserver routes_observer(router());

  EXPECT_CALL(routes_observer, OnRoutesUpdated(SizeIs(0)));
  EXPECT_CALL(routes_observer, OnRoutesUpdated(SizeIs(1)));

  // TestCreateRoute() does not explicitly call OnRoutesUpdated() on the router.
  TestCreateRoute();
  base::RunLoop().RunUntilIdle();
}

TEST_F(MediaRouterMojoImplTest, CreateIncognitoRoute) {
  ProvideTestSink(mojom::MediaRouteProviderId::CAST, kSinkId);
  MediaSource media_source(kSource);
  MediaRoute expected_route(kRouteId, media_source, kSinkId, "", false);
  expected_route.set_off_the_record(true);

  // Use a lambda function as an invocation target here to work around
  // a limitation with GMock::Invoke that prevents it from using move-only types
  // in runnable parameter lists.
  EXPECT_CALL(mock_cast_provider_,
              CreateRouteInternal(kSource, kSinkId, _,
                                  url::Origin::Create(GURL(kOrigin)),
                                  kInvalidFrameNodeId, _, _, _))
      .WillOnce(WithArg<7>(
          Invoke([&expected_route](
                     mojom::MediaRouteProvider::CreateRouteCallback& cb) {
            std::move(cb).Run(expected_route, nullptr, std::string(),
                              mojom::RouteRequestResultCode::OK);
          })));

  base::RunLoop run_loop;
  RouteResponseCallbackHandler handler;
  EXPECT_CALL(handler, DoInvoke(Pointee(expected_route), Not(""), "",
                                mojom::RouteRequestResultCode::OK, _))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  router()->CreateRoute(kSource, kSinkId, url::Origin::Create(GURL(kOrigin)),
                        nullptr,
                        base::BindOnce(&RouteResponseCallbackHandler::Invoke,
                                       base::Unretained(&handler)),
                        base::Milliseconds(kTimeoutMillis), true);
  run_loop.Run();
  ExpectCastResultBucketCount("CreateRoute", mojom::RouteRequestResultCode::OK,
                              1);
}

TEST_F(MediaRouterMojoImplTest, CreateRouteFails) {
  ProvideTestSink(mojom::MediaRouteProviderId::CAST, kSinkId);
  EXPECT_CALL(mock_cast_provider_,
              CreateRouteInternal(kSource, kSinkId, _,
                                  url::Origin::Create(GURL(kOrigin)),
                                  kInvalidFrameNodeId, _, _, _))
      .WillOnce(WithArg<7>(
          Invoke([](mojom::MediaRouteProvider::CreateRouteCallback& cb) {
            std::move(cb).Run(absl::nullopt, nullptr, std::string(kError),
                              mojom::RouteRequestResultCode::TIMED_OUT);
          })));

  RouteResponseCallbackHandler handler;
  base::RunLoop run_loop;
  EXPECT_CALL(handler, DoInvoke(nullptr, "", kError,
                                mojom::RouteRequestResultCode::TIMED_OUT, _))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  router()->CreateRoute(kSource, kSinkId, url::Origin::Create(GURL(kOrigin)),
                        nullptr,
                        base::BindOnce(&RouteResponseCallbackHandler::Invoke,
                                       base::Unretained(&handler)),
                        base::Milliseconds(kTimeoutMillis), false);
  run_loop.Run();
  ExpectCastResultBucketCount("CreateRoute",
                              mojom::RouteRequestResultCode::TIMED_OUT, 1);
}

TEST_F(MediaRouterMojoImplTest, CreateRouteIncognitoMismatchFails) {
  ProvideTestSink(mojom::MediaRouteProviderId::CAST, kSinkId);
  EXPECT_CALL(mock_cast_provider_,
              CreateRouteInternal(kSource, kSinkId, _,
                                  url::Origin::Create(GURL(kOrigin)),
                                  kInvalidFrameNodeId, _, _, _))
      .WillOnce(WithArg<7>(
          Invoke([](mojom::MediaRouteProvider::CreateRouteCallback& cb) {
            std::move(cb).Run(CreateMediaRoute(), nullptr, std::string(),
                              mojom::RouteRequestResultCode::OK);
          })));

  RouteResponseCallbackHandler handler;
  base::RunLoop run_loop;
  std::string error(
      "Mismatch in OffTheRecord status: request = 1, response = 0");
  EXPECT_CALL(handler,
              DoInvoke(nullptr, "", error,
                       mojom::RouteRequestResultCode::ROUTE_NOT_FOUND, _))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  router()->CreateRoute(kSource, kSinkId, url::Origin::Create(GURL(kOrigin)),
                        nullptr,
                        base::BindOnce(&RouteResponseCallbackHandler::Invoke,
                                       base::Unretained(&handler)),
                        base::Milliseconds(kTimeoutMillis), true);
  run_loop.Run();
  ExpectCastResultBucketCount(
      "CreateRoute", mojom::RouteRequestResultCode::ROUTE_NOT_FOUND, 1);
}

TEST_F(MediaRouterMojoImplTest, JoinRoute) {
  TestJoinRoute(kPresentationId);
  ExpectCastResultBucketCount("JoinRoute", mojom::RouteRequestResultCode::OK,
                              1);
}

TEST_F(MediaRouterMojoImplTest, JoinRouteNotFoundFails) {
  RouteResponseCallbackHandler handler;
  base::RunLoop run_loop;
  EXPECT_CALL(handler,
              DoInvoke(nullptr, "", "Route not found",
                       mojom::RouteRequestResultCode::ROUTE_NOT_FOUND, _))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  router()->JoinRoute(kSource, kPresentationId,
                      url::Origin::Create(GURL(kOrigin)), nullptr,
                      base::BindOnce(&RouteResponseCallbackHandler::Invoke,
                                     base::Unretained(&handler)),
                      base::Milliseconds(kTimeoutMillis), false);
  run_loop.Run();
  ExpectResultBucketCount("JoinRoute",
                          mojom::RouteRequestResultCode::ROUTE_NOT_FOUND, 1);
}

TEST_F(MediaRouterMojoImplTest, JoinRouteTimedOutFails) {
  // Make sure the MR has received an update with the route, so it knows there
  // is a route to join.
  const std::vector<MediaRoute> routes{CreateMediaRoute()};
  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes);
  EXPECT_TRUE(router()->HasJoinableRoute());

  EXPECT_CALL(
      mock_cast_provider_,
      JoinRouteInternal(kSource, kPresentationId,
                        url::Origin::Create(GURL(kOrigin)), kInvalidFrameNodeId,
                        base::Milliseconds(kTimeoutMillis), _, _))
      .WillOnce(WithArg<6>(
          Invoke([](mojom::MediaRouteProvider::JoinRouteCallback& cb) {
            std::move(cb).Run(absl::nullopt, nullptr, std::string(kError),
                              mojom::RouteRequestResultCode::TIMED_OUT);
          })));

  RouteResponseCallbackHandler handler;
  base::RunLoop run_loop;
  EXPECT_CALL(handler, DoInvoke(nullptr, "", kError,
                                mojom::RouteRequestResultCode::TIMED_OUT, _))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  router()->JoinRoute(kSource, kPresentationId,
                      url::Origin::Create(GURL(kOrigin)), nullptr,
                      base::BindOnce(&RouteResponseCallbackHandler::Invoke,
                                     base::Unretained(&handler)),
                      base::Milliseconds(kTimeoutMillis), false);
  run_loop.Run();
  ExpectCastResultBucketCount("JoinRoute",
                              mojom::RouteRequestResultCode::TIMED_OUT, 1);
}

TEST_F(MediaRouterMojoImplTest, JoinRouteIncognitoMismatchFails) {
  const MediaRoute route = CreateMediaRoute();

  // Make sure the MR has received an update with the route, so it knows there
  // is a route to join.
  const std::vector<MediaRoute> routes{route};
  UpdateRoutes(mojom::MediaRouteProviderId::CAST, routes);
  EXPECT_TRUE(router()->HasJoinableRoute());

  // Use a lambda function as an invocation target here to work around
  // a limitation with GMock::Invoke that prevents it from using move-only types
  // in runnable parameter lists.
  EXPECT_CALL(
      mock_cast_provider_,
      JoinRouteInternal(kSource, kPresentationId,
                        url::Origin::Create(GURL(kOrigin)), kInvalidFrameNodeId,
                        base::Milliseconds(kTimeoutMillis), _, _))
      .WillOnce(WithArg<6>(
          Invoke([&route](mojom::MediaRouteProvider::JoinRouteCallback& cb) {
            std::move(cb).Run(route, nullptr, std::string(),
                              mojom::RouteRequestResultCode::OK);
          })));

  RouteResponseCallbackHandler handler;
  base::RunLoop run_loop;
  std::string error(
      "Mismatch in OffTheRecord status: request = 1, response = 0");
  EXPECT_CALL(handler,
              DoInvoke(nullptr, "", error,
                       mojom::RouteRequestResultCode::ROUTE_NOT_FOUND, _))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
  router()->JoinRoute(kSource, kPresentationId,
                      url::Origin::Create(GURL(kOrigin)), nullptr,
                      base::BindOnce(&RouteResponseCallbackHandler::Invoke,
                                     base::Unretained(&handler)),
                      base::Milliseconds(kTimeoutMillis), true);
  run_loop.Run();
  ExpectCastResultBucketCount(
      "JoinRoute", mojom::RouteRequestResultCode::ROUTE_NOT_FOUND, 1);
}

TEST_F(MediaRouterMojoImplTest, DetachRoute) {
  TestDetachRoute();
}

TEST_F(MediaRouterMojoImplTest, TerminateRoute) {
  TestTerminateRoute();
  ExpectCastResultBucketCount("TerminateRoute",
                              mojom::RouteRequestResultCode::OK, 1);
}

TEST_F(MediaRouterMojoImplTest, TerminateRouteFails) {
  ProvideTestRoute(mojom::MediaRouteProviderId::CAST, kRouteId);
  EXPECT_CALL(mock_cast_provider_, TerminateRouteInternal(kRouteId, _))
      .WillOnce(
          Invoke([](const std::string& route_id,
                    mojom::MediaRouteProvider::TerminateRouteCallback& cb) {
            std::move(cb).Run(std::string("timed out"),
                              mojom::RouteRequestResultCode::TIMED_OUT);
          }));
  router()->TerminateRoute(kRouteId);
  base::RunLoop().RunUntilIdle();
  ExpectCastResultBucketCount("TerminateRoute",
                              mojom::RouteRequestResultCode::OK, 0);
  ExpectCastResultBucketCount("TerminateRoute",
                              mojom::RouteRequestResultCode::TIMED_OUT, 1);
}

#if !BUILDFLAG(IS_ANDROID)
TEST_F(MediaRouterMojoImplTest, HandleIssue) {
  MockIssuesObserver issue_observer1(router()->GetIssueManager());
  MockIssuesObserver issue_observer2(router()->GetIssueManager());
  issue_observer1.Init();
  issue_observer2.Init();

  IssueInfo issue_info = CreateIssueInfo("title 1");

  Issue issue_from_observer1((IssueInfo()));
  Issue issue_from_observer2((IssueInfo()));
  EXPECT_CALL(issue_observer1, OnIssue(_))
      .WillOnce(SaveArg<0>(&issue_from_observer1));
  EXPECT_CALL(issue_observer2, OnIssue(_))
      .WillOnce(SaveArg<0>(&issue_from_observer2));

  router()->OnIssue(issue_info);
  base::RunLoop().RunUntilIdle();

  ASSERT_EQ(issue_from_observer1.id(), issue_from_observer2.id());
  EXPECT_EQ(issue_info, issue_from_observer1.info());
  EXPECT_EQ(issue_info, issue_from_observer2.info());
}
#endif  // !BUILDFLAG(IS_ANDROID)

TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) {
  MediaSource media_source(kSource);

  // These should only be called once even if there is more than one observer
  // for a given source.
  EXPECT_CALL(mock_cast_provider_, StartObservingMediaSinks(kSource));
  EXPECT_CALL(mock_cast_provider_, StartObservingMediaSinks(kSource2));

  auto sinks_observer = std::make_unique<NiceMock<MockMediaSinksObserver>>(
      router(), media_source, url::Origin::Create(GURL(kOrigin)));
  EXPECT_TRUE(sinks_observer->Init());
  auto extra_sinks_observer =
      std::make_unique<NiceMock<MockMediaSinksObserver>>(
          router(), media_source, url::Origin::Create(GURL(kOrigin)));
  EXPECT_TRUE(extra_sinks_observer->Init());
  auto unrelated_sinks_observer =
      std::make_unique<NiceMock<MockMediaSinksObserver>>(
          router(), MediaSource(kSource2), url::Origin::Create(GURL(kOrigin)));
  EXPECT_TRUE(unrelated_sinks_observer->Init());
  base::RunLoop().RunUntilIdle();

  const std::vector<MediaSink> kExpectedSinks{
      CreateCastSink(kSinkId, kSinkName), CreateCastSink(kSinkId2, kSinkName)};

  EXPECT_CALL(*sinks_observer, OnSinksReceived(kExpectedSinks));
  EXPECT_CALL(*extra_sinks_observer, OnSinksReceived(kExpectedSinks));
  ReceiveSinks(mojom::MediaRouteProviderId::CAST, media_source.id(),
               ToInternalSinks(kExpectedSinks));

  // Since the MediaRouterMojoImpl has already received results for
  // |media_source|, return cached results to observers that are subsequently
  // registered.
  auto cached_sinks_observer = std::make_unique<MockMediaSinksObserver>(
      router(), media_source, url::Origin::Create(GURL(kOrigin)));
  EXPECT_CALL(*cached_sinks_observer, OnSinksReceived(kExpectedSinks));
  EXPECT_TRUE(cached_sinks_observer->Init());

  // Different origin from cached result. Empty list will be returned.
  auto cached_sinks_observer2 = std::make_unique<MockMediaSinksObserver>(
      router(), media_source, url::Origin::Create(GURL("https://youtube.com")));
  EXPECT_CALL(*cached_sinks_observer2, OnSinksReceived(IsEmpty()));
  EXPECT_TRUE(cached_sinks_observer2->Init());

  EXPECT_CALL(mock_cast_provider_, StopObservingMediaSinks(kSource));
  EXPECT_CALL(mock_cast_provider_, StopObservingMediaSinks(kSource2));
  sinks_observer.reset();
  extra_sinks_observer.reset();
  unrelated_sinks_observer.reset();
  cached_sinks_observer.reset();
  cached_sinks_observer2.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(MediaRouterMojoImplTest, TabSinksObserverIsShared) {
  MediaSource tab_source_one(kTabSourceOne);
  MediaSource tab_source_two(kTabSourceTwo);

  // Media router should not try to start observing for each tab, but instead
  // only once for all tabs.
  EXPECT_CALL(mock_cast_provider_, StartObservingMediaSinks(kTabSourceOne))
      .Times(0);
  EXPECT_CALL(mock_cast_provider_, StartObservingMediaSinks(kTabSourceTwo))
      .Times(0);
  EXPECT_CALL(mock_cast_provider_,
              StartObservingMediaSinks(MediaSource::ForAnyTab().id()))
      .Times(1);

  const auto origin = url::Origin::Create(GURL(kOrigin));
  auto sinks_observer = std::make_unique<NiceMock<MockMediaSinksObserver>>(
      router(), tab_source_one, origin);
  EXPECT_TRUE(sinks_observer->Init());
  auto extra_sinks_observer =
      std::make_unique<NiceMock<MockMediaSinksObserver>>(
          router(), tab_source_one, origin);
  EXPECT_TRUE(extra_sinks_observer->Init());
  auto second_tab_sinks_observer =
      std::make_unique<NiceMock<MockMediaSinksObserver>>(
          router(), MediaSource(kTabSourceTwo), origin);
  EXPECT_TRUE(second_tab_sinks_observer->Init());
  base::RunLoop().RunUntilIdle();

  const std::vector<MediaSink> kExpectedSinks{
      CreateCastSink(kSinkId, kSinkName), CreateCastSink(kSinkId2, kSinkName)};

  // All tabs should get the same updates.
  EXPECT_CALL(*sinks_observer, OnSinksReceived(kExpectedSinks));
  EXPECT_CALL(*extra_sinks_observer, OnSinksReceived(kExpectedSinks));
  EXPECT_CALL(*second_tab_sinks_observer, OnSinksReceived(kExpectedSinks));
  ReceiveSinks(mojom::MediaRouteProviderId::CAST, tab_source_one.id(),
               ToInternalSinks(kExpectedSinks));

  // Since the MediaRouterMojoImpl has already received results for
  // |media_source|, return cached results to observers that are subsequently
  // registered.
  auto cached_sinks_observer = std::make_unique<MockMediaSinksObserver>(
      router(), tab_source_one, url::Origin::Create(GURL(kOrigin)));
  EXPECT_CALL(*cached_sinks_observer, OnSinksReceived(kExpectedSinks));
  EXPECT_TRUE(cached_sinks_observer->Init());

  // Different origin from cached result. Empty list will be returned.
  auto cached_sinks_observer2 = std::make_unique<MockMediaSinksObserver>(
      router(), tab_source_one,
      url::Origin::Create(GURL("https://youtube.com")));
  EXPECT_CALL(*cached_sinks_observer2, OnSinksReceived(IsEmpty()));
  EXPECT_TRUE(cached_sinks_observer2->Init());

  // Since tabs share observation, stop observing should not be called.
  EXPECT_CALL(mock_cast_provider_, StopObservingMediaSinks(kTabSourceOne))
      .Times(0);
  EXPECT_CALL(mock_cast_provider_, StopObservingMediaSinks(kTabSourceTwo))
      .Times(0);
  EXPECT_CALL(mock_cast_provider_,
              StopObservingMediaSinks(MediaSource::ForAnyTab().id()))
      .Times(0);
  sinks_observer.reset();
  extra_sinks_observer.reset();
  second_tab_sinks_observer.reset();
  cached_sinks_observer.reset();
  cached_sinks_observer2.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) {
  MockMediaRouter mock_router;

  MediaRoutesObserver* observer_captured;
  EXPECT_CALL(mock_router, RegisterMediaRoutesObserver(_))
      .Times(3)
      .WillRepeatedly(SaveArg<0>(&observer_captured));
  MockMediaRoutesObserver routes_observer(&mock_router);
  EXPECT_EQ(observer_captured, &routes_observer);

  MockMediaRoutesObserver extra_routes_observer(&mock_router);
  EXPECT_EQ(observer_captured, &extra_routes_observer);

  MockMediaRoutesObserver different_routes_observer(&mock_router);
  EXPECT_EQ(observer_captured, &different_routes_observer);

  EXPECT_CALL(mock_router, UnregisterMediaRoutesObserver(&routes_observer));
  EXPECT_CALL(mock_router,
              UnregisterMediaRoutesObserver(&extra_routes_observer));
  EXPECT_CALL(mock_router,
              UnregisterMediaRoutesObserver(&different_routes_observer));
  UnregisterMediaRoutesObserver(&routes_observer);
  UnregisterMediaRoutesObserver(&extra_routes_observer);
  UnregisterMediaRoutesObserver(&different_routes_observer);
}

TEST_F(MediaRouterMojoImplTest, RegisteredObserversGetMediaRouteUpdates) {
  StrictMock<MockMediaRoutesObserver> routes_observer(router());
  StrictMock<MockMediaRoutesObserver> extra_routes_observer(router());
  StrictMock<MockMediaRoutesObserver> different_routes_observer(router());

  MediaSource media_source(kSource);
  std::vector<MediaRoute> expected_routes{
      MediaRoute(kRouteId, media_source, kSinkId, kDescription, false)};

  MediaRoute incognito_expected_route(kRouteId2, media_source, kSinkId,
                                      kDescription, false);
  incognito_expected_route.set_off_the_record(true);
  expected_routes.push_back(incognito_expected_route);

  EXPECT_CALL(routes_observer, OnRoutesUpdated(expected_routes)).Times(1);
  EXPECT_CALL(extra_routes_observer, OnRoutesUpdated(expected_routes)).Times(1);
  EXPECT_CALL(different_routes_observer, OnRoutesUpdated(expected_routes))
      .Times(1);

  UpdateRoutes(mojom::MediaRouteProviderId::CAST, expected_routes);

  UnregisterMediaRoutesObserver(&routes_observer);
  UnregisterMediaRoutesObserver(&extra_routes_observer);
  UnregisterMediaRoutesObserver(&different_routes_observer);

  // No route observers should be notified.
  UpdateRoutes(mojom::MediaRouteProviderId::CAST, expected_routes);
}

TEST_F(MediaRouterMojoImplTest, SendRouteMessage) {
  TestSendRouteMessage();
}

TEST_F(MediaRouterMojoImplTest, SendRouteBinaryMessage) {
  TestSendRouteBinaryMessage();
}

namespace {

// Used in the RouteMessages* tests to populate the messages that will be
// processed and dispatched to RouteMessageObservers.
void PopulateRouteMessages(std::vector<RouteMessagePtr>* batch1,
                           std::vector<RouteMessagePtr>* batch2,
                           std::vector<RouteMessagePtr>* batch3,
                           std::vector<RouteMessagePtr>* all_messages) {
  batch1->clear();
  batch2->clear();
  batch3->clear();
  batch1->emplace_back(message_util::RouteMessageFromString("text1"));
  batch2->emplace_back(
      message_util::RouteMessageFromData(std::vector<uint8_t>({UINT8_C(1)})));
  batch2->emplace_back(message_util::RouteMessageFromString("text2"));
  batch3->emplace_back(message_util::RouteMessageFromString("text3"));
  batch3->emplace_back(
      message_util::RouteMessageFromData(std::vector<uint8_t>({UINT8_C(2)})));
  batch3->emplace_back(
      message_util::RouteMessageFromData(std::vector<uint8_t>({UINT8_C(3)})));
  all_messages->clear();
  for (auto* message_batch : {batch1, batch2, batch3}) {
    for (auto& message : *message_batch) {
      all_messages->emplace_back(message->Clone());
    }
  }
}

// Used in the RouteMessages* tests to observe and sanity-check that the
// messages being received from the router are correct and in-sequence. The
// checks here correspond to the expected messages in PopulateRouteMessages()
// above.
class ExpectedMessagesObserver final : public RouteMessageObserver {
 public:
  ExpectedMessagesObserver(MediaRouter* router,
                           const MediaRoute::Id& route_id,
                           std::vector<RouteMessagePtr> expected_messages)
      : RouteMessageObserver(router, route_id),
        expected_messages_(std::move(expected_messages)) {}

  ~ExpectedMessagesObserver() override { CheckReceivedMessages(); }

 private:
  void OnMessagesReceived(std::vector<RouteMessagePtr> messages) override {
    for (auto& message : messages)
      messages_.emplace_back(std::move(message));
  }

  void CheckReceivedMessages() {
    ASSERT_EQ(expected_messages_.size(), messages_.size());
    for (size_t i = 0; i < expected_messages_.size(); i++) {
      EXPECT_EQ(expected_messages_[i], messages_[i])
          << "Message mismatch at index " << i
          << ": expected: " << RouteMessageToString(expected_messages_[i])
          << ", actual: " << RouteMessageToString(messages_[i]);
    }
  }

  std::vector<RouteMessagePtr> expected_messages_;
  std::vector<RouteMessagePtr> messages_;
};

}  // namespace

TEST_F(MediaRouterMojoImplTest, RouteMessagesSingleObserver) {
  std::vector<RouteMessagePtr> incoming_batch1, incoming_batch2,
      incoming_batch3, all_messages;
  ProvideTestRoute(mojom::MediaRouteProviderId::CAST, kRouteId);
  PopulateRouteMessages(&incoming_batch1, &incoming_batch2, &incoming_batch3,
                        &all_messages);

  base::RunLoop run_loop;
  EXPECT_CALL(mock_cast_provider_, StartListeningForRouteMessages(Eq(kRouteId)))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));

  // Creating ExpectedMessagesObserver will register itself to the
  // MediaRouter, which in turn will start listening for route messages.
  ExpectedMessagesObserver observer(router(), kRouteId,
                                    std::move(all_messages));
  run_loop.Run();  // Will quit when StartListeningForRouteMessages() is called.
  ReceiveRouteMessages(kRouteId, std::move(incoming_batch1));
  ReceiveRouteMessages(kRouteId, std::move(incoming_batch2));
  ReceiveRouteMessages(kRouteId, std::move(incoming_batch3));
  // When |observer| goes out-of-scope, its destructor will ensure all expected
  // messages have been received.
}

TEST_F(MediaRouterMojoImplTest, RouteMessagesMultipleObservers) {
  std::vector<RouteMessagePtr> incoming_batch1, incoming_batch2,
      incoming_batch3, all_messages;
  ProvideTestRoute(mojom::MediaRouteProviderId::CAST, kRouteId);
  PopulateRouteMessages(&incoming_batch1, &incoming_batch2, &incoming_batch3,
                        &all_messages);
  std::vector<RouteMessagePtr> all_messages2;
  for (auto& message : all_messages)
    all_messages2.emplace_back(message->Clone());

  base::RunLoop run_loop;
  EXPECT_CALL(mock_cast_provider_, StartListeningForRouteMessages(Eq(kRouteId)))
      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));

  // The ExpectedMessagesObservers will register themselves with the
  // MediaRouter, which in turn will start listening for route messages.
  ExpectedMessagesObserver observer1(router(), kRouteId,
                                     std::move(all_messages));
  ExpectedMessagesObserver observer2(router(), kRouteId,
                                     std::move(all_messages2));
  run_loop.Run();  // Will quit when StartListeningForRouteMessages() is called.
  ReceiveRouteMessages(kRouteId, std::move(incoming_batch1));
  ReceiveRouteMessages(kRouteId, std::move(incoming_batch2));
  ReceiveRouteMessages(kRouteId, std::move(incoming_batch3));
  // As each |observer| goes out-of-scope, its destructor will ensure all
  // expected messages have been received.
}

TEST_F(MediaRouterMojoImplTest, PresentationConnectionStateChangedCallback) {
  MediaRoute::Id route_id("route-id");
  const GURL presentation_url("http://www.example.com/presentation.html");
  const std::string presentation_id("pid");
  blink::mojom::PresentationInfo connection(presentation_url, presentation_id);
  base::MockCallback<content::PresentationConnectionStateChangedCallback>
      callback;
  base::CallbackListSubscription subscription =
      router()->AddPresentationConnectionStateChangedCallback(route_id,
                                                              callback.Get());

  {
    base::RunLoop run_loop;
    content::PresentationConnectionStateChangeInfo closed_info(
        PresentationConnectionState::CLOSED);
    closed_info.close_reason = PresentationConnectionCloseReason::WENT_AWAY;
    closed_info.message = "Foo";

    EXPECT_CALL(callback, Run(StateChangeInfoEquals(closed_info)))
        .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
    router()->OnPresentationConnectionClosed(
        route_id, PresentationConnectionCloseReason::WENT_AWAY, "Foo");
    run_loop.Run();
    EXPECT_TRUE(Mock::VerifyAndClearExpectations(&callback));
  }

  content::PresentationConnectionStateChangeInfo terminated_info(
      PresentationConnectionState::TERMINATED);
  {
    base::RunLoop run_loop;
    EXPECT_CALL(callback, Run(StateChangeInfoEquals(terminated_info)))
        .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
    router()->OnPresentationConnectionStateChanged(
        route_id, PresentationConnectionState::TERMINATED);
    run_loop.Run();

    EXPECT_TRUE(Mock::VerifyAndClearExpectations(&callback));
  }
}

TEST_F(MediaRouterMojoImplTest,
       PresentationConnectionStateChangedCallbackRemoved) {
  MediaRoute::Id route_id("route-id");
  base::MockCallback<content::PresentationConnectionStateChangedCallback>
      callback;
  base::CallbackListSubscription subscription =
      router()->AddPresentationConnectionStateChangedCallback(route_id,
                                                              callback.Get());

  // Callback has been removed, so we don't expect it to be called anymore.
  subscription = {};
  EXPECT_TRUE(router()->presentation_connection_state_callbacks_.empty());

  EXPECT_CALL(callback, Run(_)).Times(0);
  router()->OnPresentationConnectionStateChanged(
      route_id, PresentationConnectionState::TERMINATED);
  base::RunLoop().RunUntilIdle();
}

TEST_F(MediaRouterMojoImplTest, GetMediaController) {
  MockMediaController mock_controller;
  mojo::Remote<mojom::MediaController> controller_remote;
  mojo::PendingRemote<mojom::MediaStatusObserver> observer_remote;
  MockMediaStatusObserver mock_observer(
      observer_remote.InitWithNewPipeAndPassReceiver());
  mojo::Remote<mojom::MediaStatusObserver> observer_remote_held_by_controller;
  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {CreateMediaRoute()});

  EXPECT_CALL(mock_cast_provider_,
              CreateMediaRouteControllerInternal(kRouteId, _, _, _))
      .WillOnce(
          [&](const std::string& route_id,
              mojo::PendingReceiver<mojom::MediaController>& media_controller,
              mojo::PendingRemote<mojom::MediaStatusObserver>& observer,
              MockMediaRouteProvider::CreateMediaRouteControllerCallback&
                  callback) {
            mock_controller.Bind(std::move(media_controller));
            observer_remote_held_by_controller.Bind(std::move(observer));
            std::move(callback).Run(true);
          });
  router()->GetMediaController(kRouteId,
                               controller_remote.BindNewPipeAndPassReceiver(),
                               std::move(observer_remote));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_controller, Play());
  controller_remote->Play();
  EXPECT_CALL(mock_observer, OnMediaStatusUpdated(_));
  observer_remote_held_by_controller->OnMediaStatusUpdated(
      mojom::MediaStatus::New());
  base::RunLoop().RunUntilIdle();
}

TEST_F(MediaRouterMojoImplTest, SendSinkRequestsToMultipleProviders) {
  // Register |mock_wired_display_provider_| with the router.
  // |mock_cast_provider_| has already been registered during setup.
  RegisterWiredDisplayProvider();

  // Set |mock_cast_provider_| to have one sink with ID |kSinkId|.
  ProvideTestSink(mojom::MediaRouteProviderId::CAST, kSinkId);
  mock_cast_provider_.SetRouteToReturn(CreateMediaRoute());

  // Set |mock_wired_display_provider_| to have one sink with ID |kSinkId2|.
  ProvideTestSink(mojom::MediaRouteProviderId::WIRED_DISPLAY, kSinkId2);
  mock_wired_display_provider_.SetRouteToReturn(CreateMediaRoute2());

  // A request to |kSinkId| should only be sent to |mock_cast_provider_|.
  EXPECT_CALL(mock_cast_provider_,
              CreateRouteInternal(_, kSinkId, _, _, _, _, _, _))
      .WillOnce(WithArg<7>(Invoke(
          &mock_cast_provider_, &MockMediaRouteProvider::RouteRequestSuccess)));
  EXPECT_CALL(mock_wired_display_provider_,
              CreateRouteInternal(_, kSinkId, _, _, _, _, _, _))
      .Times(0);
  router()->CreateRoute(kSource, kSinkId, url::Origin::Create(GURL(kOrigin)),
                        nullptr, base::DoNothing(),
                        base::Milliseconds(kTimeoutMillis), true);

  // A request to |kSinkId2| should only be sent to
  // |mock_wired_display_provider_|.
  EXPECT_CALL(mock_cast_provider_,
              CreateRouteInternal(_, kSinkId2, _, _, _, _, _, _))
      .Times(0);
  EXPECT_CALL(mock_wired_display_provider_,
              CreateRouteInternal(_, kSinkId2, _, _, _, _, _, _))
      .WillOnce(
          WithArg<7>(Invoke(&mock_wired_display_provider_,
                            &MockMediaRouteProvider::RouteRequestSuccess)));
  router()->CreateRoute(kSource, kSinkId2, url::Origin::Create(GURL(kOrigin)),
                        nullptr, base::DoNothing(),
                        base::Milliseconds(kTimeoutMillis), true);
  base::RunLoop().RunUntilIdle();
}

TEST_F(MediaRouterMojoImplTest, SendRouteRequestsToMultipleProviders) {
  // Register |mock_wired_display_provider_| with the router.
  // |mock_cast_provider_| has already been registered during setup.
  RegisterWiredDisplayProvider();

  // Set |mock_cast_provider_| to have one route with ID |kRouteId|.
  ProvideTestRoute(mojom::MediaRouteProviderId::CAST, kRouteId);

  // Set |mock_wired_display_provider_| to have one route with ID |kRouteId2|.
  ProvideTestRoute(mojom::MediaRouteProviderId::WIRED_DISPLAY, kRouteId2);

  // A request for |kRouteId| should only be sent to |mock_cast_provider_|.
  EXPECT_CALL(mock_cast_provider_, DetachRoute(kRouteId));
  EXPECT_CALL(mock_wired_display_provider_, DetachRoute(kRouteId)).Times(0);
  router()->DetachRoute(kRouteId);

  // A request for |kRouteId2| should only be sent to
  // |mock_wired_display_provider_|.
  EXPECT_CALL(mock_cast_provider_, TerminateRouteInternal(kRouteId2, _))
      .Times(0);
  EXPECT_CALL(mock_wired_display_provider_,
              TerminateRouteInternal(kRouteId2, _))
      .WillOnce(
          WithArg<1>(Invoke(&mock_wired_display_provider_,
                            &MockMediaRouteProvider::TerminateRouteSuccess)));
  router()->TerminateRoute(kRouteId2);
  base::RunLoop().RunUntilIdle();
}

TEST_F(MediaRouterMojoImplTest, ObserveSinksFromMultipleProviders) {
  const MediaSource source(kSource);
  // Sinks for the Cast MRP.
  MediaSinkInternal sink1a;
  MediaSinkInternal sink1b;
  sink1a.set_sink(MediaSink("sink1a", "", SinkIconType::CAST,
                            mojom::MediaRouteProviderId::CAST));
  sink1b.set_sink(MediaSink("sink1b", "", SinkIconType::CAST,
                            mojom::MediaRouteProviderId::CAST));
  // Sinks for the wired display MRP.
  MediaSinkInternal sink2a;
  MediaSinkInternal sink2b;
  sink2a.set_sink(MediaSink("sink2a", "", SinkIconType::CAST,
                            mojom::MediaRouteProviderId::WIRED_DISPLAY));
  sink2b.set_sink(MediaSink("sink2b", "", SinkIconType::CAST,
                            mojom::MediaRouteProviderId::WIRED_DISPLAY));
  RegisterWiredDisplayProvider();
  NiceMock<MockMediaSinksObserver> observer(router(), source,
                                            url::Origin::Create(GURL(kOrigin)));
  observer.Init();

  // Have the Cast MRP report sinks.
  EXPECT_CALL(observer, OnSinksReceived(UnorderedElementsAre(sink1a.sink(),
                                                             sink1b.sink())));
  ReceiveSinks(mojom::MediaRouteProviderId::CAST, kSource, {sink1a, sink1b});

  // Have the wired display MRP report sinks.
  EXPECT_CALL(observer,
              OnSinksReceived(UnorderedElementsAre(
                  sink1a.sink(), sink1b.sink(), sink2a.sink(), sink2b.sink())));
  ReceiveSinks(mojom::MediaRouteProviderId::WIRED_DISPLAY, kSource,
               {sink2a, sink2b});

  // Have the Cast MRP report an empty list of sinks.
  EXPECT_CALL(observer, OnSinksReceived(UnorderedElementsAre(sink2a.sink(),
                                                             sink2b.sink())));
  ReceiveSinks(mojom::MediaRouteProviderId::CAST, kSource, {});

  // Have the wired display MRP report an empty list of sinks.
  EXPECT_CALL(observer, OnSinksReceived(IsEmpty()));
  ReceiveSinks(mojom::MediaRouteProviderId::WIRED_DISPLAY, kSource, {});
}

TEST_F(MediaRouterMojoImplTest, ObserveRoutesFromMultipleProviders) {
  const MediaSource source(kSource);
  // Routes for the Cast MRP.
  const MediaRoute route1a("route1a", source, "sink 1a", "", true);
  const MediaRoute route1b("route1b", source, "sink 1b", "", true);
  // Routes for the wired display MRP.
  const MediaRoute route2a("route2a", source, "sink 2a", "", true);
  const MediaRoute route2b("route2b", source, "sink 2b", "", true);
  RegisterWiredDisplayProvider();
  MockMediaRoutesObserver observer(router());

  // Have the Cast MRP report routes.
  EXPECT_CALL(observer,
              OnRoutesUpdated(UnorderedElementsAre(route1a, route1b)));
  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {route1a, route1b});

  // Have the wired display MRP report routes.
  EXPECT_CALL(observer, OnRoutesUpdated(UnorderedElementsAre(
                            route1a, route1b, route2a, route2b)));
  UpdateRoutes(mojom::MediaRouteProviderId::WIRED_DISPLAY, {route2a, route2b});

  // Have the Cast MRP report an empty list of routes.
  EXPECT_CALL(observer,
              OnRoutesUpdated(UnorderedElementsAre(route2a, route2b)));
  UpdateRoutes(mojom::MediaRouteProviderId::CAST, {});

  // Have the wired display MRP report an empty list of routes.
  EXPECT_CALL(observer, OnRoutesUpdated(IsEmpty()));
  UpdateRoutes(mojom::MediaRouteProviderId::WIRED_DISPLAY, {});
}

TEST_F(MediaRouterMojoImplTest, TestRecordPresentationRequestUrlBySink) {
  using base::Bucket;
  using PresentationUrlBySink = MediaRouterMojoImpl::PresentationUrlBySink;

  MediaSource cast_source("cast:ABCD1234");
  MediaSource dial_source(
      GURL(base::StrCat({kCastDialPresentationUrlScheme, ":YouTube"})));
  MediaSource presentation_url(GURL("https://www.example.com"));

  base::HistogramTester tester;
  RecordPresentationRequestUrlBySink(cast_source,
                                     mojom::MediaRouteProviderId::CAST, 5);
  RecordPresentationRequestUrlBySink(dial_source,
                                     mojom::MediaRouteProviderId::DIAL, 4);
  RecordPresentationRequestUrlBySink(presentation_url,
                                     mojom::MediaRouteProviderId::CAST, 3);
  RecordPresentationRequestUrlBySink(
      presentation_url, mojom::MediaRouteProviderId::WIRED_DISPLAY, 2);
  // DIAL devices don't support normal URLs, so this will get logged as
  // kUnknown.
  RecordPresentationRequestUrlBySink(presentation_url,
                                     mojom::MediaRouteProviderId::DIAL, 1);

  EXPECT_THAT(
      tester.GetAllSamples("MediaRouter.PresentationRequest.UrlBySink"),
      testing::UnorderedElementsAre(
          Bucket(static_cast<int>(PresentationUrlBySink::kUnknown), 1),
          Bucket(
              static_cast<int>(PresentationUrlBySink::kNormalUrlToChromecast),
              3),
          Bucket(
              static_cast<int>(PresentationUrlBySink::kNormalUrlToWiredDisplay),
              2),
          Bucket(static_cast<int>(PresentationUrlBySink::kCastUrlToChromecast),
                 5),
          Bucket(static_cast<int>(PresentationUrlBySink::kDialUrlToDial), 4)));
}

TEST_F(MediaRouterMojoImplTest, TestGetCurrentRoutes) {
  MediaSource source1("source_1");
  MediaSource source2("source_1");
  MediaRoute route1("route_1", source1, "sink_1", "", false);
  MediaRoute route2("route_2", source2, "sink_2", "", true);
  std::vector<MediaRoute> routes = {route1, route2};

  EXPECT_TRUE(router()->GetCurrentRoutes().empty());
  router()->internal_routes_observer_->OnRoutesUpdated(routes);
  std::vector<MediaRoute> current_routes = router()->GetCurrentRoutes();
  ASSERT_EQ(current_routes.size(), 2u);
  EXPECT_EQ(current_routes[0], route1);
  EXPECT_EQ(current_routes[1], route2);

  router()->internal_routes_observer_->OnRoutesUpdated(
      std::vector<MediaRoute>());
  EXPECT_TRUE(router()->GetCurrentRoutes().empty());
}

}  // namespace media_router


text:
<?php

namespace ZendPdf\Annotation;

use ZendPdf as Pdf;
use ZendPdf\Exception;
use ZendPdf\InternalType;

/**
 * A markup annotation
 *
 * @package    Zend_PDF
 * @subpackage Zend_PDF_Annotation
 */
class Markup extends AbstractAnnotation
{
    /**
     * Annotation subtypes
     */
    const SUBTYPE_HIGHLIGHT = 'Highlight';
    const SUBTYPE_UNDERLINE = 'Underline';
    const SUBTYPE_SQUIGGLY  = 'Squiggly';
    const SUBTYPE_STRIKEOUT = 'StrikeOut';

    /**
     * Annotation object constructor
     *
     * @throws \ZendPdf\Exception\ExceptionInterface
     */
    public function __construct(InternalType\AbstractTypeObject $annotationDictionary)
    {
        if ($annotationDictionary->getType() != InternalType\AbstractTypeObject::TYPE_DICTIONARY) {
            throw new Exception\CorruptedPdfException('Annotation dictionary resource has to be a dictionary.');
        }

        if ($annotationDictionary->Subtype === null  ||
            $annotationDictionary->Subtype->getType() != InternalType\AbstractTypeObject::TYPE_NAME  ||
            !in_array( $annotationDictionary->Subtype->value,
                       array(self::SUBTYPE_HIGHLIGHT,
                             self::SUBTYPE_UNDERLINE,
                             self::SUBTYPE_SQUIGGLY,
                             self::SUBTYPE_STRIKEOUT) )) {
            throw new Exception\CorruptedPdfException('Subtype => Markup entry is omitted or has wrong value.');
        }

        parent::__construct($annotationDictionary);
    }

    /**
     * Create markup annotation object
     *
     * Text markup annotations appear as highlights, underlines, strikeouts or
     * jagged ("squiggly") underlines in the text of a document. When opened,
     * they display a pop-up window containing the text of the associated note.
     *
     * $subType parameter may contain
     *     \ZendPdf\Annotation\Markup::SUBTYPE_HIGHLIGHT
     *     \ZendPdf\Annotation\Markup::SUBTYPE_UNDERLINE
     *     \ZendPdf\Annotation\Markup::SUBTYPE_SQUIGGLY
     *     \ZendPdf\Annotation\Markup::SUBTYPE_STRIKEOUT
     * for for a highlight, underline, squiggly-underline, or strikeout annotation,
     * respectively.
     *
     * $quadPoints is an array of 8xN numbers specifying the coordinates of
     * N quadrilaterals default user space. Each quadrilateral encompasses a word or
     * group of contiguous words in the text underlying the annotation.
     * The coordinates for each quadrilateral are given in the order
     *     x1 y1 x2 y2 x3 y3 x4 y4
     * specifying the quadrilateral's four vertices in counterclockwise order
     * starting from left bottom corner.
     * The text is oriented with respect to the edge connecting points
     * (x1, y1) and (x2, y2).
     *
     * @param float $x1
     * @param float $y1
     * @param float $x2
     * @param float $y2
     * @param string $text
     * @param string $subType
     * @param array $quadPoints  [x1 y1 x2 y2 x3 y3 x4 y4]
     * @return \ZendPdf\Annotation\Markup
     * @throws \ZendPdf\Exception\ExceptionInterface
     */
    public static function create($x1, $y1, $x2, $y2, $text, $subType, $quadPoints)
    {
        $annotationDictionary = new InternalType\DictionaryObject();

        $annotationDictionary->Type    = new InternalType\NameObject('Annot');
        $annotationDictionary->Subtype = new InternalType\NameObject($subType);

        $rectangle = new InternalType\ArrayObject();
        $rectangle->items[] = new InternalType\NumericObject($x1);
        $rectangle->items[] = new InternalType\NumericObject($y1);
        $rectangle->items[] = new InternalType\NumericObject($x2);
        $rectangle->items[] = new InternalType\NumericObject($y2);
        $annotationDictionary->Rect = $rectangle;

        $annotationDictionary->Contents = new InternalType\StringObject($text);

        if (!is_array($quadPoints)  ||  count($quadPoints) == 0  ||  count($quadPoints) % 8 != 0) {
            throw new Exception\InvalidArgumentException('$quadPoints parameter must be an array of 8xN numbers');
        }
        $points = new InternalType\ArrayObject();
        foreach ($quadPoints as $quadPoint) {
            $points->items[] = new InternalType\NumericObject($quadPoint);
        }
        $annotationDictionary->QuadPoints = $points;

        return new self($annotationDictionary);
    }
}


text:
Choisir l'album à partager

	@foreach ($albums as $album)

{{ $album['title']['_content'] }}

{{ $album['description']['_content'] }}

					Partager cet album
					getUid() }}/sets/{{ $album['id'] }}" target="_blank" class="label label-default" rel="tooltip">Voir sur Flickr

	@endforeach

@if (count($shares))

Partages actifs

		Nom de l'album
		Lien
		Expiration
		Vue(s)
		Actions

	@foreach ($shares as $share)

		{{ $share->title }}
		hash)) }}" target="_blank">{{ action('FlickrController@access', array($share->hash)) }}
		{{ $share->expiration->format('d/m/Y') }}
		{{ $share->views }}
		id)) }}" class="btn btn-danger btn-xs">

	@endforeach

@endif


text:
package cucumber.eclipse.steps.integration;

import java.io.IOException;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;

public interface IStepGenerator {

	boolean supports(IFile stepFile);

	TextEdit createStepSnippet(gherkin.formatter.model.Step step,
	        IDocument targetDocument) throws IOException, CoreException;
}


text:
'use strict';

var _stringify = require('babel-runtime/core-js/json/stringify');

var _stringify2 = _interopRequireDefault(_stringify);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var path = require('path');
var fs = require('fs');
var webpack = require('webpack');
var express = require('express');
var lodash = require('lodash');
var isFunction = lodash.isFunction;
var proxyMiddleware = require('http-proxy-middleware');

module.exports = function (argv) {
  var compilerOptions = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

  if (argv.experimental) {
    return experimental(argv, compilerOptions);
  } else {
    return original(argv, compilerOptions);
  }
};

/**
 * Branching the function instead of trying to modify it
 */
function experimental(argv, _ref) {
  var project = _ref.project;
}

function original(argv) {
  var compilerOptions = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  var onCompile = compilerOptions.onCompile;
  var beforeCompile = compilerOptions.beforeCompile;

  if (!argv) {
    argv = require('yargs').argv;
  }

  var app = express();

  var config = require('./index')(argv);

  if (argv.webpackConfig) {
    var mod = require(path.resolve(argv.webpackConfig));
    config = config.merge(mod);
  }

  config = config.resolve();

  if (beforeCompile) {
    beforeCompile({ config: config, argv: argv });
  }

  // Monkey patching the Webpack Dev Server Compiler
  // so that when the compile finishes we can do something.
  // e.g. let the skypager-electron system know the bundle is ready
  var compiler = webpack(config),
      originalWatch = compiler.watch;

  compiler.watch = function (watchOptions, handler) {
    originalWatch.call(compiler, watchOptions, function (err, stats) {
      handler && handler(err, stats);

      if (onCompile && isFunction(onCompile)) {
        onCompile(err, stats);
      }

      try {
        if (argv.saveWebpackStats) {
          fs.writeFileSync(argv.saveWebpackStats, (0, _stringify2.default)(stats.toJson(), null, 2), '');
        }
      } catch (error) {}
    });
  };

  // use proxyPath = '/engine.io' and proxyTarget 'http://localhost:6020'
  // to proxy the deepstream server for example
  if (argv.proxyPath && argv.proxyTarget) {
    app.use(proxyMiddleware(argv.proxyPath, {
      target: argv.proxyTarget,
      ws: true
    }));
  }

  var devMiddleware = require('webpack-dev-middleware')(compiler, {
    publicPath: config.output.publicPath,
    stats: !!!argv.silent,
    noInfo: !!argv.silent,
    quiet: !!argv.silent
  });

  app.use(devMiddleware);
  app.use(require('webpack-hot-middleware')(compiler));

  app.use(express.static(config.output.path));

  if (argv.middleware) {
    app.use(require(path.resolve(argv.middleware))({
      config: config,
      compiler: compiler
    }));
  }

  app.get('*', function (req, res) {
    if (req.accepts('html')) {
      var indexPath = path.join(config.output.path, 'index.html');
      var index = devMiddleware.fileSystem.readFileSync(indexPath);
      res.set('Content-Type', 'text/html');
      res.send(index);
    }
  });

  app.listen(argv.port || 3000, argv.hostname || 'localhost', function (err) {
    if (err) {
      console.log(err);
      return;
    }
    if (!argv.silent) {
      console.log('Listening at http://localhost:' + (argv.port || 3000));
    }
  });
}